xref: /OK3568_Linux_fs/app/forlinx/quectelCM/libmnl/dhcp/dhcpclient.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 /*
2  * Copyright 2008, The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include <dirent.h>
17 #include <errno.h>
18 #include <poll.h>
19 #include <netinet/in.h>
20 #include <stdarg.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <sys/select.h>
25 #include <sys/socket.h>
26 #include <sys/time.h>
27 #include <sys/types.h>
28 #include <net/if.h>
29 #include <time.h>
30 #include <unistd.h>
31 #include <net/if.h>
32 
33 #include "../ifutils.h"
34 #include "dhcpmsg.h"
35 #include "packet.h"
36 
37 #define VERBOSE 2
38 
39 static int verbose = 1;
40 static char errmsg[2048];
41 
42 typedef unsigned long long msecs_t;
43 #if VERBOSE
44 void dump_dhcp_msg();
45 #endif
46 
get_msecs(void)47 msecs_t get_msecs(void)
48 {
49     struct timespec ts;
50 
51     if (clock_gettime(CLOCK_MONOTONIC, &ts)) {
52         return 0;
53     } else {
54         return (((msecs_t) ts.tv_sec) * ((msecs_t) 1000)) +
55             (((msecs_t) ts.tv_nsec) / ((msecs_t) 1000000));
56     }
57 }
58 
printerr(char * fmt,...)59 void printerr(char *fmt, ...)
60 {
61     va_list ap;
62 
63     va_start(ap, fmt);
64     vsnprintf(errmsg, sizeof(errmsg), fmt, ap);
65     va_end(ap);
66 
67     printf("%s\n", errmsg);
68 }
69 
dhcp_lasterror()70 const char *dhcp_lasterror()
71 {
72     return errmsg;
73 }
74 
fatal(const char * reason)75 int fatal(const char *reason)
76 {
77     printerr("%s: %s\n", reason, strerror(errno));
78     return -1;
79 //    exit(1);
80 }
81 
82 typedef struct dhcp_info dhcp_info;
83 
84 struct dhcp_info {
85     uint32_t type;
86 
87     uint32_t ipaddr;
88     uint32_t gateway;
89     uint32_t prefixLength;
90 
91     uint32_t dns1;
92     uint32_t dns2;
93 
94     uint32_t serveraddr;
95     uint32_t lease;
96 };
97 
98 dhcp_info last_good_info;
99 
get_dhcp_info(uint32_t * ipaddr,uint32_t * gateway,uint32_t * prefixLength,uint32_t * dns1,uint32_t * dns2,uint32_t * server,uint32_t * lease)100 void get_dhcp_info(uint32_t *ipaddr, uint32_t *gateway, uint32_t *prefixLength,
101                    uint32_t *dns1, uint32_t *dns2, uint32_t *server,
102                    uint32_t *lease)
103 {
104     *ipaddr = last_good_info.ipaddr;
105     *gateway = last_good_info.gateway;
106     *prefixLength = last_good_info.prefixLength;
107     *dns1 = last_good_info.dns1;
108     *dns2 = last_good_info.dns2;
109     *server = last_good_info.serveraddr;
110     *lease = last_good_info.lease;
111 }
112 
dhcp_configure(const char * ifname,dhcp_info * info)113 static int dhcp_configure(const char *ifname, dhcp_info *info)
114 {
115     last_good_info = *info;
116     return if_set_network_v4(ifname, info->ipaddr, info->prefixLength, info->gateway,
117                          info->dns1, info->dns2);
118 }
119 
dhcp_type_to_name(uint32_t type)120 static const char *dhcp_type_to_name(uint32_t type)
121 {
122     switch(type) {
123     case DHCPDISCOVER: return "discover";
124     case DHCPOFFER:    return "offer";
125     case DHCPREQUEST:  return "request";
126     case DHCPDECLINE:  return "decline";
127     case DHCPACK:      return "ack";
128     case DHCPNAK:      return "nak";
129     case DHCPRELEASE:  return "release";
130     case DHCPINFORM:   return "inform";
131     default:           return "???";
132     }
133 }
134 
dump_dhcp_info(dhcp_info * info)135 void dump_dhcp_info(dhcp_info *info)
136 {
137     char addr[20], gway[20];
138     printf("--- dhcp %s (%d) ---\n",
139             dhcp_type_to_name(info->type), info->type);
140     strcpy(addr, ipaddr_to_string_v4(info->ipaddr));
141     strcpy(gway, ipaddr_to_string_v4(info->gateway));
142     printf("ip %s gw %s prefixLength %d\n", addr, gway, info->prefixLength);
143     if (info->dns1) printf("dns1: %s\n", ipaddr_to_string_v4(info->dns1));
144     if (info->dns2) printf("dns2: %s\n", ipaddr_to_string_v4(info->dns2));
145     printf("server %s, lease %d seconds\n",
146             ipaddr_to_string_v4(info->serveraddr), info->lease);
147 }
148 
149 
decode_dhcp_msg(dhcp_msg * msg,int len,dhcp_info * info)150 int decode_dhcp_msg(dhcp_msg *msg, int len, dhcp_info *info)
151 {
152     uint8_t *x;
153     unsigned int opt;
154     int optlen;
155 
156     memset(info, 0, sizeof(dhcp_info));
157     if (len < (DHCP_MSG_FIXED_SIZE + 4)) return -1;
158 
159     len -= (DHCP_MSG_FIXED_SIZE + 4);
160 
161     if (msg->options[0] != OPT_COOKIE1) return -1;
162     if (msg->options[1] != OPT_COOKIE2) return -1;
163     if (msg->options[2] != OPT_COOKIE3) return -1;
164     if (msg->options[3] != OPT_COOKIE4) return -1;
165 
166     x = msg->options + 4;
167 
168     while (len > 2) {
169         opt = *x++;
170         if (opt == OPT_PAD) {
171             len--;
172             continue;
173         }
174         if (opt == OPT_END) {
175             break;
176         }
177         optlen = *x++;
178         len -= 2;
179         if (optlen > len) {
180             break;
181         }
182         switch(opt) {
183         case OPT_SUBNET_MASK:
184             if (optlen >= 4) {
185                 in_addr_t mask;
186                 memcpy(&mask, x, 4);
187                 info->prefixLength = mask_to_prefix_v4(mask);
188             }
189             break;
190         case OPT_GATEWAY:
191             if (optlen >= 4) memcpy(&info->gateway, x, 4);
192             break;
193         case OPT_DNS:
194             if (optlen >= 4) memcpy(&info->dns1, x + 0, 4);
195             if (optlen >= 8) memcpy(&info->dns2, x + 4, 4);
196             break;
197         case OPT_LEASE_TIME:
198             if (optlen >= 4) {
199                 memcpy(&info->lease, x, 4);
200                 info->lease = ntohl(info->lease);
201             }
202             break;
203         case OPT_SERVER_ID:
204             if (optlen >= 4) memcpy(&info->serveraddr, x, 4);
205             break;
206         case OPT_MESSAGE_TYPE:
207             info->type = *x;
208             break;
209         default:
210             break;
211         }
212         x += optlen;
213         len -= optlen;
214     }
215 
216     info->ipaddr = msg->yiaddr;
217 
218     return 0;
219 }
220 
221 #if VERBOSE
222 
hex2str(char * buf,size_t buf_size,const unsigned char * array,int len)223 static void hex2str(char *buf, size_t buf_size, const unsigned char *array, int len)
224 {
225     int i;
226     char *cp = buf;
227     char *buf_end = buf + buf_size;
228     for (i = 0; i < len; i++) {
229         cp += snprintf(cp, buf_end - cp, " %02x ", array[i]);
230     }
231 }
232 
dump_dhcp_msg(dhcp_msg * msg,int len)233 void dump_dhcp_msg(dhcp_msg *msg, int len)
234 {
235     unsigned char *x;
236     unsigned int n,c;
237     int optsz;
238     const char *name;
239     char buf[2048];
240 
241     if (len < DHCP_MSG_FIXED_SIZE) {
242         printf("Invalid length %d, should be %d", len, DHCP_MSG_FIXED_SIZE);
243         return;
244     }
245 
246     len -= DHCP_MSG_FIXED_SIZE;
247 
248     if (msg->op == OP_BOOTREQUEST)
249         name = "BOOTREQUEST";
250     else if (msg->op == OP_BOOTREPLY)
251         name = "BOOTREPLY";
252     else
253         name = "????";
254 
255     c = msg->hlen > 16 ? 16 : msg->hlen;
256     hex2str(buf, sizeof(buf), msg->chaddr, c);
257 
258     for (n = 0; n < 64; n++) {
259         unsigned char x = msg->sname[n];
260         if ((x < ' ') || (x > 127)) {
261             if (x == 0) break;
262             msg->sname[n] = '.';
263         }
264     }
265     msg->sname[63] = 0;
266 
267     for (n = 0; n < 128; n++) {
268         unsigned char x = msg->file[n];
269         if ((x < ' ') || (x > 127)) {
270             if (x == 0) break;
271             msg->file[n] = '.';
272         }
273     }
274     msg->file[127] = 0;
275 
276     if (len < 4) return;
277     len -= 4;
278     x = msg->options + 4;
279 
280     while (len > 2) {
281         if (*x == 0) {
282             x++;
283             len--;
284             continue;
285         }
286         if (*x == OPT_END) {
287             break;
288         }
289         len -= 2;
290         optsz = x[1];
291         if (optsz > len) break;
292         if (x[0] == OPT_DOMAIN_NAME || x[0] == OPT_MESSAGE) {
293             if ((unsigned int)optsz < sizeof(buf) - 1) {
294                 n = optsz;
295             } else {
296                 n = sizeof(buf) - 1;
297             }
298             memcpy(buf, &x[2], n);
299             buf[n] = '\0';
300         } else {
301             hex2str(buf, sizeof(buf), &x[2], optsz);
302         }
303         if (x[0] == OPT_MESSAGE_TYPE)
304             name = dhcp_type_to_name(x[2]);
305         else
306             name = NULL;
307         len -= optsz;
308         x = x + optsz + 2;
309     }
310 }
311 
312 #endif
313 
send_message(int sock,int if_index,dhcp_msg * msg,int size)314 static int send_message(int sock, int if_index, dhcp_msg  *msg, int size)
315 {
316 #if VERBOSE > 1
317     dump_dhcp_msg(msg, size);
318 #endif
319     return send_packet(sock, if_index, msg, size, INADDR_ANY, INADDR_BROADCAST,
320                        PORT_BOOTP_CLIENT, PORT_BOOTP_SERVER);
321 }
322 
is_valid_reply(dhcp_msg * msg,dhcp_msg * reply,int sz)323 static int is_valid_reply(dhcp_msg *msg, dhcp_msg *reply, int sz)
324 {
325     if (sz < DHCP_MSG_FIXED_SIZE) {
326         if (verbose) printf("Wrong size %d != %d\n", sz, DHCP_MSG_FIXED_SIZE);
327         return 0;
328     }
329     if (reply->op != OP_BOOTREPLY) {
330         if (verbose) printf("Wrong Op %d != %d\n", reply->op, OP_BOOTREPLY);
331         return 0;
332     }
333     if (reply->xid != msg->xid) {
334         if (verbose) printf("Wrong Xid 0x%x != 0x%x\n", ntohl(reply->xid),
335                            ntohl(msg->xid));
336         return 0;
337     }
338     if (reply->htype != msg->htype) {
339         if (verbose) printf("Wrong Htype %d != %d\n", reply->htype, msg->htype);
340         return 0;
341     }
342     if (reply->hlen != msg->hlen) {
343         if (verbose) printf("Wrong Hlen %d != %d\n", reply->hlen, msg->hlen);
344         return 0;
345     }
346     if (memcmp(msg->chaddr, reply->chaddr, msg->hlen)) {
347         if (verbose) printf("Wrong chaddr %x != %x\n", *(reply->chaddr),*(msg->chaddr));
348         return 0;
349     }
350     return 1;
351 }
352 
353 #define STATE_SELECTING  1
354 #define STATE_REQUESTING 2
355 
356 #define TIMEOUT_INITIAL   4000
357 #define TIMEOUT_MAX      32000
358 
dhcp_init_ifc(const char * ifname)359 int dhcp_init_ifc(const char *ifname)
360 {
361     dhcp_msg discover_msg;
362     dhcp_msg request_msg;
363     dhcp_msg reply;
364     dhcp_msg *msg;
365     dhcp_info info;
366     int s, r, size;
367     int valid_reply;
368     uint32_t xid;
369     unsigned char hwaddr[6];
370     struct pollfd pfd;
371     unsigned int state;
372     unsigned int timeout;
373     int if_index;
374 
375     xid = (uint32_t) get_msecs();
376 
377     if (if_get_hwaddr(ifname, hwaddr)) {
378         return fatal("cannot obtain interface address");
379     }
380     if ((if_index = if_nametoindex(ifname)) == 0) {
381         return fatal("cannot obtain interface index");
382     }
383 
384     s = open_raw_socket(ifname, hwaddr, if_index);
385 
386     timeout = TIMEOUT_INITIAL;
387     state = STATE_SELECTING;
388     info.type = 0;
389     goto transmit;
390 
391     for (;;) {
392         pfd.fd = s;
393         pfd.events = POLLIN;
394         pfd.revents = 0;
395         r = poll(&pfd, 1, timeout);
396 
397         if (r == 0) {
398 #if VERBOSE
399             printerr("TIMEOUT\n");
400 #endif
401             if (timeout >= TIMEOUT_MAX) {
402                 printerr("timed out\n");
403                 if ( info.type == DHCPOFFER ) {
404                     printerr("no acknowledgement from DHCP server\nconfiguring %s with offered parameters\n", ifname);
405                     return dhcp_configure(ifname, &info);
406                 }
407                 errno = ETIME;
408                 close(s);
409                 return -1;
410             }
411             timeout = timeout * 2;
412 
413         transmit:
414             size = 0;
415             msg = NULL;
416             switch(state) {
417             case STATE_SELECTING:
418                 msg = &discover_msg;
419                 size = init_dhcp_discover_msg(msg, hwaddr, xid);
420                 break;
421             case STATE_REQUESTING:
422                 msg = &request_msg;
423                 size = init_dhcp_request_msg(msg, hwaddr, xid, info.ipaddr, info.serveraddr);
424                 break;
425             default:
426                 r = 0;
427             }
428             if (size != 0) {
429                 r = send_message(s, if_index, msg, size);
430                 if (r < 0) {
431                     printerr("error sending dhcp msg: %s\n", strerror(errno));
432                 }
433             }
434             continue;
435         }
436 
437         if (r < 0) {
438             if ((errno == EAGAIN) || (errno == EINTR)) {
439                 continue;
440             }
441             return fatal("poll failed");
442         }
443 
444         errno = 0;
445         r = receive_packet(s, &reply);
446         if (r < 0) {
447             if (errno != 0) {
448                 printf("receive_packet failed (%d): %s", r, strerror(errno));
449                 if (errno == ENETDOWN || errno == ENXIO) {
450                     return -1;
451                 }
452             }
453             continue;
454         }
455 
456 #if VERBOSE > 1
457         dump_dhcp_msg(&reply, r);
458 #endif
459         decode_dhcp_msg(&reply, r, &info);
460 
461         if (state == STATE_SELECTING) {
462             valid_reply = is_valid_reply(&discover_msg, &reply, r);
463         } else {
464             valid_reply = is_valid_reply(&request_msg, &reply, r);
465         }
466         if (!valid_reply) {
467             printerr("invalid reply\n");
468             continue;
469         }
470 
471         if (verbose) dump_dhcp_info(&info);
472 
473         switch(state) {
474         case STATE_SELECTING:
475             if (info.type == DHCPOFFER) {
476                 state = STATE_REQUESTING;
477                 timeout = TIMEOUT_INITIAL;
478                 xid++;
479                 goto transmit;
480             }
481             break;
482         case STATE_REQUESTING:
483             if (info.type == DHCPACK) {
484                 printerr("configuring %s\n", ifname);
485                 close(s);
486                 return dhcp_configure(ifname, &info);
487             } else if (info.type == DHCPNAK) {
488                 printerr("configuration request denied\n");
489                 close(s);
490                 return -1;
491             } else {
492                 printerr("ignoring %s message in state %d\n",
493                          dhcp_type_to_name(info.type), state);
494             }
495             break;
496         }
497     }
498     close(s);
499     return 0;
500 }
501 
do_dhcp(char * iname)502 int do_dhcp(char *iname)
503 {
504     if (if_set_addr_v4(iname, 0, 32)) {
505         printerr("failed to set ip addr for %s to 0.0.0.0: %s\n", iname, strerror(errno));
506         return -1;
507     }
508 
509     if (if_link_up(iname)) {
510         printerr("failed to bring up interface %s: %s\n", iname, strerror(errno));
511         return -1;
512     }
513 
514     return dhcp_init_ifc(iname);
515 }
516