xref: /openbsd-src/usr.sbin/dhcrelay6/dispatch.c (revision 9a58347058de119d8fb16fe4b1957031059669e9)
1*9a583470Sgerhard /*	$OpenBSD: dispatch.c,v 1.3 2023/07/14 07:09:00 gerhard Exp $	*/
29cbab583Srzalamena 
39cbab583Srzalamena /*
49cbab583Srzalamena  * Copyright 2004 Henning Brauer <henning@openbsd.org>
59cbab583Srzalamena  * Copyright (c) 1995, 1996, 1997, 1998, 1999
69cbab583Srzalamena  * The Internet Software Consortium.   All rights reserved.
79cbab583Srzalamena  *
89cbab583Srzalamena  * Redistribution and use in source and binary forms, with or without
99cbab583Srzalamena  * modification, are permitted provided that the following conditions
109cbab583Srzalamena  * are met:
119cbab583Srzalamena  *
129cbab583Srzalamena  * 1. Redistributions of source code must retain the above copyright
139cbab583Srzalamena  *    notice, this list of conditions and the following disclaimer.
149cbab583Srzalamena  * 2. Redistributions in binary form must reproduce the above copyright
159cbab583Srzalamena  *    notice, this list of conditions and the following disclaimer in the
169cbab583Srzalamena  *    documentation and/or other materials provided with the distribution.
179cbab583Srzalamena  * 3. Neither the name of The Internet Software Consortium nor the names
189cbab583Srzalamena  *    of its contributors may be used to endorse or promote products derived
199cbab583Srzalamena  *    from this software without specific prior written permission.
209cbab583Srzalamena  *
219cbab583Srzalamena  * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
229cbab583Srzalamena  * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
239cbab583Srzalamena  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
249cbab583Srzalamena  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
259cbab583Srzalamena  * DISCLAIMED.  IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
269cbab583Srzalamena  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
279cbab583Srzalamena  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
289cbab583Srzalamena  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
299cbab583Srzalamena  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
309cbab583Srzalamena  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
319cbab583Srzalamena  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
329cbab583Srzalamena  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
339cbab583Srzalamena  * SUCH DAMAGE.
349cbab583Srzalamena  *
359cbab583Srzalamena  * This software has been written for the Internet Software Consortium
369cbab583Srzalamena  * by Ted Lemon <mellon@fugue.com> in cooperation with Vixie
379cbab583Srzalamena  * Enterprises.  To learn more about the Internet Software Consortium,
389cbab583Srzalamena  * see ``http://www.vix.com/isc''.  To learn more about Vixie
399cbab583Srzalamena  * Enterprises, see ``http://www.vix.com''.
409cbab583Srzalamena  */
419cbab583Srzalamena 
429cbab583Srzalamena #include <sys/types.h>
439cbab583Srzalamena #include <sys/ioctl.h>
449cbab583Srzalamena #include <sys/socket.h>
459cbab583Srzalamena 
469cbab583Srzalamena #include <net/if.h>
479cbab583Srzalamena #include <net/if_dl.h>
489cbab583Srzalamena #include <net/if_media.h>
499cbab583Srzalamena #include <net/if_types.h>
509cbab583Srzalamena 
519cbab583Srzalamena #include <netinet/in.h>
529cbab583Srzalamena #include <netinet/if_ether.h>
539cbab583Srzalamena 
549cbab583Srzalamena #include <errno.h>
559cbab583Srzalamena #include <ifaddrs.h>
569cbab583Srzalamena #include <limits.h>
579cbab583Srzalamena #include <poll.h>
589cbab583Srzalamena #include <stdlib.h>
599cbab583Srzalamena #include <string.h>
609cbab583Srzalamena #include <syslog.h>
619cbab583Srzalamena #include <time.h>
629cbab583Srzalamena #include <unistd.h>
639cbab583Srzalamena 
649cbab583Srzalamena #include "dhcp.h"
659cbab583Srzalamena #include "dhcpd.h"
669cbab583Srzalamena #include "log.h"
679cbab583Srzalamena 
689cbab583Srzalamena /*
699cbab583Srzalamena  * Macros implementation used to generate link-local addresses. This
709cbab583Srzalamena  * code was copied from: sys/netinet6/in6_ifattach.c.
719cbab583Srzalamena  */
729cbab583Srzalamena #define EUI64_UBIT		0x02
739cbab583Srzalamena #define EUI64_TO_IFID(in6) \
749cbab583Srzalamena 	do { (in6)->s6_addr[8] ^= EUI64_UBIT; } while (0)
759cbab583Srzalamena 
769cbab583Srzalamena struct protocol *protocols;
779cbab583Srzalamena struct timeout *timeouts;
789cbab583Srzalamena static struct timeout *free_timeouts;
799cbab583Srzalamena static int interfaces_invalidated;
809cbab583Srzalamena 
819cbab583Srzalamena void (*bootp_packet_handler)(struct interface_info *,
829cbab583Srzalamena     void *, size_t, struct packet_ctx *);
839cbab583Srzalamena 
849cbab583Srzalamena static int interface_status(struct interface_info *ifinfo);
859cbab583Srzalamena 
869cbab583Srzalamena struct interface_info *
iflist_getbyindex(unsigned int index)879cbab583Srzalamena iflist_getbyindex(unsigned int index)
889cbab583Srzalamena {
899cbab583Srzalamena 	struct interface_info	*intf;
909cbab583Srzalamena 
919cbab583Srzalamena 	TAILQ_FOREACH(intf, &intflist, entry) {
929cbab583Srzalamena 		if (intf->index != index)
939cbab583Srzalamena 			continue;
949cbab583Srzalamena 
959cbab583Srzalamena 		return intf;
969cbab583Srzalamena 	}
979cbab583Srzalamena 
989cbab583Srzalamena 	return NULL;
999cbab583Srzalamena }
1009cbab583Srzalamena 
1019cbab583Srzalamena struct interface_info *
iflist_getbyname(const char * name)1029cbab583Srzalamena iflist_getbyname(const char *name)
1039cbab583Srzalamena {
1049cbab583Srzalamena 	struct interface_info	*intf;
1059cbab583Srzalamena 
1069cbab583Srzalamena 	TAILQ_FOREACH(intf, &intflist, entry) {
1079cbab583Srzalamena 		if (strcmp(intf->name, name) != 0)
1089cbab583Srzalamena 			continue;
1099cbab583Srzalamena 
1109cbab583Srzalamena 		return intf;
1119cbab583Srzalamena 	}
1129cbab583Srzalamena 
1139cbab583Srzalamena 	return NULL;
1149cbab583Srzalamena }
1159cbab583Srzalamena 
1169cbab583Srzalamena struct interface_info *
iflist_getbyaddr6(struct in6_addr * addr)1179cbab583Srzalamena iflist_getbyaddr6(struct in6_addr *addr)
1189cbab583Srzalamena {
1199cbab583Srzalamena 	struct interface_info	*intf;
1209cbab583Srzalamena 
1219cbab583Srzalamena 	TAILQ_FOREACH(intf, &intflist, entry) {
1229cbab583Srzalamena 		/* Look for link-layer address. */
1239cbab583Srzalamena 		if (memcmp(&intf->linklocal, addr, sizeof(*addr)) == 0)
1249cbab583Srzalamena 			return intf;
1259cbab583Srzalamena 	}
1269cbab583Srzalamena 
1279cbab583Srzalamena 	return NULL;
1289cbab583Srzalamena }
1299cbab583Srzalamena 
1309cbab583Srzalamena void
setup_iflist(void)1319cbab583Srzalamena setup_iflist(void)
1329cbab583Srzalamena {
1339cbab583Srzalamena 	struct interface_info		*intf;
1349cbab583Srzalamena 	struct sockaddr_dl		*sdl;
1359cbab583Srzalamena 	struct ifaddrs			*ifap, *ifa;
1369cbab583Srzalamena 	struct if_data			*ifi;
1379cbab583Srzalamena 	struct sockaddr_in		*sin;
1389cbab583Srzalamena 	struct sockaddr_in6		*sin6;
1399cbab583Srzalamena 
1409cbab583Srzalamena 	TAILQ_INIT(&intflist);
1419cbab583Srzalamena 	if (getifaddrs(&ifap))
1429cbab583Srzalamena 		fatalx("getifaddrs failed");
1439cbab583Srzalamena 
1449cbab583Srzalamena 	for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
1459cbab583Srzalamena 		if ((ifa->ifa_flags & IFF_LOOPBACK) ||
1469cbab583Srzalamena 		    (ifa->ifa_flags & IFF_POINTOPOINT))
1479cbab583Srzalamena 			continue;
1489cbab583Srzalamena 
1499cbab583Srzalamena 		/* Find interface or create it. */
1509cbab583Srzalamena 		intf = iflist_getbyname(ifa->ifa_name);
1519cbab583Srzalamena 		if (intf == NULL) {
1529cbab583Srzalamena 			intf = calloc(1, sizeof(*intf));
1539cbab583Srzalamena 			if (intf == NULL)
1549cbab583Srzalamena 				fatal("calloc");
1559cbab583Srzalamena 
1569cbab583Srzalamena 			strlcpy(intf->name, ifa->ifa_name,
1579cbab583Srzalamena 			    sizeof(intf->name));
1589cbab583Srzalamena 			TAILQ_INSERT_HEAD(&intflist, intf, entry);
1599cbab583Srzalamena 		}
1609cbab583Srzalamena 
1619cbab583Srzalamena 		/* Signal disabled interface. */
1629cbab583Srzalamena 		if ((ifa->ifa_flags & IFF_UP) == 0)
1639cbab583Srzalamena 			intf->dead = 1;
1649cbab583Srzalamena 
1659cbab583Srzalamena 		if (ifa->ifa_addr->sa_family == AF_LINK) {
1669cbab583Srzalamena 			sdl = (struct sockaddr_dl *)ifa->ifa_addr;
1679cbab583Srzalamena 			ifi = (struct if_data *)ifa->ifa_data;
1689cbab583Srzalamena 
1699cbab583Srzalamena 			/* Skip non ethernet interfaces. */
1709cbab583Srzalamena 			if (ifi->ifi_type != IFT_ETHER &&
171*9a583470Sgerhard 			    ifi->ifi_type != IFT_ENC &&
172*9a583470Sgerhard 			    ifi->ifi_type != IFT_CARP) {
1739cbab583Srzalamena 				TAILQ_REMOVE(&intflist, intf, entry);
1749cbab583Srzalamena 				free(intf);
1759cbab583Srzalamena 				continue;
1769cbab583Srzalamena 			}
1779cbab583Srzalamena 
1789cbab583Srzalamena 			intf->index = sdl->sdl_index;
1799cbab583Srzalamena 			intf->hw_address.hlen = sdl->sdl_alen;
1809cbab583Srzalamena 			memcpy(intf->hw_address.haddr,
1819cbab583Srzalamena 			    LLADDR(sdl), sdl->sdl_alen);
1829cbab583Srzalamena 		} else if (ifa->ifa_addr->sa_family == AF_INET) {
1839cbab583Srzalamena 			sin = (struct sockaddr_in *)ifa->ifa_addr;
1849cbab583Srzalamena 			if (sin->sin_addr.s_addr == htonl(INADDR_LOOPBACK) ||
1859cbab583Srzalamena 			    intf->primary_address.s_addr != INADDR_ANY)
1869cbab583Srzalamena 				continue;
1879cbab583Srzalamena 
1889cbab583Srzalamena 			intf->primary_address = sin->sin_addr;
1899cbab583Srzalamena 		} else if (ifa->ifa_addr->sa_family == AF_INET6) {
1909cbab583Srzalamena 			sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
1919cbab583Srzalamena 			if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
1929cbab583Srzalamena 				intf->linklocal = sin6->sin6_addr;
193276ba0c3Sclaudio #ifdef __KAME__
194276ba0c3Sclaudio 				/*
195276ba0c3Sclaudio 				 * Remove possible scope from address if
196276ba0c3Sclaudio 				 * link-local.
197276ba0c3Sclaudio 				 */
1989cbab583Srzalamena 				intf->linklocal.s6_addr[2] = 0;
1999cbab583Srzalamena 				intf->linklocal.s6_addr[3] = 0;
200276ba0c3Sclaudio #endif
2019cbab583Srzalamena 			} else
2029cbab583Srzalamena 				intf->gipv6 = 1;
2039cbab583Srzalamena 
2049cbab583Srzalamena 			/* At least one IPv6 address was found. */
2059cbab583Srzalamena 			intf->ipv6 = 1;
2069cbab583Srzalamena 		}
2079cbab583Srzalamena 	}
2089cbab583Srzalamena 
2099cbab583Srzalamena 	freeifaddrs(ifap);
2109cbab583Srzalamena 
2119cbab583Srzalamena 	/*
2129cbab583Srzalamena 	 * Generate link-local IPv6 address for interfaces without it.
2139cbab583Srzalamena 	 *
2149cbab583Srzalamena 	 * For IPv6 DHCP Relay it doesn't matter what is used for
2159cbab583Srzalamena 	 * link-addr field, so let's generate an address that won't
2169cbab583Srzalamena 	 * change during execution so we can always find the interface
2179cbab583Srzalamena 	 * to relay packets back. This is only used for layer 2 relaying
2189cbab583Srzalamena 	 * when the interface might not have an address.
2199cbab583Srzalamena 	 */
2209cbab583Srzalamena 	TAILQ_FOREACH(intf, &intflist, entry) {
2219cbab583Srzalamena 		if (memcmp(&intf->linklocal, &in6addr_any,
2229cbab583Srzalamena 		    sizeof(in6addr_any)) != 0)
2239cbab583Srzalamena 			continue;
2249cbab583Srzalamena 
2259cbab583Srzalamena 		intf->linklocal.s6_addr[0] = 0xfe;
2269cbab583Srzalamena 		intf->linklocal.s6_addr[1] = 0x80;
2279cbab583Srzalamena 		intf->linklocal.s6_addr[8] = intf->hw_address.haddr[0];
2289cbab583Srzalamena 		intf->linklocal.s6_addr[9] = intf->hw_address.haddr[1];
2299cbab583Srzalamena 		intf->linklocal.s6_addr[10] = intf->hw_address.haddr[2];
2309cbab583Srzalamena 		intf->linklocal.s6_addr[11] = 0xff;
2319cbab583Srzalamena 		intf->linklocal.s6_addr[12] = 0xfe;
2329cbab583Srzalamena 		intf->linklocal.s6_addr[13] = intf->hw_address.haddr[3];
2339cbab583Srzalamena 		intf->linklocal.s6_addr[14] = intf->hw_address.haddr[4];
2349cbab583Srzalamena 		intf->linklocal.s6_addr[15] = intf->hw_address.haddr[5];
2359cbab583Srzalamena 		EUI64_TO_IFID(&intf->linklocal);
2369cbab583Srzalamena 	}
2379cbab583Srzalamena }
2389cbab583Srzalamena 
2399cbab583Srzalamena struct interface_info *
register_interface(const char * ifname,void (* handler)(struct protocol *))2409cbab583Srzalamena register_interface(const char *ifname, void (*handler)(struct protocol *))
2419cbab583Srzalamena {
2429cbab583Srzalamena 	struct interface_info		*intf;
2439cbab583Srzalamena 
2449cbab583Srzalamena 	if ((intf = iflist_getbyname(ifname)) == NULL)
2459cbab583Srzalamena 		return NULL;
2469cbab583Srzalamena 
2479cbab583Srzalamena 	/* Don't register disabled interfaces. */
2489cbab583Srzalamena 	if (intf->dead)
2499cbab583Srzalamena 		return NULL;
2509cbab583Srzalamena 
2519cbab583Srzalamena 	/* Check if we already registered the interface. */
2529cbab583Srzalamena 	if (intf->ifr.ifr_name[0] != 0)
2539cbab583Srzalamena 		return intf;
2549cbab583Srzalamena 
2559cbab583Srzalamena 	if (strlcpy(intf->ifr.ifr_name, ifname,
2569cbab583Srzalamena 	    sizeof(intf->ifr.ifr_name)) >= sizeof(intf->ifr.ifr_name))
2579cbab583Srzalamena 		fatalx("interface name '%s' too long", ifname);
2589cbab583Srzalamena 
2599cbab583Srzalamena 	if_register_receive(intf);
2609cbab583Srzalamena 	if_register_send(intf);
2619cbab583Srzalamena 	add_protocol(intf->name, intf->rfdesc, handler, intf);
2629cbab583Srzalamena 
2639cbab583Srzalamena 	return intf;
2649cbab583Srzalamena }
2659cbab583Srzalamena 
2669cbab583Srzalamena /*
2679cbab583Srzalamena  * Wait for packets to come in using poll().  When a packet comes in,
2689cbab583Srzalamena  * call receive_packet to receive the packet and possibly strip hardware
2699cbab583Srzalamena  * addressing information from it, and then call through the
2709cbab583Srzalamena  * bootp_packet_handler hook to try to do something with it.
2719cbab583Srzalamena  */
2729cbab583Srzalamena void
dispatch(void)2739cbab583Srzalamena dispatch(void)
2749cbab583Srzalamena {
2759cbab583Srzalamena 	int count, i, to_msec, nfds = 0;
2769cbab583Srzalamena 	struct protocol *l;
2779cbab583Srzalamena 	struct pollfd *fds;
2789cbab583Srzalamena 	time_t howlong;
2799cbab583Srzalamena 
2809cbab583Srzalamena 	nfds = 0;
2819cbab583Srzalamena 	for (l = protocols; l; l = l->next)
2829cbab583Srzalamena 		nfds++;
2839cbab583Srzalamena 
2849cbab583Srzalamena 	fds = calloc(nfds, sizeof(struct pollfd));
2859cbab583Srzalamena 	if (fds == NULL)
2869cbab583Srzalamena 		fatalx("Can't allocate poll structures.");
2879cbab583Srzalamena 
2889cbab583Srzalamena 	do {
2899cbab583Srzalamena 		/*
2909cbab583Srzalamena 		 * Call any expired timeouts, and then if there's still
2919cbab583Srzalamena 		 * a timeout registered, time out the select call then.
2929cbab583Srzalamena 		 */
2939cbab583Srzalamena another:
2949cbab583Srzalamena 		if (timeouts) {
2959cbab583Srzalamena 			if (timeouts->when <= cur_time) {
2969cbab583Srzalamena 				struct timeout *t = timeouts;
2979cbab583Srzalamena 
2989cbab583Srzalamena 				timeouts = timeouts->next;
2999cbab583Srzalamena 				(*(t->func))(t->what);
3009cbab583Srzalamena 				t->next = free_timeouts;
3019cbab583Srzalamena 				free_timeouts = t;
3029cbab583Srzalamena 				goto another;
3039cbab583Srzalamena 			}
3049cbab583Srzalamena 
3059cbab583Srzalamena 			/*
3069cbab583Srzalamena 			 * Figure timeout in milliseconds, and check for
3079cbab583Srzalamena 			 * potential overflow, so we can cram into an
3089cbab583Srzalamena 			 * int for poll, while not polling with a
3099cbab583Srzalamena 			 * negative timeout and blocking indefinitely.
3109cbab583Srzalamena 			 */
3119cbab583Srzalamena 			howlong = timeouts->when - cur_time;
3129cbab583Srzalamena 			if (howlong > INT_MAX / 1000)
3139cbab583Srzalamena 				howlong = INT_MAX / 1000;
3149cbab583Srzalamena 			to_msec = howlong * 1000;
3159cbab583Srzalamena 		} else
3169cbab583Srzalamena 			to_msec = -1;
3179cbab583Srzalamena 
3189cbab583Srzalamena 		/* Set up the descriptors to be polled. */
3199cbab583Srzalamena 		i = 0;
3209cbab583Srzalamena 
3219cbab583Srzalamena 		for (l = protocols; l; l = l->next) {
3229cbab583Srzalamena 			struct interface_info *ip = l->local;
3239cbab583Srzalamena 
3249cbab583Srzalamena 			if (ip && (l->handler != got_one || !ip->dead)) {
3259cbab583Srzalamena 				fds[i].fd = l->fd;
3269cbab583Srzalamena 				fds[i].events = POLLIN;
3279cbab583Srzalamena 				fds[i].revents = 0;
3289cbab583Srzalamena 				i++;
3299cbab583Srzalamena 			}
3309cbab583Srzalamena 		}
3319cbab583Srzalamena 
3329cbab583Srzalamena 		if (i == 0)
3339cbab583Srzalamena 			fatalx("No live interfaces to poll on - exiting.");
3349cbab583Srzalamena 
3359cbab583Srzalamena 		/* Wait for a packet or a timeout... XXX */
3369cbab583Srzalamena 		count = poll(fds, nfds, to_msec);
3379cbab583Srzalamena 
3389cbab583Srzalamena 		/* Not likely to be transitory... */
3399cbab583Srzalamena 		if (count == -1) {
3409cbab583Srzalamena 			if (errno == EAGAIN || errno == EINTR) {
3419cbab583Srzalamena 				time(&cur_time);
3429cbab583Srzalamena 				continue;
3439cbab583Srzalamena 			}
3449cbab583Srzalamena 			else
3459cbab583Srzalamena 				fatal("poll");
3469cbab583Srzalamena 		}
3479cbab583Srzalamena 
3489cbab583Srzalamena 		/* Get the current time... */
3499cbab583Srzalamena 		time(&cur_time);
3509cbab583Srzalamena 
3519cbab583Srzalamena 		i = 0;
3529cbab583Srzalamena 		for (l = protocols; l; l = l->next) {
3539cbab583Srzalamena 			struct interface_info *ip = l->local;
3549cbab583Srzalamena 
3559cbab583Srzalamena 			if ((fds[i].revents & (POLLIN | POLLHUP))) {
3569cbab583Srzalamena 				fds[i].revents = 0;
3579cbab583Srzalamena 				if (ip && (l->handler != got_one ||
3589cbab583Srzalamena 				    !ip->dead))
3599cbab583Srzalamena 					(*(l->handler))(l);
3609cbab583Srzalamena 				if (interfaces_invalidated)
3619cbab583Srzalamena 					break;
3629cbab583Srzalamena 			}
3639cbab583Srzalamena 			i++;
3649cbab583Srzalamena 		}
3659cbab583Srzalamena 		interfaces_invalidated = 0;
3669cbab583Srzalamena 	} while (1);
3679cbab583Srzalamena }
3689cbab583Srzalamena 
3699cbab583Srzalamena 
3709cbab583Srzalamena void
got_one(struct protocol * l)3719cbab583Srzalamena got_one(struct protocol *l)
3729cbab583Srzalamena {
3739cbab583Srzalamena 	struct packet_ctx pc;
3749cbab583Srzalamena 	ssize_t result;
3759cbab583Srzalamena 	uint8_t buf[4096];
3769cbab583Srzalamena 	struct interface_info *ip = l->local;
3779cbab583Srzalamena 
3789cbab583Srzalamena 	memset(&pc, 0, sizeof(pc));
3799cbab583Srzalamena 
3809cbab583Srzalamena 	if ((result = receive_packet(ip, buf, sizeof(buf), &pc)) == -1) {
3819cbab583Srzalamena 		log_warn("receive_packet failed on %s", ip->name);
3829cbab583Srzalamena 		ip->errors++;
3839cbab583Srzalamena 		if ((!interface_status(ip)) ||
3849cbab583Srzalamena 		    (ip->noifmedia && ip->errors > 20)) {
3859cbab583Srzalamena 			/* our interface has gone away. */
3869cbab583Srzalamena 			log_warnx("Interface %s no longer appears valid.",
3879cbab583Srzalamena 			    ip->name);
3889cbab583Srzalamena 			ip->dead = 1;
3899cbab583Srzalamena 			interfaces_invalidated = 1;
3909cbab583Srzalamena 			close(l->fd);
3919cbab583Srzalamena 			remove_protocol(l);
3929cbab583Srzalamena 			free(ip);
3939cbab583Srzalamena 		}
3949cbab583Srzalamena 		return;
3959cbab583Srzalamena 	}
3969cbab583Srzalamena 	if (result == 0)
3979cbab583Srzalamena 		return;
3989cbab583Srzalamena 
3999cbab583Srzalamena 	if (bootp_packet_handler)
4009cbab583Srzalamena 		(*bootp_packet_handler)(ip, buf, result, &pc);
4019cbab583Srzalamena }
4029cbab583Srzalamena 
4039cbab583Srzalamena int
interface_status(struct interface_info * ifinfo)4049cbab583Srzalamena interface_status(struct interface_info *ifinfo)
4059cbab583Srzalamena {
4069cbab583Srzalamena 	char *ifname = ifinfo->name;
4079cbab583Srzalamena 	int ifsock = ifinfo->rfdesc;
4089cbab583Srzalamena 	struct ifreq ifr;
4099cbab583Srzalamena 	struct ifmediareq ifmr;
4109cbab583Srzalamena 
4119cbab583Srzalamena 	/* get interface flags */
4129cbab583Srzalamena 	memset(&ifr, 0, sizeof(ifr));
4139cbab583Srzalamena 	strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
4149cbab583Srzalamena 	if (ioctl(ifsock, SIOCGIFFLAGS, &ifr) == -1) {
4159cbab583Srzalamena 		log_warn("ioctl(SIOCGIFFLAGS) on %s", ifname);
4169cbab583Srzalamena 		goto inactive;
4179cbab583Srzalamena 	}
4189cbab583Srzalamena 	/*
4199cbab583Srzalamena 	 * if one of UP and RUNNING flags is dropped,
4209cbab583Srzalamena 	 * the interface is not active.
4219cbab583Srzalamena 	 */
4229cbab583Srzalamena 	if ((ifr.ifr_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
4239cbab583Srzalamena 		goto inactive;
4249cbab583Srzalamena 	}
4259cbab583Srzalamena 	/* Next, check carrier on the interface, if possible */
4269cbab583Srzalamena 	if (ifinfo->noifmedia)
4279cbab583Srzalamena 		goto active;
4289cbab583Srzalamena 	memset(&ifmr, 0, sizeof(ifmr));
4299cbab583Srzalamena 	strlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
4309cbab583Srzalamena 	if (ioctl(ifsock, SIOCGIFMEDIA, (caddr_t)&ifmr) == -1) {
4319cbab583Srzalamena 		if (errno != EINVAL) {
4329cbab583Srzalamena 			log_debug("ioctl(SIOCGIFMEDIA) on %s", ifname);
4339cbab583Srzalamena 			ifinfo->noifmedia = 1;
4349cbab583Srzalamena 			goto active;
4359cbab583Srzalamena 		}
4369cbab583Srzalamena 		/*
4379cbab583Srzalamena 		 * EINVAL (or ENOTTY) simply means that the interface
4389cbab583Srzalamena 		 * does not support the SIOCGIFMEDIA ioctl. We regard it alive.
4399cbab583Srzalamena 		 */
4409cbab583Srzalamena 		ifinfo->noifmedia = 1;
4419cbab583Srzalamena 		goto active;
4429cbab583Srzalamena 	}
4439cbab583Srzalamena 	if (ifmr.ifm_status & IFM_AVALID) {
4449cbab583Srzalamena 		switch (ifmr.ifm_active & IFM_NMASK) {
4459cbab583Srzalamena 		case IFM_ETHER:
4469cbab583Srzalamena 			if (ifmr.ifm_status & IFM_ACTIVE)
4479cbab583Srzalamena 				goto active;
4489cbab583Srzalamena 			else
4499cbab583Srzalamena 				goto inactive;
4509cbab583Srzalamena 			break;
4519cbab583Srzalamena 		default:
4529cbab583Srzalamena 			goto inactive;
4539cbab583Srzalamena 		}
4549cbab583Srzalamena 	}
4559cbab583Srzalamena inactive:
4569cbab583Srzalamena 	return (0);
4579cbab583Srzalamena active:
4589cbab583Srzalamena 	return (1);
4599cbab583Srzalamena }
4609cbab583Srzalamena 
4619cbab583Srzalamena /* Add a protocol to the list of protocols... */
4629cbab583Srzalamena void
add_protocol(char * name,int fd,void (* handler)(struct protocol *),void * local)4639cbab583Srzalamena add_protocol(char *name, int fd, void (*handler)(struct protocol *),
4649cbab583Srzalamena     void *local)
4659cbab583Srzalamena {
4669cbab583Srzalamena 	struct protocol *p;
4679cbab583Srzalamena 
4689cbab583Srzalamena 	p = malloc(sizeof(*p));
4699cbab583Srzalamena 	if (!p)
4709cbab583Srzalamena 		fatalx("can't allocate protocol struct for %s", name);
4719cbab583Srzalamena 
4729cbab583Srzalamena 	p->fd = fd;
4739cbab583Srzalamena 	p->handler = handler;
4749cbab583Srzalamena 	p->local = local;
4759cbab583Srzalamena 	p->next = protocols;
4769cbab583Srzalamena 	protocols = p;
4779cbab583Srzalamena }
4789cbab583Srzalamena 
4799cbab583Srzalamena void
remove_protocol(struct protocol * proto)4809cbab583Srzalamena remove_protocol(struct protocol *proto)
4819cbab583Srzalamena {
4829cbab583Srzalamena 	struct protocol *p, *next, *prev;
4839cbab583Srzalamena 
4849cbab583Srzalamena 	prev = NULL;
4859cbab583Srzalamena 	for (p = protocols; p; p = next) {
4869cbab583Srzalamena 		next = p->next;
4879cbab583Srzalamena 		if (p == proto) {
4889cbab583Srzalamena 			if (prev)
4899cbab583Srzalamena 				prev->next = p->next;
4909cbab583Srzalamena 			else
4919cbab583Srzalamena 				protocols = p->next;
4929cbab583Srzalamena 			free(p);
4939cbab583Srzalamena 		}
4949cbab583Srzalamena 	}
4959cbab583Srzalamena }
496