summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRafael Zalamena <rzalamena@cvs.openbsd.org>2017-03-15 14:31:50 +0000
committerRafael Zalamena <rzalamena@cvs.openbsd.org>2017-03-15 14:31:50 +0000
commit5f408e08fd8bf86f6e77fa07e833048027c01cf4 (patch)
tree8de83373f5453b2743b1d2313783775663723194
parent7a00f75f1939163102bf9679143f8225da5bedb0 (diff)
Improve the interface and addresses discovery code and prepare to
receive IPv6 support. ok reyk@
-rw-r--r--usr.sbin/dhcrelay/dhcpd.h23
-rw-r--r--usr.sbin/dhcrelay/dhcrelay.c14
-rw-r--r--usr.sbin/dhcrelay/dispatch.c180
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;
}
/*