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