gdhcp: Move get_interface_name and interface_is_up to common.c
[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_UNKNOWN,               0x00 },
56 };
57
58 GDHCPOptionType dhcp_get_code_type(uint8_t code)
59 {
60         int i;
61
62         for (i = 0; client_options[i].code; i++) {
63                 if (client_options[i].code == code)
64                         return client_options[i].type;
65         }
66
67         return OPTION_UNKNOWN;
68 }
69
70 uint8_t *dhcp_get_option(struct dhcp_packet *packet, int code)
71 {
72         int len, rem;
73         uint8_t *optionptr;
74         uint8_t overload = 0;
75
76         /* option bytes: [code][len][data1][data2]..[dataLEN] */
77         optionptr = packet->options;
78         rem = sizeof(packet->options);
79
80         while (1) {
81                 if (rem <= 0)
82                         /* Bad packet, malformed option field */
83                         return NULL;
84
85                 if (optionptr[OPT_CODE] == DHCP_PADDING) {
86                         rem--;
87                         optionptr++;
88
89                         continue;
90                 }
91
92                 if (optionptr[OPT_CODE] == DHCP_END) {
93                         if (overload & FILE_FIELD) {
94                                 overload &= ~FILE_FIELD;
95
96                                 optionptr = packet->file;
97                                 rem = sizeof(packet->file);
98
99                                 continue;
100                         } else if (overload & SNAME_FIELD) {
101                                 overload &= ~SNAME_FIELD;
102
103                                 optionptr = packet->sname;
104                                 rem = sizeof(packet->sname);
105
106                                 continue;
107                         }
108
109                         break;
110                 }
111
112                 len = 2 + optionptr[OPT_LEN];
113
114                 rem -= len;
115                 if (rem < 0)
116                         continue; /* complain and return NULL */
117
118                 if (optionptr[OPT_CODE] == code)
119                         return optionptr + OPT_DATA;
120
121                 if (optionptr[OPT_CODE] == DHCP_OPTION_OVERLOAD)
122                         overload |= optionptr[OPT_DATA];
123
124                 optionptr += len;
125         }
126
127         return NULL;
128 }
129
130 int dhcp_end_option(uint8_t *optionptr)
131 {
132         int i = 0;
133
134         while (optionptr[i] != DHCP_END) {
135                 if (optionptr[i] != DHCP_PADDING)
136                         i += optionptr[i + OPT_LEN] + OPT_DATA - 1;
137
138                 i++;
139         }
140
141         return i;
142 }
143
144 /*
145  * Add an option (supplied in binary form) to the options.
146  * Option format: [code][len][data1][data2]..[dataLEN]
147  */
148 void dhcp_add_binary_option(struct dhcp_packet *packet, uint8_t *addopt)
149 {
150         unsigned len;
151         uint8_t *optionptr = packet->options;
152         unsigned end = dhcp_end_option(optionptr);
153
154         len = OPT_DATA + addopt[OPT_LEN];
155
156         /* end position + (option code/length + addopt length) + end option */
157         if (end + len + 1 >= DHCP_OPTIONS_BUFSIZE)
158                 /* option did not fit into the packet */
159                 return;
160
161         memcpy(optionptr + end, addopt, len);
162
163         optionptr[end + len] = DHCP_END;
164 }
165
166 void dhcp_add_simple_option(struct dhcp_packet *packet, uint8_t code,
167                                                         uint32_t data)
168 {
169         uint8_t option[6], len;
170         GDHCPOptionType type = dhcp_get_code_type(code);
171
172         if (type == OPTION_UNKNOWN)
173                 return;
174
175         option[OPT_CODE] = code;
176
177         len = dhcp_option_lengths[type & OPTION_TYPE_MASK];
178         option[OPT_LEN] = len;
179
180 #if __BYTE_ORDER == __BIG_ENDIAN
181         data <<= 8 * (4 - len);
182 #endif
183
184         dhcp_put_unaligned(data, (uint32_t *) &option[OPT_DATA]);
185         dhcp_add_binary_option(packet, option);
186
187         return;
188 }
189
190 void dhcp_init_header(struct dhcp_packet *packet, char type)
191 {
192         memset(packet, 0, sizeof(*packet));
193
194         packet->op = BOOTREQUEST;
195
196         switch (type) {
197         case DHCPOFFER:
198         case DHCPACK:
199         case DHCPNAK:
200                 packet->op = BOOTREPLY;
201         }
202
203         packet->htype = 1;
204         packet->hlen = 6;
205         packet->cookie = htonl(DHCP_MAGIC);
206         packet->options[0] = DHCP_END;
207
208         dhcp_add_simple_option(packet, DHCP_MESSAGE_TYPE, type);
209 }
210
211 static gboolean check_vendor(uint8_t  *option_vendor, const char *vendor)
212 {
213         uint8_t vendor_length = sizeof(vendor) - 1;
214
215         if (option_vendor[OPT_LEN - OPT_DATA] != vendor_length)
216                 return FALSE;
217
218         if (memcmp(option_vendor, vendor, vendor_length) != 0)
219                 return FALSE;
220
221         return TRUE;
222 }
223
224 static void check_broken_vendor(struct dhcp_packet *packet)
225 {
226         uint8_t *vendor;
227
228         if (packet->op != BOOTREQUEST)
229                 return;
230
231         vendor = dhcp_get_option(packet, DHCP_VENDOR);
232         if (vendor == NULL)
233                 return;
234
235         if (check_vendor(vendor, "MSFT 98") == TRUE)
236                 packet->flags |= htons(BROADCAST_FLAG);
237 }
238
239 int dhcp_recv_l3_packet(struct dhcp_packet *packet, int fd)
240 {
241         int n;
242
243         memset(packet, 0, sizeof(*packet));
244
245         n = read(fd, packet, sizeof(*packet));
246         if (n < 0)
247                 return -errno;
248
249         if (packet->cookie != htonl(DHCP_MAGIC))
250                 return -EPROTO;
251
252         check_broken_vendor(packet);
253
254         return n;
255 }
256
257 /* TODO: Use glib checksum */
258 uint16_t dhcp_checksum(void *addr, int count)
259 {
260         /*
261          * Compute Internet Checksum for "count" bytes
262          * beginning at location "addr".
263          */
264         int32_t sum = 0;
265         uint16_t *source = (uint16_t *) addr;
266
267         while (count > 1)  {
268                 /*  This is the inner loop */
269                 sum += *source++;
270                 count -= 2;
271         }
272
273         /*  Add left-over byte, if any */
274         if (count > 0) {
275                 /* Make sure that the left-over byte is added correctly both
276                  * with little and big endian hosts */
277                 uint16_t tmp = 0;
278                 *(uint8_t *) &tmp = *(uint8_t *) source;
279                 sum += tmp;
280         }
281         /*  Fold 32-bit sum to 16 bits */
282         while (sum >> 16)
283                 sum = (sum & 0xffff) + (sum >> 16);
284
285         return ~sum;
286 }
287
288 int dhcp_send_raw_packet(struct dhcp_packet *dhcp_pkt,
289                 uint32_t source_ip, int source_port, uint32_t dest_ip,
290                         int dest_port, const uint8_t *dest_arp, int ifindex)
291 {
292         struct sockaddr_ll dest;
293         struct ip_udp_dhcp_packet packet;
294         int fd, n;
295
296         enum {
297                 IP_UPD_DHCP_SIZE = sizeof(struct ip_udp_dhcp_packet) -
298                                                 EXTEND_FOR_BUGGY_SERVERS,
299                 UPD_DHCP_SIZE = IP_UPD_DHCP_SIZE -
300                                 offsetof(struct ip_udp_dhcp_packet, udp),
301         };
302
303         fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
304         if (fd < 0)
305                 return -errno;
306
307         memset(&dest, 0, sizeof(dest));
308         memset(&packet, 0, sizeof(packet));
309         packet.data = *dhcp_pkt;
310
311         dest.sll_family = AF_PACKET;
312         dest.sll_protocol = htons(ETH_P_IP);
313         dest.sll_ifindex = ifindex;
314         dest.sll_halen = 6;
315         memcpy(dest.sll_addr, dest_arp, 6);
316         if (bind(fd, (struct sockaddr *)&dest, sizeof(dest)) < 0) {
317                 close(fd);
318                 return -errno;
319         }
320
321         packet.ip.protocol = IPPROTO_UDP;
322         packet.ip.saddr = source_ip;
323         packet.ip.daddr = dest_ip;
324         packet.udp.source = htons(source_port);
325         packet.udp.dest = htons(dest_port);
326         /* size, excluding IP header: */
327         packet.udp.len = htons(UPD_DHCP_SIZE);
328         /* for UDP checksumming, ip.len is set to UDP packet len */
329         packet.ip.tot_len = packet.udp.len;
330         packet.udp.check = dhcp_checksum(&packet, IP_UPD_DHCP_SIZE);
331         /* but for sending, it is set to IP packet len */
332         packet.ip.tot_len = htons(IP_UPD_DHCP_SIZE);
333         packet.ip.ihl = sizeof(packet.ip) >> 2;
334         packet.ip.version = IPVERSION;
335         packet.ip.ttl = IPDEFTTL;
336         packet.ip.check = dhcp_checksum(&packet.ip, sizeof(packet.ip));
337
338         /*
339          * Currently we send full-sized DHCP packets (zero padded).
340          * If you need to change this: last byte of the packet is
341          * packet.data.options[dhcp_end_option(packet.data.options)]
342          */
343         n = sendto(fd, &packet, IP_UPD_DHCP_SIZE, 0,
344                         (struct sockaddr *) &dest, sizeof(dest));
345         if (n < 0)
346                 return -errno;
347
348         close(fd);
349
350         return n;
351 }
352
353 int dhcp_send_kernel_packet(struct dhcp_packet *dhcp_pkt,
354                                 uint32_t source_ip, int source_port,
355                                 uint32_t dest_ip, int dest_port)
356 {
357         struct sockaddr_in client;
358         int fd, n, opt = 1;
359
360         enum {
361                 DHCP_SIZE = sizeof(struct dhcp_packet) -
362                                         EXTEND_FOR_BUGGY_SERVERS,
363         };
364
365         fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
366         if (fd < 0)
367                 return -errno;
368
369         setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
370
371         memset(&client, 0, sizeof(client));
372         client.sin_family = AF_INET;
373         client.sin_port = htons(source_port);
374         client.sin_addr.s_addr = source_ip;
375         if (bind(fd, (struct sockaddr *) &client, sizeof(client)) < 0) {
376                 close(fd);
377                 return -errno;
378         }
379
380         memset(&client, 0, sizeof(client));
381         client.sin_family = AF_INET;
382         client.sin_port = htons(dest_port);
383         client.sin_addr.s_addr = dest_ip;
384         if (connect(fd, (struct sockaddr *) &client, sizeof(client)) < 0) {
385                 close(fd);
386                 return -errno;
387         }
388
389         n = write(fd, dhcp_pkt, DHCP_SIZE);
390
391         close(fd);
392
393         if (n < 0)
394                 return -errno;
395
396         return n;
397 }
398
399 int dhcp_l3_socket(int port, const char *interface)
400 {
401         int fd, opt = 1;
402         struct sockaddr_in addr;
403
404         fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
405
406         setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
407
408         if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,
409                                 interface, strlen(interface) + 1) < 0) {
410                 close(fd);
411                 return -1;
412         }
413
414         memset(&addr, 0, sizeof(addr));
415         addr.sin_family = AF_INET;
416         addr.sin_port = htons(port);
417         if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
418                 close(fd);
419                 return -1;
420         }
421
422         return fd;
423 }
424
425 char *get_interface_name(int index)
426 {
427         struct ifreq ifr;
428         int sk, err;
429
430         if (index < 0)
431                 return NULL;
432
433         sk = socket(PF_INET, SOCK_DGRAM, 0);
434         if (sk < 0) {
435                 perror("Open socket error");
436                 return NULL;
437         }
438
439         memset(&ifr, 0, sizeof(ifr));
440         ifr.ifr_ifindex = index;
441
442         err = ioctl(sk, SIOCGIFNAME, &ifr);
443         if (err < 0) {
444                 perror("Get interface name error");
445                 close(sk);
446                 return NULL;
447         }
448
449         close(sk);
450
451         return g_strdup(ifr.ifr_name);
452 }
453
454 gboolean interface_is_up(int index)
455 {
456         int sk, err;
457         struct ifreq ifr;
458         gboolean ret = FALSE;
459
460         sk = socket(PF_INET, SOCK_DGRAM, 0);
461         if (sk < 0) {
462                 perror("Open socket error");
463                 return FALSE;
464         }
465
466         memset(&ifr, 0, sizeof(ifr));
467         ifr.ifr_ifindex = index;
468
469         err = ioctl(sk, SIOCGIFNAME, &ifr);
470         if (err < 0) {
471                 perror("Get interface name error");
472                 goto done;
473         }
474
475         err = ioctl(sk, SIOCGIFFLAGS, &ifr);
476         if (err < 0) {
477                 perror("Get interface flags error");
478                 goto done;
479         }
480
481         if (ifr.ifr_flags & IFF_UP)
482                 ret = TRUE;
483
484 done:
485         close(sk);
486
487         return ret;
488 }