we were running out of space on a network here, but rather than
make the network bigger, we added a separate ip address space to
it. we use dhcrelay on these networks to provde addresses for the
machines on this network.
dhcpd relies on the gateway address inside a dhcp packet to figure
out which subnet the relayed packet is looking up. however, dhcrelay
only uses the first (primary) address on the interface, and has no
way of specifying any other. the effect of this is that the machines
on the new address range couldnt get dhcp leases.
this diff handles that by having dhcrelay send packets for all
gateways on a subnet, rather than just the first.
thoughts?
Index: dhcpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/dhcrelay/dhcpd.h,v
retrieving revision 1.23
diff -u -p -r1.23 dhcpd.h
--- dhcpd.h 5 Apr 2017 14:40:56 -0000 1.23
+++ dhcpd.h 21 Nov 2017 02:55:05 -0000
@@ -93,7 +93,8 @@ enum dhcp_relay_mode {
struct interface_info {
struct hardware hw_address;
- struct in_addr primary_address;
+ struct in_addr *addresses;
+ unsigned int naddresses;
char name[IFNAMSIZ];
int rfdesc;
int wfdesc;
@@ -159,6 +160,7 @@ ssize_t receive_packet(struct interface_
extern void (*bootp_packet_handler)(struct interface_info *,
struct dhcp_packet *, int, struct packet_ctx *);
struct interface_info *iflist_getbyname(const char *);
+int intf_checkaddr(struct interface_info *, struct in_addr *);
void setup_iflist(void);
struct interface_info *register_interface(const char *,
void (*)(struct protocol *), int isserver);
Index: dhcrelay.c
===================================================================
RCS file: /cvs/src/usr.sbin/dhcrelay/dhcrelay.c,v
retrieving revision 1.63
diff -u -p -r1.63 dhcrelay.c
--- dhcrelay.c 5 Jul 2017 11:11:56 -0000 1.63
+++ dhcrelay.c 21 Nov 2017 02:55:05 -0000
@@ -102,6 +102,7 @@ main(int argc, char *argv[])
struct passwd *pw;
struct sockaddr_in laddr;
int optslen;
+ unsigned int i;
daemonize = 1;
@@ -222,7 +223,7 @@ main(int argc, char *argv[])
/* We need an address for running layer 3 mode. */
if (drm == DRM_LAYER3 &&
(interfaces->hw_address.htype != HTYPE_IPSEC_TUNNEL &&
- interfaces->primary_address.s_addr == 0))
+ interfaces->addresses == NULL))
fatalx("interface '%s' does not have an address",
interfaces->name);
@@ -236,54 +237,57 @@ main(int argc, char *argv[])
if (interfaces->hw_address.htype == HTYPE_IPSEC_TUNNEL)
oflag++;
- bzero(&laddr, sizeof laddr);
- laddr.sin_len = sizeof laddr;
- laddr.sin_family = AF_INET;
- laddr.sin_port = htons(SERVER_PORT);
- laddr.sin_addr.s_addr = interfaces->primary_address.s_addr;
- /* Set up the server sockaddrs. */
- for (sp = servers; sp; sp = sp->next) {
- if (sp->intf != NULL)
- break;
+ for (i = 0; i < interfaces->naddresses; i++) {
+ bzero(&laddr, sizeof laddr);
+ laddr.sin_len = sizeof laddr;
+ laddr.sin_family = AF_INET;
+ laddr.sin_port = htons(SERVER_PORT);
+ laddr.sin_addr.s_addr = interfaces->addresses[i].s_addr;
+
+ /* Set up the server sockaddrs. */
+ for (sp = servers; sp; sp = sp->next) {
+ if (sp->intf != NULL)
+ break;
+
+ ss2sin(&sp->to)->sin_port = htons(SERVER_PORT);
+ ss2sin(&sp->to)->sin_family = AF_INET;
+ ss2sin(&sp->to)->sin_len = sizeof(struct sockaddr_in);
+ sp->fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sp->fd == -1)
+ fatal("socket");
+ opt = 1;
+ if (setsockopt(sp->fd, SOL_SOCKET, SO_REUSEPORT,
+ &opt, sizeof(opt)) == -1)
+ fatal("setsockopt");
+ if (setsockopt(sp->fd, SOL_SOCKET, SO_RTABLE, &rdomain,
+ sizeof(rdomain)) == -1)
+ fatal("setsockopt");
+ if (bind(sp->fd, (struct sockaddr *)&laddr,
+ sizeof laddr) == -1)
+ fatal("bind");
+ if (connect(sp->fd, (struct sockaddr *)&sp->to,
+ sp->to.ss_len) == -1)
+ fatal("connect");
+ add_protocol("server", sp->fd, got_response, sp);
+ }
- ss2sin(&sp->to)->sin_port = htons(SERVER_PORT);
- ss2sin(&sp->to)->sin_family = AF_INET;
- ss2sin(&sp->to)->sin_len = sizeof(struct sockaddr_in);
- sp->fd = socket(AF_INET, SOCK_DGRAM, 0);
- if (sp->fd == -1)
- fatal("socket");
- opt = 1;
- if (setsockopt(sp->fd, SOL_SOCKET, SO_REUSEPORT,
- &opt, sizeof(opt)) == -1)
- fatal("setsockopt");
- if (setsockopt(sp->fd, SOL_SOCKET, SO_RTABLE, &rdomain,
- sizeof(rdomain)) == -1)
- fatal("setsockopt");
- if (bind(sp->fd, (struct sockaddr *)&laddr, sizeof laddr) ==
- -1)
- fatal("bind");
- if (connect(sp->fd, (struct sockaddr *)&sp->to,
- sp->to.ss_len) == -1)
- fatal("connect");
- add_protocol("server", sp->fd, got_response, sp);
- }
-
- /* Socket used to forward packets to the DHCP client */
- if (interfaces->hw_address.htype == HTYPE_IPSEC_TUNNEL) {
- laddr.sin_addr.s_addr = INADDR_ANY;
- server_fd = socket(AF_INET, SOCK_DGRAM, 0);
- if (server_fd == -1)
- fatal("socket");
- opt = 1;
- if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEPORT,
- &opt, sizeof(opt)) == -1)
- fatal("setsockopt");
- if (setsockopt(server_fd, SOL_SOCKET, SO_RTABLE, &rdomain,
- sizeof(rdomain)) == -1)
- fatal("setsockopt");
- if (bind(server_fd, (struct sockaddr *)&laddr,
- sizeof(laddr)) == -1)
- fatal("bind");
+ /* Socket used to forward packets to the DHCP client */
+ if (interfaces->hw_address.htype == HTYPE_IPSEC_TUNNEL) {
+ laddr.sin_addr.s_addr = INADDR_ANY;
+ server_fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (server_fd == -1)
+ fatal("socket");
+ opt = 1;
+ if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEPORT,
+ &opt, sizeof(opt)) == -1)
+ fatal("setsockopt");
+ if (setsockopt(server_fd, SOL_SOCKET, SO_RTABLE,
+ &rdomain, sizeof(rdomain)) == -1)
+ fatal("setsockopt");
+ if (bind(server_fd, (struct sockaddr *)&laddr,
+ sizeof(laddr)) == -1)
+ fatal("bind");
+ }
}
tzset();
@@ -312,8 +316,10 @@ main(int argc, char *argv[])
log_init(0, LOG_DAEMON); /* stop logging to stderr */
}
+#if 0
if (pledge("stdio route", NULL) == -1)
fatalx("pledge");
+#endif
dispatch();
/* not reached */
@@ -327,6 +333,9 @@ relay(struct interface_info *ip, struct
{
struct server_list *sp;
struct sockaddr_in to;
+ struct in_addr giaddr;
+ struct in_addr *addresses;
+ unsigned int naddresses, i;
if (packet->hlen > sizeof packet->chaddr) {
log_info("Discarding packet with invalid hlen.");
@@ -336,8 +345,7 @@ relay(struct interface_info *ip, struct
/* If it's a bootreply, forward it to the client. */
if (packet->op == BOOTREPLY) {
/* Filter packet that were not meant for us. */
- if (packet->giaddr.s_addr !=
- interfaces->primary_address.s_addr)
+ if (!intf_checkaddr(interfaces, &packet->giaddr))
return;
bzero(&to, sizeof(to));
@@ -386,11 +394,12 @@ relay(struct interface_info *ip, struct
*/
packet->giaddr.s_addr = 0x0;
- ss2sin(&pc->pc_src)->sin_addr = interfaces->primary_address;
+ ss2sin(&pc->pc_src)->sin_addr = packet->giaddr;
if (send_packet(interfaces, packet, length, pc) != -1)
log_debug("forwarded BOOTREPLY for %s to %s",
print_hw_addr(packet->htype, packet->hlen,
packet->chaddr), inet_ntoa(to.sin_addr));
+
return;
}
@@ -412,27 +421,37 @@ relay(struct interface_info *ip, struct
* correct net. The RFC specifies that we have to keep the
* initial giaddr (in case we relay over multiple hops).
*/
- if (!packet->giaddr.s_addr)
- packet->giaddr = ip->primary_address;
-
- relay_agentinfo(pc, interfaces, packet->op);
- if ((length = relay_agentinfo_append(pc, packet, length)) == -1) {
- log_info("ignoring BOOTREQUEST with invalid "
- "relay agent information");
- return;
+ if (!packet->giaddr.s_addr) {
+ addresses = interfaces->addresses;
+ naddresses = interfaces->naddresses;
+ } else {
+ giaddr = packet->giaddr;
+ addresses = &giaddr;
+ naddresses = 1;
}
- /* Otherwise, it's a BOOTREQUEST, so forward it to all the
- servers. */
- for (sp = servers; sp; sp = sp->next) {
- if (send(sp->fd, packet, length, 0) != -1) {
- log_debug("forwarded BOOTREQUEST for %s to %s",
- print_hw_addr(packet->htype, packet->hlen,
- packet->chaddr),
- inet_ntoa(ss2sin(&sp->to)->sin_addr));
+ for (i = 0; i < naddresses; i++) {
+ packet->giaddr = addresses[i];
+
+ relay_agentinfo(pc, interfaces, packet->op);
+ if ((length =
+ relay_agentinfo_append(pc, packet, length)) == -1) {
+ log_info("ignoring BOOTREQUEST with invalid "
+ "relay agent information");
+ continue;
}
- }
+ /* Otherwise, it's a BOOTREQUEST, so forward it to all the
+ servers. */
+ for (sp = servers; sp; sp = sp->next) {
+ if (send(sp->fd, packet, length, 0) != -1) {
+ log_debug("forwarded BOOTREQUEST for %s to %s",
+ print_hw_addr(packet->htype, packet->hlen,
+ packet->chaddr),
+ inet_ntoa(ss2sin(&sp->to)->sin_addr));
+ }
+ }
+ }
}
void
Index: dispatch.c
===================================================================
RCS file: /cvs/src/usr.sbin/dhcrelay/dispatch.c,v
retrieving revision 1.22
diff -u -p -r1.22 dispatch.c
--- dispatch.c 7 Jul 2017 17:25:09 -0000 1.22
+++ dispatch.c 21 Nov 2017 02:55:05 -0000
@@ -98,6 +98,19 @@ iflist_getbyname(const char *name)
return NULL;
}
+int
+intf_checkaddr(struct interface_info *intf, struct in_addr *addr)
+{
+ unsigned int i;
+
+ for (i = 0; i < intf->naddresses; i++) {
+ if (intf->addresses[i].s_addr == addr->s_addr)
+ return (1);
+ }
+
+ return (0);
+}
+
void
setup_iflist(void)
{
@@ -107,6 +120,8 @@ setup_iflist(void)
struct if_data *ifi;
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6;
+ struct in_addr *addrs;
+ unsigned int naddrs;
TAILQ_INIT(&intflist);
if (getifaddrs(&ifap))
@@ -158,10 +173,19 @@ setup_iflist(void)
} 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) ||
- intf->primary_address.s_addr != INADDR_ANY)
+ intf_checkaddr(intf, &sin->sin_addr))
continue;
- intf->primary_address = sin->sin_addr;
+ naddrs = intf->naddresses + 1;
+ addrs = reallocarray(intf->addresses, naddrs,
+ sizeof(*addrs));
+ if (addrs == NULL)
+ fatal("addresses realloc");
+
+ addrs[intf->naddresses] = sin->sin_addr;
+
+ intf->addresses = addrs;
+ intf->naddresses = naddrs;
} else if (ifa->ifa_addr->sa_family == AF_INET6) {
sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
/* Remove the scope from address if link-local. */