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. */