net.c revision e86eee143ed21592f88a46623a81f71002430459
1/*
2 * dhcpcd - DHCP client daemon
3 * Copyright (c) 2006-2010 Roy Marples <roy@marples.name>
4 * All rights reserved
5
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include <sys/types.h>
29#include <sys/ioctl.h>
30#include <sys/param.h>
31#include <sys/socket.h>
32#include <sys/time.h>
33
34#include <arpa/inet.h>
35#include <net/if.h>
36#include <net/if_arp.h>
37#ifdef AF_LINK
38#  include <net/if_dl.h>
39#  include <net/if_types.h>
40#endif
41#include <netinet/in_systm.h>
42#include <netinet/in.h>
43#include <netinet/ip.h>
44#define __FAVOR_BSD /* Nasty glibc hack so we can use BSD semantics for UDP */
45#include <netinet/udp.h>
46#undef __FAVOR_BSD
47#ifdef AF_PACKET
48#  include <netpacket/packet.h>
49#endif
50#ifdef SIOCGIFMEDIA
51#  include <net/if_media.h>
52#endif
53
54#include <ctype.h>
55#include <errno.h>
56#include <ifaddrs.h>
57#include <fnmatch.h>
58#include <stddef.h>
59#include <stdio.h>
60#include <stdlib.h>
61#include <string.h>
62#include <syslog.h>
63#include <unistd.h>
64
65#include "config.h"
66#include "common.h"
67#include "dhcp.h"
68#include "if-options.h"
69#include "net.h"
70#include "signals.h"
71
72static char hwaddr_buffer[(HWADDR_LEN * 3) + 1];
73
74int socket_afnet = -1;
75
76int
77inet_ntocidr(struct in_addr address)
78{
79	int cidr = 0;
80	uint32_t mask = htonl(address.s_addr);
81
82	while (mask) {
83		cidr++;
84		mask <<= 1;
85	}
86	return cidr;
87}
88
89int
90inet_cidrtoaddr(int cidr, struct in_addr *addr)
91{
92	int ocets;
93
94	if (cidr < 1 || cidr > 32) {
95		errno = EINVAL;
96		return -1;
97	}
98	ocets = (cidr + 7) / 8;
99
100	addr->s_addr = 0;
101	if (ocets > 0) {
102		memset(&addr->s_addr, 255, (size_t)ocets - 1);
103		memset((unsigned char *)&addr->s_addr + (ocets - 1),
104		    (256 - (1 << (32 - cidr) % 8)), 1);
105	}
106
107	return 0;
108}
109
110uint32_t
111get_netmask(uint32_t addr)
112{
113	uint32_t dst;
114
115	if (addr == 0)
116		return 0;
117
118	dst = htonl(addr);
119	if (IN_CLASSA(dst))
120		return ntohl(IN_CLASSA_NET);
121	if (IN_CLASSB(dst))
122		return ntohl(IN_CLASSB_NET);
123	if (IN_CLASSC(dst))
124		return ntohl(IN_CLASSC_NET);
125
126	return 0;
127}
128
129char *
130hwaddr_ntoa(const unsigned char *hwaddr, size_t hwlen)
131{
132	char *p = hwaddr_buffer;
133	size_t i;
134
135	for (i = 0; i < hwlen && i < HWADDR_LEN; i++) {
136		if (i > 0)
137			*p ++= ':';
138		p += snprintf(p, 3, "%.2x", hwaddr[i]);
139	}
140
141	*p ++= '\0';
142
143	return hwaddr_buffer;
144}
145
146size_t
147hwaddr_aton(unsigned char *buffer, const char *addr)
148{
149	char c[3];
150	const char *p = addr;
151	unsigned char *bp = buffer;
152	size_t len = 0;
153
154	c[2] = '\0';
155	while (*p) {
156		c[0] = *p++;
157		c[1] = *p++;
158		/* Ensure that digits are hex */
159		if (isxdigit((unsigned char)c[0]) == 0 ||
160		    isxdigit((unsigned char)c[1]) == 0)
161		{
162			errno = EINVAL;
163			return 0;
164		}
165		/* We should have at least two entries 00:01 */
166		if (len == 0 && *p == '\0') {
167			errno = EINVAL;
168			return 0;
169		}
170		/* Ensure that next data is EOL or a seperator with data */
171		if (!(*p == '\0' || (*p == ':' && *(p + 1) != '\0'))) {
172			errno = EINVAL;
173			return 0;
174		}
175		if (*p)
176			p++;
177		if (bp)
178			*bp++ = (unsigned char)strtol(c, NULL, 16);
179		len++;
180	}
181	return len;
182}
183
184struct interface *
185init_interface(const char *ifname)
186{
187	struct ifreq ifr;
188	struct interface *iface = NULL;
189
190	memset(&ifr, 0, sizeof(ifr));
191	strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
192	if (ioctl(socket_afnet, SIOCGIFFLAGS, &ifr) == -1)
193		goto eexit;
194
195	iface = xzalloc(sizeof(*iface));
196	strlcpy(iface->name, ifname, sizeof(iface->name));
197	iface->flags = ifr.ifr_flags;
198	/* We reserve the 100 range for virtual interfaces, if and when
199	 * we can work them out. */
200	iface->metric = 200 + if_nametoindex(iface->name);
201	if (getifssid(ifname, iface->ssid) != -1) {
202		iface->wireless = 1;
203		iface->metric += 100;
204	}
205
206	if (ioctl(socket_afnet, SIOCGIFMTU, &ifr) == -1)
207		goto eexit;
208	/* Ensure that the MTU is big enough for DHCP */
209	if (ifr.ifr_mtu < MTU_MIN) {
210		ifr.ifr_mtu = MTU_MIN;
211		strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
212		if (ioctl(socket_afnet, SIOCSIFMTU, &ifr) == -1)
213			goto eexit;
214	}
215
216	snprintf(iface->leasefile, sizeof(iface->leasefile),
217	    LEASEFILE, ifname);
218	/* 0 is a valid fd, so init to -1 */
219	iface->raw_fd = -1;
220	iface->udp_fd = -1;
221	iface->arp_fd = -1;
222	goto exit;
223
224eexit:
225	free(iface);
226	iface = NULL;
227exit:
228	return iface;
229}
230
231void
232free_interface(struct interface *iface)
233{
234	if (!iface)
235		return;
236	if (iface->state) {
237		free_options(iface->state->options);
238		free(iface->state->old);
239		free(iface->state->new);
240		free(iface->state->offer);
241		free(iface->state);
242	}
243	free(iface->clientid);
244	free(iface);
245}
246
247int
248carrier_status(struct interface *iface)
249{
250	int ret;
251	struct ifreq ifr;
252#ifdef SIOCGIFMEDIA
253	struct ifmediareq ifmr;
254#endif
255#ifdef __linux__
256	char *p;
257#endif
258
259	memset(&ifr, 0, sizeof(ifr));
260	strlcpy(ifr.ifr_name, iface->name, sizeof(ifr.ifr_name));
261#ifdef __linux__
262	/* We can only test the real interface up */
263	if ((p = strchr(ifr.ifr_name, ':')))
264		*p = '\0';
265#endif
266
267	if (ioctl(socket_afnet, SIOCGIFFLAGS, &ifr) == -1)
268		return -1;
269	iface->flags = ifr.ifr_flags;
270
271	ret = -1;
272#ifdef SIOCGIFMEDIA
273	memset(&ifmr, 0, sizeof(ifmr));
274	strlcpy(ifmr.ifm_name, iface->name, sizeof(ifmr.ifm_name));
275	if (ioctl(socket_afnet, SIOCGIFMEDIA, &ifmr) != -1 &&
276	    ifmr.ifm_status & IFM_AVALID)
277		ret = (ifmr.ifm_status & IFM_ACTIVE) ? 1 : 0;
278#endif
279	if (ret == -1)
280		ret = (ifr.ifr_flags & IFF_RUNNING) ? 1 : 0;
281	return ret;
282}
283
284int
285up_interface(struct interface *iface)
286{
287	struct ifreq ifr;
288	int retval = -1;
289#ifdef __linux__
290	char *p;
291#endif
292
293	memset(&ifr, 0, sizeof(ifr));
294	strlcpy(ifr.ifr_name, iface->name, sizeof(ifr.ifr_name));
295#ifdef __linux__
296	/* We can only bring the real interface up */
297	if ((p = strchr(ifr.ifr_name, ':')))
298		*p = '\0';
299#endif
300	if (ioctl(socket_afnet, SIOCGIFFLAGS, &ifr) == 0) {
301		if ((ifr.ifr_flags & IFF_UP))
302			retval = 0;
303		else {
304			ifr.ifr_flags |= IFF_UP;
305			if (ioctl(socket_afnet, SIOCSIFFLAGS, &ifr) == 0)
306				retval = 0;
307		}
308		iface->flags = ifr.ifr_flags;
309	}
310	return retval;
311}
312
313struct interface *
314discover_interfaces(int argc, char * const *argv)
315{
316	struct ifaddrs *ifaddrs, *ifa;
317	char *p;
318	int i;
319	struct interface *ifp, *ifs, *ifl;
320#ifdef __linux__
321	char ifn[IF_NAMESIZE];
322#endif
323#ifdef AF_LINK
324	const struct sockaddr_dl *sdl;
325#ifdef IFLR_ACTIVE
326	struct if_laddrreq iflr;
327	int socket_aflink;
328
329	socket_aflink = socket(AF_LINK, SOCK_DGRAM, 0);
330	if (socket_aflink == -1)
331		return NULL;
332	memset(&iflr, 0, sizeof(iflr));
333#endif
334#elif AF_PACKET
335	const struct sockaddr_ll *sll;
336#endif
337
338	if (getifaddrs(&ifaddrs) == -1)
339		return NULL;
340
341	ifs = ifl = NULL;
342	for (ifa = ifaddrs; ifa; ifa = ifa->ifa_next) {
343		if (ifa->ifa_addr != NULL) {
344#ifdef AF_LINK
345			if (ifa->ifa_addr->sa_family != AF_LINK)
346				continue;
347#elif AF_PACKET
348			if (ifa->ifa_addr->sa_family != AF_PACKET)
349				continue;
350#endif
351		}
352
353		/* It's possible for an interface to have >1 AF_LINK.
354		 * For our purposes, we use the first one. */
355		for (ifp = ifs; ifp; ifp = ifp->next)
356			if (strcmp(ifp->name, ifa->ifa_name) == 0)
357				break;
358		if (ifp)
359			continue;
360		if (argc > 0) {
361			for (i = 0; i < argc; i++) {
362#ifdef __linux__
363				/* Check the real interface name */
364				strlcpy(ifn, argv[i], sizeof(ifn));
365				p = strchr(ifn, ':');
366				if (p)
367					*p = '\0';
368				if (strcmp(ifn, ifa->ifa_name) == 0)
369					break;
370#else
371				if (strcmp(argv[i], ifa->ifa_name) == 0)
372					break;
373#endif
374			}
375			if (i == argc)
376				continue;
377			p = argv[i];
378		} else {
379			/* -1 means we're discovering against a specific
380			 * interface, but we still need the below rules
381			 * to apply. */
382			if (argc == -1 && strcmp(argv[0], ifa->ifa_name) != 0)
383				continue;
384			for (i = 0; i < ifdc; i++)
385				if (!fnmatch(ifdv[i], ifa->ifa_name, 0))
386					break;
387			if (i < ifdc)
388				continue;
389			for (i = 0; i < ifac; i++)
390				if (!fnmatch(ifav[i], ifa->ifa_name, 0))
391					break;
392			if (ifac && i == ifac)
393				continue;
394			p = ifa->ifa_name;
395		}
396		if ((ifp = init_interface(p)) == NULL)
397			continue;
398
399		/* Bring the interface up if not already */
400		if (!(ifp->flags & IFF_UP)
401#ifdef SIOCGIFMEDIA
402		    && carrier_status(ifp) != -1
403#endif
404		   )
405		{
406			if (up_interface(ifp) == 0)
407				options |= DHCPCD_WAITUP;
408			else
409				syslog(LOG_ERR, "%s: up_interface: %m", ifp->name);
410		}
411
412		/* Don't allow loopback unless explicit */
413		if (ifp->flags & IFF_LOOPBACK) {
414			if (argc == 0 && ifac == 0) {
415				free_interface(ifp);
416				continue;
417			}
418		} else if (ifa->ifa_addr != NULL) {
419#ifdef AF_LINK
420			sdl = (const struct sockaddr_dl *)(void *)ifa->ifa_addr;
421
422#ifdef IFLR_ACTIVE
423			/* We need to check for active address */
424			strlcpy(iflr.iflr_name, ifp->name,
425			    sizeof(iflr.iflr_name));
426			memcpy(&iflr.addr, ifa->ifa_addr,
427			    MIN(ifa->ifa_addr->sa_len, sizeof(iflr.addr)));
428			iflr.flags = IFLR_PREFIX;
429			iflr.prefixlen = sdl->sdl_alen * NBBY;
430			if (ioctl(socket_aflink, SIOCGLIFADDR, &iflr) == -1 ||
431			    !(iflr.flags & IFLR_ACTIVE))
432			{
433				free_interface(ifp);
434				continue;
435			}
436#endif
437
438			switch(sdl->sdl_type) {
439			case IFT_ETHER:
440				ifp->family = ARPHRD_ETHER;
441				break;
442			case IFT_IEEE1394:
443				ifp->family = ARPHRD_IEEE1394;
444				break;
445			}
446			ifp->hwlen = sdl->sdl_alen;
447#ifndef CLLADDR
448#  define CLLADDR(s) ((const char *)((s)->sdl_data + (s)->sdl_nlen))
449#endif
450			memcpy(ifp->hwaddr, CLLADDR(sdl), ifp->hwlen);
451#elif AF_PACKET
452			sll = (const struct sockaddr_ll *)(void *)ifa->ifa_addr;
453			ifp->family = sll->sll_hatype;
454			ifp->hwlen = sll->sll_halen;
455			if (ifp->hwlen != 0)
456				memcpy(ifp->hwaddr, sll->sll_addr, ifp->hwlen);
457#endif
458		}
459
460		/* We only work on ethernet by default */
461		if (!(ifp->flags & IFF_POINTOPOINT) &&
462		    ifp->family != ARPHRD_ETHER)
463		{
464			if (argc == 0 && ifac == 0) {
465				free_interface(ifp);
466				continue;
467			}
468			switch (ifp->family) {
469			case ARPHRD_IEEE1394: /* FALLTHROUGH */
470			case ARPHRD_INFINIBAND:
471				/* We don't warn for supported families */
472				break;
473			default:
474				syslog(LOG_WARNING,
475				    "%s: unknown hardware family", p);
476			}
477		}
478
479		/* Handle any platform init for the interface */
480		if (if_init(ifp) == -1) {
481			syslog(LOG_ERR, "%s: if_init: %m", p);
482			free_interface(ifp);
483			continue;
484		}
485
486		if (ifl)
487			ifl->next = ifp;
488		else
489			ifs = ifp;
490		ifl = ifp;
491	}
492	freeifaddrs(ifaddrs);
493
494#ifdef IFLR_ACTIVE
495	close(socket_aflink);
496#endif
497
498	return ifs;
499}
500
501int
502do_address(const char *ifname,
503    struct in_addr *addr, struct in_addr *net, struct in_addr *dst, int act)
504{
505	struct ifaddrs *ifaddrs, *ifa;
506	const struct sockaddr_in *a, *n, *d;
507	int retval;
508
509	if (getifaddrs(&ifaddrs) == -1)
510		return -1;
511
512	retval = 0;
513	for (ifa = ifaddrs; ifa; ifa = ifa->ifa_next) {
514		if (ifa->ifa_addr == NULL ||
515		    ifa->ifa_addr->sa_family != AF_INET ||
516		    strcmp(ifa->ifa_name, ifname) != 0)
517			continue;
518		a = (const struct sockaddr_in *)(void *)ifa->ifa_addr;
519		n = (const struct sockaddr_in *)(void *)ifa->ifa_netmask;
520		if (ifa->ifa_flags & IFF_POINTOPOINT)
521			d = (const struct sockaddr_in *)(void *)
522			    ifa->ifa_dstaddr;
523		else
524			d = NULL;
525		if (act == 1) {
526			addr->s_addr = a->sin_addr.s_addr;
527			net->s_addr = n->sin_addr.s_addr;
528			if (dst) {
529				if (ifa->ifa_flags & IFF_POINTOPOINT)
530					dst->s_addr = d->sin_addr.s_addr;
531				else
532					dst->s_addr = INADDR_ANY;
533			}
534			retval = 1;
535			break;
536		}
537		if (addr->s_addr == a->sin_addr.s_addr &&
538		    (net == NULL || net->s_addr == n->sin_addr.s_addr))
539		{
540			retval = 1;
541			break;
542		}
543	}
544	freeifaddrs(ifaddrs);
545	return retval;
546}
547
548int
549do_mtu(const char *ifname, short int mtu)
550{
551	struct ifreq ifr;
552	int r;
553
554	memset(&ifr, 0, sizeof(ifr));
555	strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
556	ifr.ifr_mtu = mtu;
557	r = ioctl(socket_afnet, mtu ? SIOCSIFMTU : SIOCGIFMTU, &ifr);
558	if (r == -1)
559		return -1;
560	return ifr.ifr_mtu;
561}
562
563void
564free_routes(struct rt *routes)
565{
566	struct rt *r;
567
568	while (routes) {
569		r = routes->next;
570		free(routes);
571		routes = r;
572	}
573}
574
575int
576open_udp_socket(struct interface *iface)
577{
578	int s;
579	struct sockaddr_in sin;
580	int n;
581#ifdef SO_BINDTODEVICE
582	struct ifreq ifr;
583	char *p;
584#endif
585
586	if ((s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
587		return -1;
588
589	n = 1;
590	if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)) == -1)
591		goto eexit;
592#ifdef SO_BINDTODEVICE
593	memset(&ifr, 0, sizeof(ifr));
594	strlcpy(ifr.ifr_name, iface->name, sizeof(ifr.ifr_name));
595	/* We can only bind to the real device */
596	p = strchr(ifr.ifr_name, ':');
597	if (p)
598		*p = '\0';
599	if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, &ifr,
600		sizeof(ifr)) == -1)
601		goto eexit;
602#endif
603	/* As we don't use this socket for receiving, set the
604	 * receive buffer to 1 */
605	n = 1;
606	if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n)) == -1)
607		goto eexit;
608	memset(&sin, 0, sizeof(sin));
609	sin.sin_family = AF_INET;
610	sin.sin_port = htons(DHCP_CLIENT_PORT);
611	sin.sin_addr.s_addr = iface->addr.s_addr;
612	if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) == -1)
613		goto eexit;
614
615	iface->udp_fd = s;
616	set_cloexec(s);
617	return 0;
618
619eexit:
620	close(s);
621	return -1;
622}
623
624ssize_t
625send_packet(const struct interface *iface, struct in_addr to,
626    const uint8_t *data, ssize_t len)
627{
628	struct sockaddr_in sin;
629
630	memset(&sin, 0, sizeof(sin));
631	sin.sin_family = AF_INET;
632	sin.sin_addr.s_addr = to.s_addr;
633	sin.sin_port = htons(DHCP_SERVER_PORT);
634	return sendto(iface->udp_fd, data, len, 0,
635	    (struct sockaddr *)&sin, sizeof(sin));
636}
637
638struct udp_dhcp_packet
639{
640	struct ip ip;
641	struct udphdr udp;
642	struct dhcp_message dhcp;
643};
644const size_t udp_dhcp_len = sizeof(struct udp_dhcp_packet);
645
646static uint16_t
647checksum(const void *data, uint16_t len)
648{
649	const uint8_t *addr = data;
650	uint32_t sum = 0;
651
652	while (len > 1) {
653		sum += addr[0] * 256 + addr[1];
654		addr += 2;
655		len -= 2;
656	}
657
658	if (len == 1)
659		sum += *addr * 256;
660
661	sum = (sum >> 16) + (sum & 0xffff);
662	sum += (sum >> 16);
663
664	sum = htons(sum);
665
666	return ~sum;
667}
668
669ssize_t
670make_udp_packet(uint8_t **packet, const uint8_t *data, size_t length,
671    struct in_addr source, struct in_addr dest)
672{
673	struct udp_dhcp_packet *udpp;
674	struct ip *ip;
675	struct udphdr *udp;
676
677	udpp = xzalloc(sizeof(*udpp));
678	ip = &udpp->ip;
679	udp = &udpp->udp;
680
681	/* OK, this is important :)
682	 * We copy the data to our packet and then create a small part of the
683	 * ip structure and an invalid ip_len (basically udp length).
684	 * We then fill the udp structure and put the checksum
685	 * of the whole packet into the udp checksum.
686	 * Finally we complete the ip structure and ip checksum.
687	 * If we don't do the ordering like so then the udp checksum will be
688	 * broken, so find another way of doing it! */
689
690	memcpy(&udpp->dhcp, data, length);
691
692	ip->ip_p = IPPROTO_UDP;
693	ip->ip_src.s_addr = source.s_addr;
694	if (dest.s_addr == 0)
695		ip->ip_dst.s_addr = INADDR_BROADCAST;
696	else
697		ip->ip_dst.s_addr = dest.s_addr;
698
699	udp->uh_sport = htons(DHCP_CLIENT_PORT);
700	udp->uh_dport = htons(DHCP_SERVER_PORT);
701	udp->uh_ulen = htons(sizeof(*udp) + length);
702	ip->ip_len = udp->uh_ulen;
703	udp->uh_sum = checksum(udpp, sizeof(*udpp));
704
705	ip->ip_v = IPVERSION;
706	ip->ip_hl = sizeof(*ip) >> 2;
707	ip->ip_id = arc4random() & UINT16_MAX;
708	ip->ip_ttl = IPDEFTTL;
709	ip->ip_len = htons(sizeof(*ip) + sizeof(*udp) + length);
710	ip->ip_sum = checksum(ip, sizeof(*ip));
711
712	*packet = (uint8_t *)udpp;
713	return sizeof(*ip) + sizeof(*udp) + length;
714}
715
716ssize_t
717get_udp_data(const uint8_t **data, const uint8_t *udp)
718{
719	struct udp_dhcp_packet packet;
720
721	memcpy(&packet, udp, sizeof(packet));
722	*data = udp + offsetof(struct udp_dhcp_packet, dhcp);
723	return ntohs(packet.ip.ip_len) -
724	    sizeof(packet.ip) -
725	    sizeof(packet.udp);
726}
727
728int
729valid_udp_packet(const uint8_t *data, size_t data_len, struct in_addr *from)
730{
731	struct udp_dhcp_packet packet;
732	uint16_t bytes, udpsum;
733
734	if (data_len < sizeof(packet.ip)) {
735		if (from)
736			from->s_addr = INADDR_ANY;
737		errno = EINVAL;
738		return -1;
739	}
740	memcpy(&packet, data, MIN(data_len, sizeof(packet)));
741	if (from)
742		from->s_addr = packet.ip.ip_src.s_addr;
743	if (data_len > sizeof(packet)) {
744		errno = EINVAL;
745		return -1;
746	}
747	if (checksum(&packet.ip, sizeof(packet.ip)) != 0) {
748		errno = EINVAL;
749		return -1;
750	}
751
752	bytes = ntohs(packet.ip.ip_len);
753	if (data_len < bytes) {
754		errno = EINVAL;
755		return -1;
756	}
757	udpsum = packet.udp.uh_sum;
758	packet.udp.uh_sum = 0;
759	packet.ip.ip_hl = 0;
760	packet.ip.ip_v = 0;
761	packet.ip.ip_tos = 0;
762	packet.ip.ip_len = packet.udp.uh_ulen;
763	packet.ip.ip_id = 0;
764	packet.ip.ip_off = 0;
765	packet.ip.ip_ttl = 0;
766	packet.ip.ip_sum = 0;
767	if (udpsum && checksum(&packet, bytes) != udpsum) {
768		errno = EINVAL;
769		return -1;
770	}
771
772	return 0;
773}
774