gdhcp: Remove unused variable
[framework/connectivity/connman.git] / gdhcp / common.c
1 /*
2  *  DHCP library with GLib integration
3  *
4  *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License version 2 as
8  *  published by the Free Software Foundation.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  *
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <stdio.h>
26 #include <errno.h>
27 #include <unistd.h>
28 #include <sys/ioctl.h>
29 #include <stdint.h>
30 #include <string.h>
31 #include <endian.h>
32 #include <net/if_arp.h>
33 #include <linux/if.h>
34 #include <netpacket/packet.h>
35 #include <net/ethernet.h>
36
37 #include "gdhcp.h"
38 #include "common.h"
39
40 static const DHCPOption client_options[] = {
41         { OPTION_IP,                    0x01 }, /* subnet-mask */
42         { OPTION_IP | OPTION_LIST,      0x03 }, /* routers */
43         { OPTION_IP | OPTION_LIST,      0x06 }, /* domain-name-servers */
44         { OPTION_STRING,                0x0c }, /* hostname */
45         { OPTION_STRING,                0x0f }, /* domain-name */
46         { OPTION_IP | OPTION_LIST,      0x2a }, /* ntp-servers */
47         { OPTION_U32,                   0x33 }, /* dhcp-lease-time */
48         /* Options below will not be exposed to user */
49         { OPTION_IP,                    0x32 }, /* requested-ip */
50         { OPTION_U8,                    0x35 }, /* message-type */
51         { OPTION_U32,                   0x36 }, /* server-id */
52         { OPTION_U16,                   0x39 }, /* max-size */
53         { OPTION_STRING,                0x3c }, /* vendor */
54         { OPTION_STRING,                0x3d }, /* client-id */
55         { OPTION_STRING,                0xfc }, /* UNOFFICIAL proxy-pac */
56         { OPTION_UNKNOWN,               0x00 },
57 };
58
59 GDHCPOptionType dhcp_get_code_type(uint8_t code)
60 {
61         int i;
62
63         for (i = 0; client_options[i].code; i++) {
64                 if (client_options[i].code == code)
65                         return client_options[i].type;
66         }
67
68         return OPTION_UNKNOWN;
69 }
70
71 uint8_t *dhcp_get_option(struct dhcp_packet *packet, int code)
72 {
73         int len, rem;
74         uint8_t *optionptr;
75         uint8_t overload = 0;
76
77         /* option bytes: [code][len][data1][data2]..[dataLEN] */
78         optionptr = packet->options;
79         rem = sizeof(packet->options);
80
81         while (1) {
82                 if (rem <= 0)
83                         /* Bad packet, malformed option field */
84                         return NULL;
85
86                 if (optionptr[OPT_CODE] == DHCP_PADDING) {
87                         rem--;
88                         optionptr++;
89
90                         continue;
91                 }
92
93                 if (optionptr[OPT_CODE] == DHCP_END) {
94                         if (overload & FILE_FIELD) {
95                                 overload &= ~FILE_FIELD;
96
97                                 optionptr = packet->file;
98                                 rem = sizeof(packet->file);
99
100                                 continue;
101                         } else if (overload & SNAME_FIELD) {
102                                 overload &= ~SNAME_FIELD;
103
104                                 optionptr = packet->sname;
105                                 rem = sizeof(packet->sname);
106
107                                 continue;
108                         }
109
110                         break;
111                 }
112
113                 len = 2 + optionptr[OPT_LEN];
114
115                 rem -= len;
116                 if (rem < 0)
117                         continue; /* complain and return NULL */
118
119                 if (optionptr[OPT_CODE] == code)
120                         return optionptr + OPT_DATA;
121
122                 if (optionptr[OPT_CODE] == DHCP_OPTION_OVERLOAD)
123                         overload |= optionptr[OPT_DATA];
124
125                 optionptr += len;
126         }
127
128         return NULL;
129 }
130
131 int dhcp_end_option(uint8_t *optionptr)
132 {
133         int i = 0;
134
135         while (optionptr[i] != DHCP_END) {
136                 if (optionptr[i] != DHCP_PADDING)
137                         i += optionptr[i + OPT_LEN] + OPT_DATA - 1;
138
139                 i++;
140         }
141
142         return i;
143 }
144
145 /*
146  * Add an option (supplied in binary form) to the options.
147  * Option format: [code][len][data1][data2]..[dataLEN]
148  */
149 void dhcp_add_binary_option(struct dhcp_packet *packet, uint8_t *addopt)
150 {
151         unsigned len;
152         uint8_t *optionptr = packet->options;
153         unsigned end = dhcp_end_option(optionptr);
154
155         len = OPT_DATA + addopt[OPT_LEN];
156
157         /* end position + (option code/length + addopt length) + end option */
158         if (end + len + 1 >= DHCP_OPTIONS_BUFSIZE)
159                 /* option did not fit into the packet */
160                 return;
161
162         memcpy(optionptr + end, addopt, len);
163
164         optionptr[end + len] = DHCP_END;
165 }
166
167 void dhcp_add_simple_option(struct dhcp_packet *packet, uint8_t code,
168                                                         uint32_t data)
169 {
170         uint8_t option[6], len;
171         GDHCPOptionType type = dhcp_get_code_type(code);
172
173         if (type == OPTION_UNKNOWN)
174                 return;
175
176         option[OPT_CODE] = code;
177
178         len = dhcp_option_lengths[type & OPTION_TYPE_MASK];
179         option[OPT_LEN] = len;
180
181 #if __BYTE_ORDER == __BIG_ENDIAN
182         data <<= 8 * (4 - len);
183 #endif
184
185         dhcp_put_unaligned(data, (uint32_t *) &option[OPT_DATA]);
186         dhcp_add_binary_option(packet, option);
187
188         return;
189 }
190
191 void dhcp_init_header(struct dhcp_packet *packet, char type)
192 {
193         memset(packet, 0, sizeof(*packet));
194
195         packet->op = BOOTREQUEST;
196
197         switch (type) {
198         case DHCPOFFER:
199         case DHCPACK:
200         case DHCPNAK:
201                 packet->op = BOOTREPLY;
202         }
203
204         packet->htype = 1;
205         packet->hlen = 6;
206         packet->cookie = htonl(DHCP_MAGIC);
207         packet->options[0] = DHCP_END;
208
209         dhcp_add_simple_option(packet, DHCP_MESSAGE_TYPE, type);
210 }
211
212 static gboolean check_vendor(uint8_t  *option_vendor, const char *vendor)
213 {
214         uint8_t vendor_length = sizeof(vendor) - 1;
215
216         if (option_vendor[OPT_LEN - OPT_DATA] != vendor_length)
217                 return FALSE;
218
219         if (memcmp(option_vendor, vendor, vendor_length) != 0)
220                 return FALSE;
221
222         return TRUE;
223 }
224
225 static void check_broken_vendor(struct dhcp_packet *packet)
226 {
227         uint8_t *vendor;
228
229         if (packet->op != BOOTREQUEST)
230                 return;
231
232         vendor = dhcp_get_option(packet, DHCP_VENDOR);
233         if (vendor == NULL)
234                 return;
235
236         if (check_vendor(vendor, "MSFT 98") == TRUE)
237                 packet->flags |= htons(BROADCAST_FLAG);
238 }
239
240 int dhcp_recv_l3_packet(struct dhcp_packet *packet, int fd)
241 {
242         int n;
243
244         memset(packet, 0, sizeof(*packet));
245
246         n = read(fd, packet, sizeof(*packet));
247         if (n < 0)
248                 return -errno;
249
250         if (packet->cookie != htonl(DHCP_MAGIC))
251                 return -EPROTO;
252
253         check_broken_vendor(packet);
254
255         return n;
256 }
257
258 /* TODO: Use glib checksum */
259 uint16_t dhcp_checksum(void *addr, int count)
260 {
261         /*
262          * Compute Internet Checksum for "count" bytes
263          * beginning at location "addr".
264          */
265         int32_t sum = 0;
266         uint16_t *source = (uint16_t *) addr;
267
268         while (count > 1)  {
269                 /*  This is the inner loop */
270                 sum += *source++;
271                 count -= 2;
272         }
273
274         /*  Add left-over byte, if any */
275         if (count > 0) {
276                 /* Make sure that the left-over byte is added correctly both
277                  * with little and big endian hosts */
278                 uint16_t tmp = 0;
279                 *(uint8_t *) &tmp = *(uint8_t *) source;
280                 sum += tmp;
281         }
282         /*  Fold 32-bit sum to 16 bits */
283         while (sum >> 16)
284                 sum = (sum & 0xffff) + (sum >> 16);
285
286         return ~sum;
287 }
288
289 int dhcp_send_raw_packet(struct dhcp_packet *dhcp_pkt,
290                 uint32_t source_ip, int source_port, uint32_t dest_ip,
291                         int dest_port, const uint8_t *dest_arp, int ifindex)
292 {
293         struct sockaddr_ll dest;
294         struct ip_udp_dhcp_packet packet;
295         int fd, n;
296
297         enum {
298                 IP_UPD_DHCP_SIZE = sizeof(struct ip_udp_dhcp_packet) -
299                                                 EXTEND_FOR_BUGGY_SERVERS,
300                 UPD_DHCP_SIZE = IP_UPD_DHCP_SIZE -
301                                 offsetof(struct ip_udp_dhcp_packet, udp),
302         };
303
304         fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
305         if (fd < 0)
306                 return -errno;
307
308         memset(&dest, 0, sizeof(dest));
309         memset(&packet, 0, sizeof(packet));
310         packet.data = *dhcp_pkt;
311
312         dest.sll_family = AF_PACKET;
313         dest.sll_protocol = htons(ETH_P_IP);
314         dest.sll_ifindex = ifindex;
315         dest.sll_halen = 6;
316         memcpy(dest.sll_addr, dest_arp, 6);
317         if (bind(fd, (struct sockaddr *)&dest, sizeof(dest)) < 0) {
318                 close(fd);
319                 return -errno;
320         }
321
322         packet.ip.protocol = IPPROTO_UDP;
323         packet.ip.saddr = source_ip;
324         packet.ip.daddr = dest_ip;
325         packet.udp.source = htons(source_port);
326         packet.udp.dest = htons(dest_port);
327         /* size, excluding IP header: */
328         packet.udp.len = htons(UPD_DHCP_SIZE);
329         /* for UDP checksumming, ip.len is set to UDP packet len */
330         packet.ip.tot_len = packet.udp.len;
331         packet.udp.check = dhcp_checksum(&packet, IP_UPD_DHCP_SIZE);
332         /* but for sending, it is set to IP packet len */
333         packet.ip.tot_len = htons(IP_UPD_DHCP_SIZE);
334         packet.ip.ihl = sizeof(packet.ip) >> 2;
335         packet.ip.version = IPVERSION;
336         packet.ip.ttl = IPDEFTTL;
337         packet.ip.check = dhcp_checksum(&packet.ip, sizeof(packet.ip));
338
339         /*
340          * Currently we send full-sized DHCP packets (zero padded).
341          * If you need to change this: last byte of the packet is
342          * packet.data.options[dhcp_end_option(packet.data.options)]
343          */
344         n = sendto(fd, &packet, IP_UPD_DHCP_SIZE, 0,
345                         (struct sockaddr *) &dest, sizeof(dest));
346         if (n < 0)
347                 return -errno;
348
349         close(fd);
350
351         return n;
352 }
353
354 int dhcp_send_kernel_packet(struct dhcp_packet *dhcp_pkt,
355                                 uint32_t source_ip, int source_port,
356                                 uint32_t dest_ip, int dest_port)
357 {
358         struct sockaddr_in client;
359         int fd, n, opt = 1;
360
361         enum {
362                 DHCP_SIZE = sizeof(struct dhcp_packet) -
363                                         EXTEND_FOR_BUGGY_SERVERS,
364         };
365
366         fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
367         if (fd < 0)
368                 return -errno;
369
370         setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
371
372         memset(&client, 0, sizeof(client));
373         client.sin_family = AF_INET;
374         client.sin_port = htons(source_port);
375         client.sin_addr.s_addr = source_ip;
376         if (bind(fd, (struct sockaddr *) &client, sizeof(client)) < 0) {
377                 close(fd);
378                 return -errno;
379         }
380
381         memset(&client, 0, sizeof(client));
382         client.sin_family = AF_INET;
383         client.sin_port = htons(dest_port);
384         client.sin_addr.s_addr = dest_ip;
385         if (connect(fd, (struct sockaddr *) &client, sizeof(client)) < 0) {
386                 close(fd);
387                 return -errno;
388         }
389
390         n = write(fd, dhcp_pkt, DHCP_SIZE);
391
392         close(fd);
393
394         if (n < 0)
395                 return -errno;
396
397         return n;
398 }
399
400 int dhcp_l3_socket(int port, const char *interface)
401 {
402         int fd, opt = 1;
403         struct sockaddr_in addr;
404
405         fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
406
407         setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
408
409         if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,
410                                 interface, strlen(interface) + 1) < 0) {
411                 close(fd);
412                 return -1;
413         }
414
415         memset(&addr, 0, sizeof(addr));
416         addr.sin_family = AF_INET;
417         addr.sin_port = htons(port);
418         if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
419                 close(fd);
420                 return -1;
421         }
422
423         return fd;
424 }
425
426 char *get_interface_name(int index)
427 {
428         struct ifreq ifr;
429         int sk, err;
430
431         if (index < 0)
432                 return NULL;
433
434         sk = socket(PF_INET, SOCK_DGRAM, 0);
435         if (sk < 0) {
436                 perror("Open socket error");
437                 return NULL;
438         }
439
440         memset(&ifr, 0, sizeof(ifr));
441         ifr.ifr_ifindex = index;
442
443         err = ioctl(sk, SIOCGIFNAME, &ifr);
444         if (err < 0) {
445                 perror("Get interface name error");
446                 close(sk);
447                 return NULL;
448         }
449
450         close(sk);
451
452         return g_strdup(ifr.ifr_name);
453 }
454
455 gboolean interface_is_up(int index)
456 {
457         int sk, err;
458         struct ifreq ifr;
459         gboolean ret = FALSE;
460
461         sk = socket(PF_INET, SOCK_DGRAM, 0);
462         if (sk < 0) {
463                 perror("Open socket error");
464                 return FALSE;
465         }
466
467         memset(&ifr, 0, sizeof(ifr));
468         ifr.ifr_ifindex = index;
469
470         err = ioctl(sk, SIOCGIFNAME, &ifr);
471         if (err < 0) {
472                 perror("Get interface name error");
473                 goto done;
474         }
475
476         err = ioctl(sk, SIOCGIFFLAGS, &ifr);
477         if (err < 0) {
478                 perror("Get interface flags error");
479                 goto done;
480         }
481
482         if (ifr.ifr_flags & IFF_UP)
483                 ret = TRUE;
484
485 done:
486         close(sk);
487
488         return ret;
489 }