diff options
author | Rafael Zalamena <rzalamena@cvs.openbsd.org> | 2017-03-15 14:31:50 +0000 |
---|---|---|
committer | Rafael Zalamena <rzalamena@cvs.openbsd.org> | 2017-03-15 14:31:50 +0000 |
commit | 5f408e08fd8bf86f6e77fa07e833048027c01cf4 (patch) | |
tree | 8de83373f5453b2743b1d2313783775663723194 | |
parent | 7a00f75f1939163102bf9679143f8225da5bedb0 (diff) |
Improve the interface and addresses discovery code and prepare to
receive IPv6 support.
ok reyk@
-rw-r--r-- | usr.sbin/dhcrelay/dhcpd.h | 23 | ||||
-rw-r--r-- | usr.sbin/dhcrelay/dhcrelay.c | 14 | ||||
-rw-r--r-- | usr.sbin/dhcrelay/dispatch.c | 180 |
3 files changed, 160 insertions, 57 deletions
diff --git a/usr.sbin/dhcrelay/dhcpd.h b/usr.sbin/dhcrelay/dhcpd.h index 556e81aad71..2e94d39dcb6 100644 --- a/usr.sbin/dhcrelay/dhcpd.h +++ b/usr.sbin/dhcrelay/dhcpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: dhcpd.h,v 1.19 2017/03/14 09:21:26 rzalamena Exp $ */ +/* $OpenBSD: dhcpd.h,v 1.20 2017/03/15 14:31:49 rzalamena Exp $ */ /* * Copyright (c) 2004 Henning Brauer <henning@openbsd.org> @@ -39,6 +39,8 @@ * Enterprises, see ``http://www.vix.com''. */ +#include <sys/queue.h> + #define SERVER_PORT 67 #define CLIENT_PORT 68 @@ -90,7 +92,6 @@ enum dhcp_relay_mode { }; struct interface_info { - struct interface_info *next; struct hardware hw_address; struct in_addr primary_address; char name[IFNAMSIZ]; @@ -105,7 +106,14 @@ struct interface_info { int errors; int dead; u_int16_t index; + + int ipv6; /* Has any IPv6 address. */ + int gipv6; /* Has global IPv6 address. */ + struct in6_addr linklocal; /* IPv6 link-local address. */ + + TAILQ_ENTRY(interface_info) entry; }; +TAILQ_HEAD(iflist, interface_info); struct timeout { struct timeout *next; @@ -143,7 +151,9 @@ ssize_t receive_packet(struct interface_info *, unsigned char *, size_t, /* dispatch.c */ extern void (*bootp_packet_handler)(struct interface_info *, struct dhcp_packet *, int, struct packet_ctx *); -struct interface_info *get_interface(const char *, +struct interface_info *iflist_getbyname(const char *); +void setup_iflist(void); +struct interface_info *register_interface(const char *, void (*)(struct protocol *), int isserver); void dispatch(void); void got_one(struct protocol *); @@ -162,6 +172,7 @@ ssize_t decode_udp_ip_header(struct interface_info *, unsigned char *, /* dhcrelay.c */ extern int server_fd; +extern struct iflist intflist; /* crap */ extern time_t cur_time; @@ -171,3 +182,9 @@ ss2sin(struct sockaddr_storage *ss) { return ((struct sockaddr_in *)ss); } + +static inline struct sockaddr_in6 * +ss2sin6(struct sockaddr_storage *ss) +{ + return ((struct sockaddr_in6 *)ss); +} diff --git a/usr.sbin/dhcrelay/dhcrelay.c b/usr.sbin/dhcrelay/dhcrelay.c index 15a8408c03e..ea8d56f0071 100644 --- a/usr.sbin/dhcrelay/dhcrelay.c +++ b/usr.sbin/dhcrelay/dhcrelay.c @@ -1,4 +1,4 @@ -/* $OpenBSD: dhcrelay.c,v 1.59 2017/03/14 09:21:26 rzalamena Exp $ */ +/* $OpenBSD: dhcrelay.c,v 1.60 2017/03/15 14:31:49 rzalamena Exp $ */ /* * Copyright (c) 2004 Henning Brauer <henning@cvs.openbsd.org> @@ -84,6 +84,7 @@ ssize_t relay_agentinfo_remove(struct packet_ctx *, struct dhcp_packet *, time_t cur_time; struct interface_info *interfaces = NULL; +struct iflist intflist; int server_fd; int oflag; @@ -113,6 +114,8 @@ main(int argc, char *argv[]) log_init(1, LOG_DAEMON); /* log to stderr until daemonized */ log_setverbose(1); + setup_iflist(); + while ((ch = getopt(argc, argv, "aC:di:oR:r")) != -1) { switch (ch) { case 'C': @@ -125,7 +128,7 @@ main(int argc, char *argv[]) if (interfaces != NULL) usage(); - interfaces = get_interface(optarg, got_one, 0); + interfaces = iflist_getbyname(optarg); if (interfaces == NULL) fatalx("interface '%s' not found", optarg); break; @@ -172,7 +175,8 @@ main(int argc, char *argv[]) if ((sp = calloc(1, sizeof(*sp))) == NULL) fatalx("calloc"); - if ((sp->intf = get_interface(argv[0], got_one, 1)) != NULL) { + if ((sp->intf = register_interface(argv[0], got_one, + 1)) != NULL) { if (drm == DRM_LAYER3) fatalx("don't mix interfaces with hosts"); @@ -218,8 +222,10 @@ main(int argc, char *argv[]) fatal("open(%s)", _PATH_DEVNULL); } - if (interfaces == NULL) + if (interfaces == NULL || + register_interface(interfaces->name, got_one, 0) == NULL) fatalx("no interface given"); + /* We need an address for running layer 3 mode. */ if (drm == DRM_LAYER3 && (interfaces->hw_address.htype != HTYPE_IPSEC_TUNNEL && diff --git a/usr.sbin/dhcrelay/dispatch.c b/usr.sbin/dhcrelay/dispatch.c index 1f075b6b54a..c33130a425d 100644 --- a/usr.sbin/dhcrelay/dispatch.c +++ b/usr.sbin/dhcrelay/dispatch.c @@ -1,4 +1,4 @@ -/* $OpenBSD: dispatch.c,v 1.19 2017/02/13 22:49:38 krw Exp $ */ +/* $OpenBSD: dispatch.c,v 1.20 2017/03/15 14:31:49 rzalamena Exp $ */ /* * Copyright 2004 Henning Brauer <henning@openbsd.org> @@ -49,6 +49,7 @@ #include <net/if_types.h> #include <netinet/in.h> +#include <netinet/if_ether.h> #include <errno.h> #include <ifaddrs.h> @@ -64,6 +65,14 @@ #include "dhcpd.h" #include "log.h" +/* + * Macros implementation used to generate link-local addresses. This + * code was copied from: sys/netinet6/in6_ifattach.c. + */ +#define EUI64_UBIT 0x02 +#define EUI64_TO_IFID(in6) \ + do { (in6)->s6_addr[8] ^= EUI64_UBIT; } while (0) + struct protocol *protocols; struct timeout *timeouts; static struct timeout *free_timeouts; @@ -75,83 +84,154 @@ void (*bootp_packet_handler)(struct interface_info *, static int interface_status(struct interface_info *ifinfo); struct interface_info * -get_interface(const char *ifname, void (*handler)(struct protocol *), - int isserver) +iflist_getbyname(const char *name) { - struct interface_info *iface; - struct ifaddrs *ifap, *ifa; - struct sockaddr_in *sin; - int found = 0; + struct interface_info *intf; - if ((iface = calloc(1, sizeof(*iface))) == NULL) - fatalx("failed to allocate memory"); + TAILQ_FOREACH(intf, &intflist, entry) { + if (strcmp(intf->name, name) != 0) + continue; - if (strlcpy(iface->name, ifname, sizeof(iface->name)) >= - sizeof(iface->name)) - fatalx("interface name '%s' too long", ifname); + return intf; + } + + return NULL; +} + +void +setup_iflist(void) +{ + struct interface_info *intf; + struct sockaddr_dl *sdl; + struct ifaddrs *ifap, *ifa; + struct if_data *ifi; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; - if (getifaddrs(&ifap) != 0) + TAILQ_INIT(&intflist); + if (getifaddrs(&ifap)) fatalx("getifaddrs failed"); for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { if ((ifa->ifa_flags & IFF_LOOPBACK) || - (ifa->ifa_flags & IFF_POINTOPOINT) || - (!(ifa->ifa_flags & IFF_UP))) + (ifa->ifa_flags & IFF_POINTOPOINT)) continue; - if (strcmp(ifname, ifa->ifa_name)) - continue; + /* Find interface or create it. */ + intf = iflist_getbyname(ifa->ifa_name); + if (intf == NULL) { + intf = calloc(1, sizeof(*intf)); + if (intf == NULL) + fatal("calloc"); - found = 1; + strlcpy(intf->name, ifa->ifa_name, + sizeof(intf->name)); + TAILQ_INSERT_HEAD(&intflist, intf, entry); + } + + /* Signal disabled interface. */ + if ((ifa->ifa_flags & IFF_UP) == 0) + intf->dead = 1; - /* - * If we have the capability, extract link information - * and record it in a linked list. - */ if (ifa->ifa_addr->sa_family == AF_LINK) { - struct sockaddr_dl *foo = - (struct sockaddr_dl *)ifa->ifa_addr; - struct if_data *ifi = - (struct if_data *)ifa->ifa_data; - - iface->index = foo->sdl_index; - iface->hw_address.hlen = foo->sdl_alen; - if (ifi->ifi_type == IFT_ENC) - iface->hw_address.htype = HTYPE_IPSEC_TUNNEL; - else - iface->hw_address.htype = HTYPE_ETHER; /*XXX*/ - memcpy(iface->hw_address.haddr, - LLADDR(foo), foo->sdl_alen); - } else if (ifa->ifa_addr->sa_family == AF_INET) { - /* We already have the primary address. */ - if (iface->primary_address.s_addr != 0) + sdl = (struct sockaddr_dl *)ifa->ifa_addr; + ifi = (struct if_data *)ifa->ifa_data; + + /* Skip non ethernet interfaces. */ + if (ifi->ifi_type != IFT_ETHER && + ifi->ifi_type != IFT_ENC) { + TAILQ_REMOVE(&intflist, intf, entry); + free(intf); continue; + } + + if (ifi->ifi_type == IFT_ETHER) + intf->hw_address.htype = HTYPE_ETHER; + else + intf->hw_address.htype = HTYPE_IPSEC_TUNNEL; + intf->index = sdl->sdl_index; + intf->hw_address.hlen = sdl->sdl_alen; + memcpy(intf->hw_address.haddr, + LLADDR(sdl), sdl->sdl_alen); + } else if (ifa->ifa_addr->sa_family == AF_INET) { sin = (struct sockaddr_in *)ifa->ifa_addr; - if (sin->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) + if (sin->sin_addr.s_addr == htonl(INADDR_LOOPBACK) || + intf->primary_address.s_addr != INADDR_ANY) continue; - iface->primary_address = sin->sin_addr; + intf->primary_address = sin->sin_addr; + } else if (ifa->ifa_addr->sa_family == AF_INET6) { + sin6 = (struct sockaddr_in6 *)ifa->ifa_addr; + /* Remove the scope from address if link-local. */ + if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) { + intf->linklocal = sin6->sin6_addr; + intf->linklocal.s6_addr[2] = 0; + intf->linklocal.s6_addr[3] = 0; + } else + intf->gipv6 = 1; + + /* At least one IPv6 address was found. */ + intf->ipv6 = 1; } } freeifaddrs(ifap); - if (!found) { - free(iface); - return (NULL); + /* + * Generate link-local IPv6 address for interfaces without it. + * + * For IPv6 DHCP Relay it doesn't matter what is used for + * link-addr field, so let's generate an address that won't + * change during execution so we can always find the interface + * to relay packets back. This is only used for layer 2 relaying + * when the interface might not have an address. + */ + TAILQ_FOREACH(intf, &intflist, entry) { + if (memcmp(&intf->linklocal, &in6addr_any, + sizeof(in6addr_any)) != 0) + continue; + + intf->linklocal.s6_addr[0] = 0xfe; + intf->linklocal.s6_addr[1] = 0x80; + intf->linklocal.s6_addr[8] = intf->hw_address.haddr[0]; + intf->linklocal.s6_addr[9] = intf->hw_address.haddr[1]; + intf->linklocal.s6_addr[10] = intf->hw_address.haddr[2]; + intf->linklocal.s6_addr[11] = 0xff; + intf->linklocal.s6_addr[12] = 0xfe; + intf->linklocal.s6_addr[13] = intf->hw_address.haddr[3]; + intf->linklocal.s6_addr[14] = intf->hw_address.haddr[4]; + intf->linklocal.s6_addr[15] = intf->hw_address.haddr[5]; + EUI64_TO_IFID(&intf->linklocal); } +} + +struct interface_info * +register_interface(const char *ifname, void (*handler)(struct protocol *), + int isserver) +{ + struct interface_info *intf; + + if ((intf = iflist_getbyname(ifname)) == NULL) + return NULL; + + /* Don't register disabled interfaces. */ + if (intf->dead) + return NULL; + + /* Check if we already registered the interface. */ + if (intf->ifr.ifr_name[0] != 0) + return intf; - if (strlcpy(iface->ifr.ifr_name, ifname, - sizeof(iface->ifr.ifr_name)) >= sizeof(iface->ifr.ifr_name)) + if (strlcpy(intf->ifr.ifr_name, ifname, + sizeof(intf->ifr.ifr_name)) >= sizeof(intf->ifr.ifr_name)) fatalx("interface name '%s' too long", ifname); - /* Register the interface... */ - if_register_receive(iface, isserver); - if_register_send(iface); - add_protocol(iface->name, iface->rfdesc, handler, iface); + if_register_receive(intf, isserver); + if_register_send(intf); + add_protocol(intf->name, intf->rfdesc, handler, intf); - return (iface); + return intf; } /* |