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
28 #include <sys/ioctl.h>
32 #include <net/if_arp.h>
34 #include <netpacket/packet.h>
35 #include <net/ethernet.h>
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 },
59 GDHCPOptionType dhcp_get_code_type(uint8_t code)
63 for (i = 0; client_options[i].code; i++) {
64 if (client_options[i].code == code)
65 return client_options[i].type;
68 return OPTION_UNKNOWN;
71 uint8_t *dhcp_get_option(struct dhcp_packet *packet, int code)
77 /* option bytes: [code][len][data1][data2]..[dataLEN] */
78 optionptr = packet->options;
79 rem = sizeof(packet->options);
83 /* Bad packet, malformed option field */
86 if (optionptr[OPT_CODE] == DHCP_PADDING) {
93 if (optionptr[OPT_CODE] == DHCP_END) {
94 if (overload & FILE_FIELD) {
95 overload &= ~FILE_FIELD;
97 optionptr = packet->file;
98 rem = sizeof(packet->file);
101 } else if (overload & SNAME_FIELD) {
102 overload &= ~SNAME_FIELD;
104 optionptr = packet->sname;
105 rem = sizeof(packet->sname);
113 len = 2 + optionptr[OPT_LEN];
117 continue; /* complain and return NULL */
119 if (optionptr[OPT_CODE] == code)
120 return optionptr + OPT_DATA;
122 if (optionptr[OPT_CODE] == DHCP_OPTION_OVERLOAD)
123 overload |= optionptr[OPT_DATA];
131 int dhcp_end_option(uint8_t *optionptr)
135 while (optionptr[i] != DHCP_END) {
136 if (optionptr[i] != DHCP_PADDING)
137 i += optionptr[i + OPT_LEN] + OPT_DATA - 1;
146 * Add an option (supplied in binary form) to the options.
147 * Option format: [code][len][data1][data2]..[dataLEN]
149 void dhcp_add_binary_option(struct dhcp_packet *packet, uint8_t *addopt)
152 uint8_t *optionptr = packet->options;
153 unsigned end = dhcp_end_option(optionptr);
155 len = OPT_DATA + addopt[OPT_LEN];
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 */
162 memcpy(optionptr + end, addopt, len);
164 optionptr[end + len] = DHCP_END;
167 void dhcp_add_simple_option(struct dhcp_packet *packet, uint8_t code,
170 uint8_t option[6], len;
171 GDHCPOptionType type = dhcp_get_code_type(code);
173 if (type == OPTION_UNKNOWN)
176 option[OPT_CODE] = code;
178 len = dhcp_option_lengths[type & OPTION_TYPE_MASK];
179 option[OPT_LEN] = len;
181 #if __BYTE_ORDER == __BIG_ENDIAN
182 data <<= 8 * (4 - len);
185 dhcp_put_unaligned(data, (uint32_t *) &option[OPT_DATA]);
186 dhcp_add_binary_option(packet, option);
191 void dhcp_init_header(struct dhcp_packet *packet, char type)
193 memset(packet, 0, sizeof(*packet));
195 packet->op = BOOTREQUEST;
201 packet->op = BOOTREPLY;
206 packet->cookie = htonl(DHCP_MAGIC);
207 packet->options[0] = DHCP_END;
209 dhcp_add_simple_option(packet, DHCP_MESSAGE_TYPE, type);
212 static gboolean check_vendor(uint8_t *option_vendor, const char *vendor)
214 uint8_t vendor_length = sizeof(vendor) - 1;
216 if (option_vendor[OPT_LEN - OPT_DATA] != vendor_length)
219 if (memcmp(option_vendor, vendor, vendor_length) != 0)
225 static void check_broken_vendor(struct dhcp_packet *packet)
229 if (packet->op != BOOTREQUEST)
232 vendor = dhcp_get_option(packet, DHCP_VENDOR);
236 if (check_vendor(vendor, "MSFT 98") == TRUE)
237 packet->flags |= htons(BROADCAST_FLAG);
240 int dhcp_recv_l3_packet(struct dhcp_packet *packet, int fd)
244 memset(packet, 0, sizeof(*packet));
246 n = read(fd, packet, sizeof(*packet));
250 if (packet->cookie != htonl(DHCP_MAGIC))
253 check_broken_vendor(packet);
258 /* TODO: Use glib checksum */
259 uint16_t dhcp_checksum(void *addr, int count)
262 * Compute Internet Checksum for "count" bytes
263 * beginning at location "addr".
266 uint16_t *source = (uint16_t *) addr;
269 /* This is the inner loop */
274 /* Add left-over byte, if any */
276 /* Make sure that the left-over byte is added correctly both
277 * with little and big endian hosts */
279 *(uint8_t *) &tmp = *(uint8_t *) source;
282 /* Fold 32-bit sum to 16 bits */
284 sum = (sum & 0xffff) + (sum >> 16);
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)
293 struct sockaddr_ll dest;
294 struct ip_udp_dhcp_packet packet;
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),
304 fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
308 memset(&dest, 0, sizeof(dest));
309 memset(&packet, 0, sizeof(packet));
310 packet.data = *dhcp_pkt;
312 dest.sll_family = AF_PACKET;
313 dest.sll_protocol = htons(ETH_P_IP);
314 dest.sll_ifindex = ifindex;
316 memcpy(dest.sll_addr, dest_arp, 6);
317 if (bind(fd, (struct sockaddr *)&dest, sizeof(dest)) < 0) {
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));
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)]
344 n = sendto(fd, &packet, IP_UPD_DHCP_SIZE, 0,
345 (struct sockaddr *) &dest, sizeof(dest));
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)
358 struct sockaddr_in client;
362 DHCP_SIZE = sizeof(struct dhcp_packet) -
363 EXTEND_FOR_BUGGY_SERVERS,
366 fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
370 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
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) {
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) {
390 n = write(fd, dhcp_pkt, DHCP_SIZE);
400 int dhcp_l3_socket(int port, const char *interface)
403 struct sockaddr_in addr;
405 fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
407 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
409 if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,
410 interface, strlen(interface) + 1) < 0) {
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) {
426 char *get_interface_name(int index)
434 sk = socket(PF_INET, SOCK_DGRAM, 0);
436 perror("Open socket error");
440 memset(&ifr, 0, sizeof(ifr));
441 ifr.ifr_ifindex = index;
443 err = ioctl(sk, SIOCGIFNAME, &ifr);
445 perror("Get interface name error");
452 return g_strdup(ifr.ifr_name);
455 gboolean interface_is_up(int index)
459 gboolean ret = FALSE;
461 sk = socket(PF_INET, SOCK_DGRAM, 0);
463 perror("Open socket error");
467 memset(&ifr, 0, sizeof(ifr));
468 ifr.ifr_ifindex = index;
470 err = ioctl(sk, SIOCGIFNAME, &ifr);
472 perror("Get interface name error");
476 err = ioctl(sk, SIOCGIFFLAGS, &ifr);
478 perror("Get interface flags error");
482 if (ifr.ifr_flags & IFF_UP)