2 * DHCP library with GLib integration
4 * Copyright (C) 2007-2010 Intel Corporation. All rights reserved.
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.
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.
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
29 #include <sys/ioctl.h>
33 #include <net/if_arp.h>
35 #include <netpacket/packet.h>
36 #include <net/ethernet.h>
37 #include <arpa/inet.h>
44 static const DHCPOption client_options[] = {
45 { OPTION_IP, 0x01 }, /* subnet-mask */
46 { OPTION_IP | OPTION_LIST, 0x03 }, /* routers */
47 { OPTION_IP | OPTION_LIST, 0x06 }, /* domain-name-servers */
48 { OPTION_STRING, 0x0c }, /* hostname */
49 { OPTION_STRING, 0x0f }, /* domain-name */
50 { OPTION_IP | OPTION_LIST, 0x2a }, /* ntp-servers */
51 { OPTION_U32, 0x33 }, /* dhcp-lease-time */
52 /* Options below will not be exposed to user */
53 { OPTION_IP, 0x32 }, /* requested-ip */
54 { OPTION_U8, 0x35 }, /* message-type */
55 { OPTION_U32, 0x36 }, /* server-id */
56 { OPTION_U16, 0x39 }, /* max-size */
57 { OPTION_STRING, 0x3c }, /* vendor */
58 { OPTION_STRING, 0x3d }, /* client-id */
59 { OPTION_STRING, 0xfc }, /* UNOFFICIAL proxy-pac */
60 { OPTION_UNKNOWN, 0x00 },
63 GDHCPOptionType dhcp_get_code_type(uint8_t code)
67 for (i = 0; client_options[i].code; i++) {
68 if (client_options[i].code == code)
69 return client_options[i].type;
72 return OPTION_UNKNOWN;
75 uint8_t *dhcp_get_option(struct dhcp_packet *packet, int code)
81 /* option bytes: [code][len][data1][data2]..[dataLEN] */
82 optionptr = packet->options;
83 rem = sizeof(packet->options);
87 /* Bad packet, malformed option field */
90 if (optionptr[OPT_CODE] == DHCP_PADDING) {
97 if (optionptr[OPT_CODE] == DHCP_END) {
98 if (overload & FILE_FIELD) {
99 overload &= ~FILE_FIELD;
101 optionptr = packet->file;
102 rem = sizeof(packet->file);
105 } else if (overload & SNAME_FIELD) {
106 overload &= ~SNAME_FIELD;
108 optionptr = packet->sname;
109 rem = sizeof(packet->sname);
117 len = 2 + optionptr[OPT_LEN];
121 continue; /* complain and return NULL */
123 if (optionptr[OPT_CODE] == code)
124 return optionptr + OPT_DATA;
126 if (optionptr[OPT_CODE] == DHCP_OPTION_OVERLOAD)
127 overload |= optionptr[OPT_DATA];
135 int dhcp_end_option(uint8_t *optionptr)
139 while (optionptr[i] != DHCP_END) {
140 if (optionptr[i] != DHCP_PADDING)
141 i += optionptr[i + OPT_LEN] + OPT_DATA - 1;
149 uint8_t *dhcpv6_get_option(struct dhcpv6_packet *packet, uint16_t pkt_len,
150 int code, uint16_t *option_len, int *option_count)
153 uint8_t *optionptr, *found = NULL;
154 uint16_t opt_code, opt_len, len;
156 optionptr = packet->options;
157 rem = pkt_len - 1 - 3;
164 opt_code = optionptr[0] << 8 | optionptr[1];
165 opt_len = len = optionptr[2] << 8 | optionptr[3];
166 len += 2 + 2; /* skip code and len */
172 if (opt_code == code) {
173 if (option_len != NULL)
174 *option_len = opt_len;
175 found = optionptr + 2 + 2;
185 if (option_count != NULL)
186 *option_count = count;
192 * Add an option (supplied in binary form) to the options.
193 * Option format: [code][len][data1][data2]..[dataLEN]
195 void dhcp_add_binary_option(struct dhcp_packet *packet, uint8_t *addopt)
198 uint8_t *optionptr = packet->options;
199 unsigned end = dhcp_end_option(optionptr);
201 len = OPT_DATA + addopt[OPT_LEN];
203 /* end position + (option code/length + addopt length) + end option */
204 if (end + len + 1 >= DHCP_OPTIONS_BUFSIZE)
205 /* option did not fit into the packet */
208 memcpy(optionptr + end, addopt, len);
210 optionptr[end + len] = DHCP_END;
214 * Add an option (supplied in binary form) to the options.
215 * Option format: [code][len][data1][data2]..[dataLEN]
217 void dhcpv6_add_binary_option(struct dhcpv6_packet *packet, uint16_t max_len,
218 uint16_t *pkt_len, uint8_t *addopt)
221 uint8_t *optionptr = packet->options;
223 len = 2 + 2 + (addopt[2] << 8 | addopt[3]);
225 /* end position + (option code/length + addopt length) */
226 if (*pkt_len + len >= max_len)
227 /* option did not fit into the packet */
230 memcpy(optionptr + *pkt_len, addopt, len);
234 void dhcp_add_simple_option(struct dhcp_packet *packet, uint8_t code,
237 uint8_t option[6], len;
238 GDHCPOptionType type = dhcp_get_code_type(code);
240 if (type == OPTION_UNKNOWN)
243 option[OPT_CODE] = code;
245 len = dhcp_option_lengths[type & OPTION_TYPE_MASK];
246 option[OPT_LEN] = len;
248 #if __BYTE_ORDER == __BIG_ENDIAN
249 data <<= 8 * (4 - len);
252 dhcp_put_unaligned(data, (uint32_t *) &option[OPT_DATA]);
253 dhcp_add_binary_option(packet, option);
258 void dhcp_init_header(struct dhcp_packet *packet, char type)
260 memset(packet, 0, sizeof(*packet));
262 packet->op = BOOTREQUEST;
268 packet->op = BOOTREPLY;
273 packet->cookie = htonl(DHCP_MAGIC);
274 packet->options[0] = DHCP_END;
276 dhcp_add_simple_option(packet, DHCP_MESSAGE_TYPE, type);
279 void dhcpv6_init_header(struct dhcpv6_packet *packet, uint8_t type)
283 memset(packet, 0, sizeof(*packet));
285 packet->message = type;
289 packet->transaction_id[0] = (id >> 16) & 0xff;
290 packet->transaction_id[1] = (id >> 8) & 0xff;
291 packet->transaction_id[2] = id & 0xff;
294 static gboolean check_vendor(uint8_t *option_vendor, const char *vendor)
296 uint8_t vendor_length = sizeof(vendor) - 1;
298 if (option_vendor[OPT_LEN - OPT_DATA] != vendor_length)
301 if (memcmp(option_vendor, vendor, vendor_length) != 0)
307 static void check_broken_vendor(struct dhcp_packet *packet)
311 if (packet->op != BOOTREQUEST)
314 vendor = dhcp_get_option(packet, DHCP_VENDOR);
318 if (check_vendor(vendor, "MSFT 98") == TRUE)
319 packet->flags |= htons(BROADCAST_FLAG);
322 int dhcp_recv_l3_packet(struct dhcp_packet *packet, int fd)
326 memset(packet, 0, sizeof(*packet));
328 n = read(fd, packet, sizeof(*packet));
332 if (packet->cookie != htonl(DHCP_MAGIC))
335 check_broken_vendor(packet);
340 int dhcpv6_recv_l3_packet(struct dhcpv6_packet **packet, unsigned char *buf,
345 n = read(fd, buf, buf_len);
349 *packet = (struct dhcpv6_packet *)buf;
354 /* TODO: Use glib checksum */
355 uint16_t dhcp_checksum(void *addr, int count)
358 * Compute Internet Checksum for "count" bytes
359 * beginning at location "addr".
362 uint16_t *source = (uint16_t *) addr;
365 /* This is the inner loop */
370 /* Add left-over byte, if any */
372 /* Make sure that the left-over byte is added correctly both
373 * with little and big endian hosts */
375 *(uint8_t *) &tmp = *(uint8_t *) source;
378 /* Fold 32-bit sum to 16 bits */
380 sum = (sum & 0xffff) + (sum >> 16);
385 #define IN6ADDR_ALL_DHCP_RELAY_AGENTS_AND_SERVERS_MC_INIT \
386 { { { 0xff,0x02,0,0,0,0,0,0,0,0,0,0,0,0x1,0,0x2 } } } /* ff02::1:2 */
387 static const struct in6_addr in6addr_all_dhcp_relay_agents_and_servers_mc =
388 IN6ADDR_ALL_DHCP_RELAY_AGENTS_AND_SERVERS_MC_INIT;
390 /* from netinet/in.h */
392 struct in6_addr ipi6_addr; /* src/dst IPv6 address */
393 unsigned int ipi6_ifindex; /* send/recv interface index */
396 int dhcpv6_send_packet(int index, struct dhcpv6_packet *dhcp_pkt, int len)
400 struct in6_pktinfo *pktinfo;
401 struct cmsghdr *cmsg;
403 struct sockaddr_in6 dst;
405 size_t control_buf_len;
407 fd = socket(PF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
411 memset(&dst, 0, sizeof(dst));
412 dst.sin6_family = AF_INET6;
413 dst.sin6_port = htons(DHCPV6_SERVER_PORT);
415 dst.sin6_addr = in6addr_all_dhcp_relay_agents_and_servers_mc;
417 control_buf_len = CMSG_SPACE(sizeof(struct in6_pktinfo));
418 control_buf = g_try_malloc0(control_buf_len);
419 if (control_buf == NULL) {
424 memset(&m, 0, sizeof(m));
425 memset(&v, 0, sizeof(v));
428 m.msg_namelen = sizeof(dst);
430 v.iov_base = (char *)dhcp_pkt;
435 m.msg_control = control_buf;
436 m.msg_controllen = control_buf_len;
437 cmsg = CMSG_FIRSTHDR(&m);
438 cmsg->cmsg_level = IPPROTO_IPV6;
439 cmsg->cmsg_type = IPV6_PKTINFO;
440 cmsg->cmsg_len = CMSG_LEN(sizeof(*pktinfo));
442 pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg);
443 memset(pktinfo, 0, sizeof(*pktinfo));
444 pktinfo->ipi6_ifindex = index;
445 m.msg_controllen = cmsg->cmsg_len;
447 ret = sendmsg(fd, &m, 0);
449 perror("DHCPv6 msg send failed");
457 int dhcp_send_raw_packet(struct dhcp_packet *dhcp_pkt,
458 uint32_t source_ip, int source_port, uint32_t dest_ip,
459 int dest_port, const uint8_t *dest_arp, int ifindex)
461 struct sockaddr_ll dest;
462 struct ip_udp_dhcp_packet packet;
466 IP_UPD_DHCP_SIZE = sizeof(struct ip_udp_dhcp_packet) -
467 EXTEND_FOR_BUGGY_SERVERS,
468 UPD_DHCP_SIZE = IP_UPD_DHCP_SIZE -
469 offsetof(struct ip_udp_dhcp_packet, udp),
472 fd = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_IP));
476 memset(&dest, 0, sizeof(dest));
477 memset(&packet, 0, sizeof(packet));
478 packet.data = *dhcp_pkt;
480 dest.sll_family = AF_PACKET;
481 dest.sll_protocol = htons(ETH_P_IP);
482 dest.sll_ifindex = ifindex;
484 memcpy(dest.sll_addr, dest_arp, 6);
485 if (bind(fd, (struct sockaddr *)&dest, sizeof(dest)) < 0) {
490 packet.ip.protocol = IPPROTO_UDP;
491 packet.ip.saddr = source_ip;
492 packet.ip.daddr = dest_ip;
493 packet.udp.source = htons(source_port);
494 packet.udp.dest = htons(dest_port);
495 /* size, excluding IP header: */
496 packet.udp.len = htons(UPD_DHCP_SIZE);
497 /* for UDP checksumming, ip.len is set to UDP packet len */
498 packet.ip.tot_len = packet.udp.len;
499 packet.udp.check = dhcp_checksum(&packet, IP_UPD_DHCP_SIZE);
500 /* but for sending, it is set to IP packet len */
501 packet.ip.tot_len = htons(IP_UPD_DHCP_SIZE);
502 packet.ip.ihl = sizeof(packet.ip) >> 2;
503 packet.ip.version = IPVERSION;
504 packet.ip.ttl = IPDEFTTL;
505 packet.ip.check = dhcp_checksum(&packet.ip, sizeof(packet.ip));
508 * Currently we send full-sized DHCP packets (zero padded).
509 * If you need to change this: last byte of the packet is
510 * packet.data.options[dhcp_end_option(packet.data.options)]
512 n = sendto(fd, &packet, IP_UPD_DHCP_SIZE, 0,
513 (struct sockaddr *) &dest, sizeof(dest));
522 int dhcp_send_kernel_packet(struct dhcp_packet *dhcp_pkt,
523 uint32_t source_ip, int source_port,
524 uint32_t dest_ip, int dest_port)
526 struct sockaddr_in client;
530 DHCP_SIZE = sizeof(struct dhcp_packet) -
531 EXTEND_FOR_BUGGY_SERVERS,
534 fd = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
538 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
540 memset(&client, 0, sizeof(client));
541 client.sin_family = AF_INET;
542 client.sin_port = htons(source_port);
543 client.sin_addr.s_addr = source_ip;
544 if (bind(fd, (struct sockaddr *) &client, sizeof(client)) < 0) {
549 memset(&client, 0, sizeof(client));
550 client.sin_family = AF_INET;
551 client.sin_port = htons(dest_port);
552 client.sin_addr.s_addr = dest_ip;
553 if (connect(fd, (struct sockaddr *) &client, sizeof(client)) < 0) {
558 n = write(fd, dhcp_pkt, DHCP_SIZE);
568 int dhcp_l3_socket(int port, const char *interface, int family)
570 int fd, opt = 1, len;
571 struct sockaddr_in addr4;
572 struct sockaddr_in6 addr6;
573 struct sockaddr *addr;
575 fd = socket(family, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
579 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
581 if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,
582 interface, strlen(interface) + 1) < 0) {
587 if (family == AF_INET) {
588 memset(&addr4, 0, sizeof(addr4));
589 addr4.sin_family = family;
590 addr4.sin_port = htons(port);
591 addr = (struct sockaddr *)&addr4;
593 } else if (family == AF_INET6) {
594 memset(&addr6, 0, sizeof(addr6));
595 addr6.sin6_family = family;
596 addr6.sin6_port = htons(port);
597 addr = (struct sockaddr *)&addr6;
604 if (bind(fd, addr, len) != 0) {
612 char *get_interface_name(int index)
620 sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
622 perror("Open socket error");
626 memset(&ifr, 0, sizeof(ifr));
627 ifr.ifr_ifindex = index;
629 err = ioctl(sk, SIOCGIFNAME, &ifr);
631 perror("Get interface name error");
638 return g_strdup(ifr.ifr_name);
641 gboolean interface_is_up(int index)
645 gboolean ret = FALSE;
647 sk = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
649 perror("Open socket error");
653 memset(&ifr, 0, sizeof(ifr));
654 ifr.ifr_ifindex = index;
656 err = ioctl(sk, SIOCGIFNAME, &ifr);
658 perror("Get interface name error");
662 err = ioctl(sk, SIOCGIFFLAGS, &ifr);
664 perror("Get interface flags error");
668 if (ifr.ifr_flags & IFF_UP)