1e9639606Spooka /*
2e9639606Spooka * dhcpcd - DHCP client daemon
3e9639606Spooka * Copyright (c) 2006-2010 Roy Marples <roy@marples.name>
4e9639606Spooka * All rights reserved
5e9639606Spooka
6e9639606Spooka * Redistribution and use in source and binary forms, with or without
7e9639606Spooka * modification, are permitted provided that the following conditions
8e9639606Spooka * are met:
9e9639606Spooka * 1. Redistributions of source code must retain the above copyright
10e9639606Spooka * notice, this list of conditions and the following disclaimer.
11e9639606Spooka * 2. Redistributions in binary form must reproduce the above copyright
12e9639606Spooka * notice, this list of conditions and the following disclaimer in the
13e9639606Spooka * documentation and/or other materials provided with the distribution.
14e9639606Spooka *
15e9639606Spooka * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16e9639606Spooka * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17e9639606Spooka * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18e9639606Spooka * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19e9639606Spooka * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20e9639606Spooka * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21e9639606Spooka * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22e9639606Spooka * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23e9639606Spooka * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24e9639606Spooka * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25e9639606Spooka * SUCH DAMAGE.
26e9639606Spooka */
27e9639606Spooka
28e9639606Spooka #include <sys/types.h>
29e9639606Spooka #include <sys/ioctl.h>
30e9639606Spooka #include <sys/param.h>
31e9639606Spooka #include <sys/socket.h>
32e9639606Spooka #include <sys/time.h>
33e9639606Spooka
34e9639606Spooka #include <arpa/inet.h>
35e9639606Spooka #include <net/if.h>
36e9639606Spooka #include <net/if_arp.h>
37e9639606Spooka #ifdef AF_LINK
38e9639606Spooka # include <net/if_dl.h>
39e9639606Spooka # include <net/if_types.h>
40e9639606Spooka #endif
41e9639606Spooka #include <netinet/in_systm.h>
42e9639606Spooka #include <netinet/in.h>
43e9639606Spooka #include <netinet/ip.h>
44e9639606Spooka #define __FAVOR_BSD /* Nasty glibc hack so we can use BSD semantics for UDP */
45e9639606Spooka #include <netinet/udp.h>
46e9639606Spooka #undef __FAVOR_BSD
47e9639606Spooka #ifdef AF_PACKET
48e9639606Spooka # include <netpacket/packet.h>
49e9639606Spooka #endif
50e9639606Spooka #ifdef SIOCGIFMEDIA
51e9639606Spooka # include <net/if_media.h>
52e9639606Spooka #endif
53e9639606Spooka
54e9639606Spooka #include <ctype.h>
55e9639606Spooka #include <errno.h>
56e9639606Spooka #include <ifaddrs.h>
57e9639606Spooka #include <fnmatch.h>
58e9639606Spooka #include <stddef.h>
59e9639606Spooka #include <stdio.h>
60e9639606Spooka #include <stdlib.h>
61e9639606Spooka #include <string.h>
62e9639606Spooka #include <unistd.h>
63e9639606Spooka
64e9639606Spooka #include "common.h"
65e9639606Spooka #include "dhcp.h"
66e9639606Spooka #include "if-options.h"
67e9639606Spooka #include "net.h"
68e9639606Spooka
69e9639606Spooka #include <rump/rump_syscalls.h>
70e9639606Spooka
71e9639606Spooka static char hwaddr_buffer[(HWADDR_LEN * 3) + 1];
72e9639606Spooka
73e9639606Spooka int socket_afnet = -1;
74e9639606Spooka
75e9639606Spooka int
inet_ntocidr(struct in_addr address)76e9639606Spooka inet_ntocidr(struct in_addr address)
77e9639606Spooka {
78e9639606Spooka int cidr = 0;
79e9639606Spooka uint32_t mask = htonl(address.s_addr);
80e9639606Spooka
81e9639606Spooka while (mask) {
82e9639606Spooka cidr++;
83e9639606Spooka mask <<= 1;
84e9639606Spooka }
85e9639606Spooka return cidr;
86e9639606Spooka }
87e9639606Spooka
88e9639606Spooka int
inet_cidrtoaddr(int cidr,struct in_addr * addr)89e9639606Spooka inet_cidrtoaddr(int cidr, struct in_addr *addr)
90e9639606Spooka {
91e9639606Spooka int ocets;
92e9639606Spooka
93e9639606Spooka if (cidr < 1 || cidr > 32) {
94e9639606Spooka errno = EINVAL;
95e9639606Spooka return -1;
96e9639606Spooka }
97e9639606Spooka ocets = (cidr + 7) / 8;
98e9639606Spooka
99e9639606Spooka addr->s_addr = 0;
100e9639606Spooka if (ocets > 0) {
101e9639606Spooka memset(&addr->s_addr, 255, (size_t)ocets - 1);
102e9639606Spooka memset((unsigned char *)&addr->s_addr + (ocets - 1),
103e9639606Spooka (256 - (1 << (32 - cidr) % 8)), 1);
104e9639606Spooka }
105e9639606Spooka
106e9639606Spooka return 0;
107e9639606Spooka }
108e9639606Spooka
109e9639606Spooka uint32_t
get_netmask(uint32_t addr)110e9639606Spooka get_netmask(uint32_t addr)
111e9639606Spooka {
112e9639606Spooka uint32_t dst;
113e9639606Spooka
114e9639606Spooka if (addr == 0)
115e9639606Spooka return 0;
116e9639606Spooka
117e9639606Spooka dst = htonl(addr);
118e9639606Spooka if (IN_CLASSA(dst))
119e9639606Spooka return ntohl(IN_CLASSA_NET);
120e9639606Spooka if (IN_CLASSB(dst))
121e9639606Spooka return ntohl(IN_CLASSB_NET);
122e9639606Spooka if (IN_CLASSC(dst))
123e9639606Spooka return ntohl(IN_CLASSC_NET);
124e9639606Spooka
125e9639606Spooka return 0;
126e9639606Spooka }
127e9639606Spooka
128e9639606Spooka char *
hwaddr_ntoa(const unsigned char * hwaddr,size_t hwlen)129e9639606Spooka hwaddr_ntoa(const unsigned char *hwaddr, size_t hwlen)
130e9639606Spooka {
131e9639606Spooka char *p = hwaddr_buffer;
132e9639606Spooka size_t i;
133e9639606Spooka
134e9639606Spooka for (i = 0; i < hwlen && i < HWADDR_LEN; i++) {
135e9639606Spooka if (i > 0)
136e9639606Spooka *p ++= ':';
137e9639606Spooka p += snprintf(p, 3, "%.2x", hwaddr[i]);
138e9639606Spooka }
139e9639606Spooka
140e9639606Spooka *p ++= '\0';
141e9639606Spooka
142e9639606Spooka return hwaddr_buffer;
143e9639606Spooka }
144e9639606Spooka
145e9639606Spooka size_t
hwaddr_aton(unsigned char * buffer,const char * addr)146e9639606Spooka hwaddr_aton(unsigned char *buffer, const char *addr)
147e9639606Spooka {
148e9639606Spooka char c[3];
149e9639606Spooka const char *p = addr;
150e9639606Spooka unsigned char *bp = buffer;
151e9639606Spooka size_t len = 0;
152e9639606Spooka
153e9639606Spooka c[2] = '\0';
154e9639606Spooka while (*p) {
155e9639606Spooka c[0] = *p++;
156e9639606Spooka c[1] = *p++;
157e9639606Spooka /* Ensure that digits are hex */
158e9639606Spooka if (isxdigit((unsigned char)c[0]) == 0 ||
159e9639606Spooka isxdigit((unsigned char)c[1]) == 0)
160e9639606Spooka {
161e9639606Spooka errno = EINVAL;
162e9639606Spooka return 0;
163e9639606Spooka }
164e9639606Spooka /* We should have at least two entries 00:01 */
165e9639606Spooka if (len == 0 && *p == '\0') {
166e9639606Spooka errno = EINVAL;
167e9639606Spooka return 0;
168e9639606Spooka }
169*d1c4e519Sandvar /* Ensure that next data is EOL or a separator with data */
170e9639606Spooka if (!(*p == '\0' || (*p == ':' && *(p + 1) != '\0'))) {
171e9639606Spooka errno = EINVAL;
172e9639606Spooka return 0;
173e9639606Spooka }
174e9639606Spooka if (*p)
175e9639606Spooka p++;
176e9639606Spooka if (bp)
177e9639606Spooka *bp++ = (unsigned char)strtol(c, NULL, 16);
178e9639606Spooka len++;
179e9639606Spooka }
180e9639606Spooka return len;
181e9639606Spooka }
182e9639606Spooka
183e9639606Spooka struct interface *
init_interface(const char * ifname)184e9639606Spooka init_interface(const char *ifname)
185e9639606Spooka {
186e9639606Spooka struct ifreq ifr;
187e9639606Spooka struct interface *iface = NULL;
188e9639606Spooka
189e9639606Spooka memset(&ifr, 0, sizeof(ifr));
190e9639606Spooka strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
191e9639606Spooka if (rump_sys_ioctl(socket_afnet, SIOCGIFFLAGS, &ifr) == -1)
192e9639606Spooka goto eexit;
193e9639606Spooka
194e9639606Spooka iface = xzalloc(sizeof(*iface));
195e9639606Spooka strlcpy(iface->name, ifname, sizeof(iface->name));
196e9639606Spooka iface->flags = ifr.ifr_flags;
197e9639606Spooka /* We reserve the 100 range for virtual interfaces, if and when
198e9639606Spooka * we can work them out. */
199e9639606Spooka iface->metric = 200 + if_nametoindex(iface->name);
200e9639606Spooka if (getifssid(ifname, iface->ssid) != -1) {
201e9639606Spooka iface->wireless = 1;
202e9639606Spooka iface->metric += 100;
203e9639606Spooka }
204e9639606Spooka
205e9639606Spooka if (rump_sys_ioctl(socket_afnet, SIOCGIFMTU, &ifr) == -1)
206e9639606Spooka goto eexit;
207e9639606Spooka /* Ensure that the MTU is big enough for DHCP */
208e9639606Spooka if (ifr.ifr_mtu < MTU_MIN) {
209e9639606Spooka ifr.ifr_mtu = MTU_MIN;
210e9639606Spooka strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
211e9639606Spooka if (rump_sys_ioctl(socket_afnet, SIOCSIFMTU, &ifr) == -1)
212e9639606Spooka goto eexit;
213e9639606Spooka }
214e9639606Spooka
215e9639606Spooka /* 0 is a valid fd, so init to -1 */
216e9639606Spooka iface->raw_fd = -1;
217e9639606Spooka iface->udp_fd = -1;
218e9639606Spooka iface->arp_fd = -1;
219e9639606Spooka goto exit;
220e9639606Spooka
221e9639606Spooka eexit:
222e9639606Spooka free(iface);
223e9639606Spooka iface = NULL;
224e9639606Spooka exit:
225e9639606Spooka return iface;
226e9639606Spooka }
227e9639606Spooka
228e9639606Spooka int
carrier_status(struct interface * iface)229e9639606Spooka carrier_status(struct interface *iface)
230e9639606Spooka {
231e9639606Spooka int ret;
232e9639606Spooka struct ifreq ifr;
233e9639606Spooka #ifdef SIOCGIFMEDIA
234e9639606Spooka struct ifmediareq ifmr;
235e9639606Spooka #endif
236e9639606Spooka #ifdef __linux__
237e9639606Spooka char *p;
238e9639606Spooka #endif
239e9639606Spooka
240e9639606Spooka memset(&ifr, 0, sizeof(ifr));
241e9639606Spooka strlcpy(ifr.ifr_name, iface->name, sizeof(ifr.ifr_name));
242e9639606Spooka #ifdef __linux__
243e9639606Spooka /* We can only test the real interface up */
244e9639606Spooka if ((p = strchr(ifr.ifr_name, ':')))
245e9639606Spooka *p = '\0';
246e9639606Spooka #endif
247e9639606Spooka
248e9639606Spooka if (rump_sys_ioctl(socket_afnet, SIOCGIFFLAGS, &ifr) == -1)
249e9639606Spooka return -1;
250e9639606Spooka iface->flags = ifr.ifr_flags;
251e9639606Spooka
252e9639606Spooka ret = -1;
253e9639606Spooka #ifdef SIOCGIFMEDIA
254e9639606Spooka memset(&ifmr, 0, sizeof(ifmr));
255e9639606Spooka strlcpy(ifmr.ifm_name, iface->name, sizeof(ifmr.ifm_name));
256e9639606Spooka if (rump_sys_ioctl(socket_afnet, SIOCGIFMEDIA, &ifmr) != -1 &&
257e9639606Spooka ifmr.ifm_status & IFM_AVALID)
258e9639606Spooka ret = (ifmr.ifm_status & IFM_ACTIVE) ? 1 : 0;
259e9639606Spooka #endif
260e9639606Spooka if (ret == -1)
261e9639606Spooka ret = (ifr.ifr_flags & IFF_RUNNING) ? 1 : 0;
262e9639606Spooka return ret;
263e9639606Spooka }
264e9639606Spooka
265e9639606Spooka int
up_interface(struct interface * iface)266e9639606Spooka up_interface(struct interface *iface)
267e9639606Spooka {
268e9639606Spooka struct ifreq ifr;
269e9639606Spooka int retval = -1;
270e9639606Spooka #ifdef __linux__
271e9639606Spooka char *p;
272e9639606Spooka #endif
273e9639606Spooka
274e9639606Spooka memset(&ifr, 0, sizeof(ifr));
275e9639606Spooka strlcpy(ifr.ifr_name, iface->name, sizeof(ifr.ifr_name));
276e9639606Spooka #ifdef __linux__
277e9639606Spooka /* We can only bring the real interface up */
278e9639606Spooka if ((p = strchr(ifr.ifr_name, ':')))
279e9639606Spooka *p = '\0';
280e9639606Spooka #endif
281e9639606Spooka if (rump_sys_ioctl(socket_afnet, SIOCGIFFLAGS, &ifr) == 0) {
282e9639606Spooka if ((ifr.ifr_flags & IFF_UP))
283e9639606Spooka retval = 0;
284e9639606Spooka else {
285e9639606Spooka ifr.ifr_flags |= IFF_UP;
286e9639606Spooka if (rump_sys_ioctl(socket_afnet, SIOCSIFFLAGS, &ifr) == 0)
287e9639606Spooka retval = 0;
288e9639606Spooka }
289e9639606Spooka iface->flags = ifr.ifr_flags;
290e9639606Spooka }
291e9639606Spooka return retval;
292e9639606Spooka }
293e9639606Spooka
294e9639606Spooka int
do_address(const char * ifname,struct in_addr * addr,struct in_addr * net,struct in_addr * dst,int act)295e9639606Spooka do_address(const char *ifname,
296e9639606Spooka struct in_addr *addr, struct in_addr *net, struct in_addr *dst, int act)
297e9639606Spooka {
298e9639606Spooka struct ifaddrs *ifaddrs, *ifa;
299e9639606Spooka const struct sockaddr_in *a, *n, *d;
300e9639606Spooka int retval;
301e9639606Spooka
302e9639606Spooka if (getifaddrs(&ifaddrs) == -1)
303e9639606Spooka return -1;
304e9639606Spooka
305e9639606Spooka retval = 0;
306e9639606Spooka for (ifa = ifaddrs; ifa; ifa = ifa->ifa_next) {
307e9639606Spooka if (ifa->ifa_addr == NULL ||
308e9639606Spooka ifa->ifa_addr->sa_family != AF_INET ||
309e9639606Spooka strcmp(ifa->ifa_name, ifname) != 0)
310e9639606Spooka continue;
311e9639606Spooka a = (const struct sockaddr_in *)(void *)ifa->ifa_addr;
312e9639606Spooka n = (const struct sockaddr_in *)(void *)ifa->ifa_netmask;
313e9639606Spooka if (ifa->ifa_flags & IFF_POINTOPOINT)
314e9639606Spooka d = (const struct sockaddr_in *)(void *)
315e9639606Spooka ifa->ifa_dstaddr;
316e9639606Spooka else
317e9639606Spooka d = NULL;
318e9639606Spooka if (act == 1) {
319e9639606Spooka addr->s_addr = a->sin_addr.s_addr;
320e9639606Spooka net->s_addr = n->sin_addr.s_addr;
321e9639606Spooka if (dst) {
322e9639606Spooka if (ifa->ifa_flags & IFF_POINTOPOINT)
323e9639606Spooka dst->s_addr = d->sin_addr.s_addr;
324e9639606Spooka else
325e9639606Spooka dst->s_addr = INADDR_ANY;
326e9639606Spooka }
327e9639606Spooka retval = 1;
328e9639606Spooka break;
329e9639606Spooka }
330e9639606Spooka if (addr->s_addr == a->sin_addr.s_addr &&
331e9639606Spooka (net == NULL || net->s_addr == n->sin_addr.s_addr))
332e9639606Spooka {
333e9639606Spooka retval = 1;
334e9639606Spooka break;
335e9639606Spooka }
336e9639606Spooka }
337e9639606Spooka freeifaddrs(ifaddrs);
338e9639606Spooka return retval;
339e9639606Spooka }
340e9639606Spooka
341e9639606Spooka int
do_mtu(const char * ifname,short int mtu)342e9639606Spooka do_mtu(const char *ifname, short int mtu)
343e9639606Spooka {
344e9639606Spooka struct ifreq ifr;
345e9639606Spooka int r;
346e9639606Spooka
347e9639606Spooka memset(&ifr, 0, sizeof(ifr));
348e9639606Spooka strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
349e9639606Spooka ifr.ifr_mtu = mtu;
350e9639606Spooka r = rump_sys_ioctl(socket_afnet, mtu ? SIOCSIFMTU : SIOCGIFMTU, &ifr);
351e9639606Spooka if (r == -1)
352e9639606Spooka return -1;
353e9639606Spooka return ifr.ifr_mtu;
354e9639606Spooka }
355e9639606Spooka
356e9639606Spooka void
free_routes(struct rt * routes)357e9639606Spooka free_routes(struct rt *routes)
358e9639606Spooka {
359e9639606Spooka struct rt *r;
360e9639606Spooka
361e9639606Spooka while (routes) {
362e9639606Spooka r = routes->next;
363e9639606Spooka free(routes);
364e9639606Spooka routes = r;
365e9639606Spooka }
366e9639606Spooka }
367e9639606Spooka
368e9639606Spooka struct udp_dhcp_packet
369e9639606Spooka {
370e9639606Spooka struct ip ip;
371e9639606Spooka struct udphdr udp;
372e9639606Spooka struct dhcp_message dhcp;
373e9639606Spooka };
374e9639606Spooka const size_t udp_dhcp_len = sizeof(struct udp_dhcp_packet);
375e9639606Spooka
376e9639606Spooka static uint16_t
checksum(const void * data,uint16_t len)377e9639606Spooka checksum(const void *data, uint16_t len)
378e9639606Spooka {
379e9639606Spooka const uint8_t *addr = data;
380e9639606Spooka uint32_t sum = 0;
381e9639606Spooka
382e9639606Spooka while (len > 1) {
383e9639606Spooka sum += addr[0] * 256 + addr[1];
384e9639606Spooka addr += 2;
385e9639606Spooka len -= 2;
386e9639606Spooka }
387e9639606Spooka
388e9639606Spooka if (len == 1)
389e9639606Spooka sum += *addr * 256;
390e9639606Spooka
391e9639606Spooka sum = (sum >> 16) + (sum & 0xffff);
392e9639606Spooka sum += (sum >> 16);
393e9639606Spooka
394e9639606Spooka sum = htons(sum);
395e9639606Spooka
396e9639606Spooka return ~sum;
397e9639606Spooka }
398e9639606Spooka
399e9639606Spooka ssize_t
make_udp_packet(uint8_t ** packet,const uint8_t * data,size_t length,struct in_addr source,struct in_addr dest)400e9639606Spooka make_udp_packet(uint8_t **packet, const uint8_t *data, size_t length,
401e9639606Spooka struct in_addr source, struct in_addr dest)
402e9639606Spooka {
403e9639606Spooka struct udp_dhcp_packet *udpp;
404e9639606Spooka struct ip *ip;
405e9639606Spooka struct udphdr *udp;
406e9639606Spooka
407e9639606Spooka udpp = xzalloc(sizeof(*udpp));
408e9639606Spooka ip = &udpp->ip;
409e9639606Spooka udp = &udpp->udp;
410e9639606Spooka
411e9639606Spooka /* OK, this is important :)
412e9639606Spooka * We copy the data to our packet and then create a small part of the
413e9639606Spooka * ip structure and an invalid ip_len (basically udp length).
414e9639606Spooka * We then fill the udp structure and put the checksum
415e9639606Spooka * of the whole packet into the udp checksum.
416e9639606Spooka * Finally we complete the ip structure and ip checksum.
417e9639606Spooka * If we don't do the ordering like so then the udp checksum will be
418e9639606Spooka * broken, so find another way of doing it! */
419e9639606Spooka
420e9639606Spooka memcpy(&udpp->dhcp, data, length);
421e9639606Spooka
422e9639606Spooka ip->ip_p = IPPROTO_UDP;
423e9639606Spooka ip->ip_src.s_addr = source.s_addr;
424e9639606Spooka if (dest.s_addr == 0)
425e9639606Spooka ip->ip_dst.s_addr = INADDR_BROADCAST;
426e9639606Spooka else
427e9639606Spooka ip->ip_dst.s_addr = dest.s_addr;
428e9639606Spooka
429e9639606Spooka udp->uh_sport = htons(DHCP_CLIENT_PORT);
430e9639606Spooka udp->uh_dport = htons(DHCP_SERVER_PORT);
431e9639606Spooka udp->uh_ulen = htons(sizeof(*udp) + length);
432e9639606Spooka ip->ip_len = udp->uh_ulen;
433e9639606Spooka udp->uh_sum = checksum(udpp, sizeof(*udpp));
434e9639606Spooka
435e9639606Spooka ip->ip_v = IPVERSION;
436e9639606Spooka ip->ip_hl = sizeof(*ip) >> 2;
437e9639606Spooka ip->ip_id = arc4random() & UINT16_MAX;
438e9639606Spooka ip->ip_ttl = IPDEFTTL;
439e9639606Spooka ip->ip_len = htons(sizeof(*ip) + sizeof(*udp) + length);
440e9639606Spooka ip->ip_sum = checksum(ip, sizeof(*ip));
441e9639606Spooka
442e9639606Spooka *packet = (uint8_t *)udpp;
443e9639606Spooka return sizeof(*ip) + sizeof(*udp) + length;
444e9639606Spooka }
445e9639606Spooka
446e9639606Spooka ssize_t
get_udp_data(const uint8_t ** data,const uint8_t * udp)447e9639606Spooka get_udp_data(const uint8_t **data, const uint8_t *udp)
448e9639606Spooka {
449e9639606Spooka struct udp_dhcp_packet packet;
450e9639606Spooka
451e9639606Spooka memcpy(&packet, udp, sizeof(packet));
452e9639606Spooka *data = udp + offsetof(struct udp_dhcp_packet, dhcp);
453e9639606Spooka return ntohs(packet.ip.ip_len) -
454e9639606Spooka sizeof(packet.ip) -
455e9639606Spooka sizeof(packet.udp);
456e9639606Spooka }
457e9639606Spooka
458e9639606Spooka int
valid_udp_packet(const uint8_t * data,size_t data_len,struct in_addr * from)459e9639606Spooka valid_udp_packet(const uint8_t *data, size_t data_len, struct in_addr *from)
460e9639606Spooka {
461e9639606Spooka struct udp_dhcp_packet packet;
462e9639606Spooka uint16_t bytes, udpsum;
463e9639606Spooka
464e9639606Spooka if (data_len < sizeof(packet.ip)) {
465e9639606Spooka if (from)
466e9639606Spooka from->s_addr = INADDR_ANY;
467e9639606Spooka errno = EINVAL;
468e9639606Spooka return -1;
469e9639606Spooka }
470e9639606Spooka memcpy(&packet, data, MIN(data_len, sizeof(packet)));
471e9639606Spooka if (from)
472e9639606Spooka from->s_addr = packet.ip.ip_src.s_addr;
473e9639606Spooka if (data_len > sizeof(packet)) {
474e9639606Spooka errno = EINVAL;
475e9639606Spooka return -1;
476e9639606Spooka }
477e9639606Spooka if (checksum(&packet.ip, sizeof(packet.ip)) != 0) {
478e9639606Spooka errno = EINVAL;
479e9639606Spooka return -1;
480e9639606Spooka }
481e9639606Spooka
482e9639606Spooka bytes = ntohs(packet.ip.ip_len);
483e9639606Spooka if (data_len < bytes) {
484e9639606Spooka errno = EINVAL;
485e9639606Spooka return -1;
486e9639606Spooka }
487e9639606Spooka udpsum = packet.udp.uh_sum;
488e9639606Spooka packet.udp.uh_sum = 0;
489e9639606Spooka packet.ip.ip_hl = 0;
490e9639606Spooka packet.ip.ip_v = 0;
491e9639606Spooka packet.ip.ip_tos = 0;
492e9639606Spooka packet.ip.ip_len = packet.udp.uh_ulen;
493e9639606Spooka packet.ip.ip_id = 0;
494e9639606Spooka packet.ip.ip_off = 0;
495e9639606Spooka packet.ip.ip_ttl = 0;
496e9639606Spooka packet.ip.ip_sum = 0;
497e9639606Spooka if (udpsum && checksum(&packet, bytes) != udpsum) {
498e9639606Spooka errno = EINVAL;
499e9639606Spooka return -1;
500e9639606Spooka }
501e9639606Spooka
502e9639606Spooka return 0;
503e9639606Spooka }
504