Index: Makefile =================================================================== RCS file: /cvs/src/usr.sbin/dhcpd/Makefile,v diff -u -p -r1.7 Makefile --- Makefile 12 Apr 2017 19:19:11 -0000 1.7 +++ Makefile 13 Jun 2025 03:27:14 -0000 @@ -2,7 +2,7 @@ .include -SRCS= bootp.c confpars.c db.c dhcp.c dhcpd.c bpf.c packet.c log.c \ +SRCS= bootp.c confpars.c db.c dhcp.c dhcpd.c sockets.c packet.c log.c \ dispatch.c print.c memory.c options.c inet.c conflex.c parse.c \ alloc.c tables.c tree.c hash.c convert.c icmp.c pfutils.c sync.c \ udpsock.c @@ -11,6 +11,8 @@ MAN= dhcpd.8 dhcpd.conf.5 dhcpd.leases.5 LDADD+=-lcrypto CFLAGS+=-Wall + +DEBUG=-g .include CFLAGS+=-Wstrict-prototypes -Wmissing-prototypes Index: bootp.c =================================================================== RCS file: /cvs/src/usr.sbin/dhcpd/bootp.c,v diff -u -p -r1.18 bootp.c --- bootp.c 13 Feb 2017 22:33:39 -0000 1.18 +++ bootp.c 13 Jun 2025 03:27:14 -0000 @@ -66,7 +66,6 @@ bootp(struct packet *packet) struct packet outgoing; struct dhcp_packet raw; struct sockaddr_in to; - struct in_addr from; struct tree_cache *options[256]; struct shared_network *s; struct subnet *subnet = NULL; @@ -234,6 +233,8 @@ lose: memset(&outgoing, 0, sizeof outgoing); memset(&raw, 0, sizeof raw); outgoing.raw = &raw; + outgoing.laddr = packet->laddr; + outgoing.raddr = packet->raddr; /* * If we didn't get a known vendor magic number on the way in, just @@ -305,7 +306,7 @@ lose: else if (subnet->interface_address.len) memcpy(&raw.siaddr, subnet->interface_address.iabuf, 4); else - raw.siaddr = packet->interface->primary_address; + raw.siaddr = packet->interface->primary_address.sin_addr; raw.giaddr = packet->raw->giaddr; if (hp->group->server_name) @@ -321,8 +322,6 @@ lose: else memcpy(raw.file, packet->raw->file, sizeof(raw.file)); - from = packet->interface->primary_address; - /* Report what we're doing... */ log_info("BOOTREPLY for %s to %s (%s) via %s", piaddr(ip_address), hp->name, print_hw_addr(packet->raw->htype, packet->raw->hlen, @@ -338,29 +337,26 @@ lose: /* If this was gatewayed, send it back to the gateway... */ if (raw.giaddr.s_addr) { + to.sin_family = AF_INET; to.sin_addr = raw.giaddr; to.sin_port = server_port; - (void) packet->interface->send_packet(packet->interface, &raw, - outgoing.packet_length, from, &to, packet->haddr); - return; - } - /* * If it comes from a client that already knows its address and is not * requesting a broadcast response, and we can unicast to a client * without using the ARP protocol, sent it directly to that client. */ - else if (!(raw.flags & htons(BOOTP_BROADCAST))) { + } else if (!(raw.flags & htons(BOOTP_BROADCAST))) { + to.sin_family = AF_INET; to.sin_addr = raw.yiaddr; to.sin_port = client_port; } else { /* Otherwise, broadcast it on the local network. */ - to.sin_addr.s_addr = INADDR_BROADCAST; + to.sin_family = AF_INET; + to.sin_addr.s_addr = htonl(INADDR_BROADCAST); to.sin_port = client_port; /* XXX */ } errno = 0; - (void) packet->interface->send_packet(packet->interface, &raw, - outgoing.packet_length, from, &to, packet->haddr); + (void) packet->interface->send_packet(&outgoing, lease, &to); } Index: bpf.c =================================================================== RCS file: /cvs/src/usr.sbin/dhcpd/bpf.c,v diff -u -p -r1.21 bpf.c --- bpf.c 7 Feb 2025 21:48:26 -0000 1.21 +++ bpf.c 13 Jun 2025 03:27:14 -0000 @@ -1,381 +0,0 @@ -/* $OpenBSD: bpf.c,v 1.21 2025/02/07 21:48:26 bluhm Exp $ */ - -/* BPF socket interface code, originally contributed by Archie Cobbs. */ - -/* - * Copyright (c) 1995, 1996, 1998, 1999 - * The Internet Software Consortium. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of The Internet Software Consortium nor the names - * of its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND - * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * This software has been written for the Internet Software Consortium - * by Ted Lemon in cooperation with Vixie - * Enterprises. To learn more about the Internet Software Consortium, - * see ``http://www.vix.com/isc''. To learn more about Vixie - * Enterprises, see ``http://www.vix.com''. - */ - -#include -#include -#include - -#include - -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "dhcp.h" -#include "tree.h" -#include "dhcpd.h" -#include "log.h" - -ssize_t send_packet (struct interface_info *, struct dhcp_packet *, - size_t, struct in_addr, struct sockaddr_in *, struct hardware *); - -/* - * Called by get_interface_list for each interface that's discovered. - * Opens a packet filter for each interface and adds it to the select - * mask. - */ -int -if_register_bpf(struct interface_info *info) -{ - int sock; - - if ((sock = open("/dev/bpf", O_RDWR)) == -1) - fatal("Can't open bpf device"); - - /* Set the BPF device to point at this interface. */ - if (ioctl(sock, BIOCSETIF, info->ifp) == -1) - fatal("Can't attach interface %s to bpf device", info->name); - - info->send_packet = send_packet; - return (sock); -} - -void -if_register_send(struct interface_info *info) -{ - /* - * If we're using the bpf API for sending and receiving, we - * don't need to register this interface twice. - */ - info->wfdesc = info->rfdesc; -} - -/* - * Packet read filter program: 'ip and udp and dst port bootps' - */ -struct bpf_insn dhcp_bpf_filter[] = { - /* Make sure this is an IP packet... */ - BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12), - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 8), - - /* Make sure it's a UDP packet... */ - BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 23), - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 6), - - /* Make sure this isn't a fragment... */ - BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20), - BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 4, 0), - - /* Get the IP header length... */ - BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14), - - /* Make sure it's to the right port... */ - BPF_STMT(BPF_LD + BPF_H + BPF_IND, 16), - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, SERVER_PORT, 0, 1), - - /* If we passed all the tests, ask for the whole packet. */ - BPF_STMT(BPF_RET+BPF_K, (u_int)-1), - - /* Otherwise, drop it. */ - BPF_STMT(BPF_RET+BPF_K, 0), -}; - -int dhcp_bpf_filter_len = sizeof(dhcp_bpf_filter) / sizeof(struct bpf_insn); - - -/* - * Packet write filter program: - * 'ip and udp and src port bootps and dst port (bootps or bootpc)' - */ -struct bpf_insn dhcp_bpf_wfilter[] = { - /* Make sure this is an IP packet... */ - BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12), - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 11), - - /* Make sure it's a UDP packet... */ - BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 23), - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 9), - - /* Make sure this isn't a fragment... */ - BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20), - BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 7, 0), - - /* Get the IP header length... */ - BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14), - - /* Make sure it's from the right port... */ - BPF_STMT(BPF_LD + BPF_H + BPF_IND, 14), - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, SERVER_PORT, 0, 4), - - /* Make sure it is to the right ports ... */ - BPF_STMT(BPF_LD + BPF_H + BPF_IND, 16), - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, CLIENT_PORT, 1, 0), - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, SERVER_PORT, 0, 1), - - /* If we passed all the tests, ask for the whole packet. */ - BPF_STMT(BPF_RET+BPF_K, (u_int)-1), - - /* Otherwise, drop it. */ - BPF_STMT(BPF_RET+BPF_K, 0), -}; - -int dhcp_bpf_wfilter_len = sizeof(dhcp_bpf_wfilter) / sizeof(struct bpf_insn); - -void -if_register_receive(struct interface_info *info) -{ - struct bpf_version v; - struct bpf_program p; - int flag = 1, sz, cmplt = 0; - int fildrop = BPF_FILDROP_CAPTURE; - - /* Open a BPF device and hang it on this interface... */ - info->rfdesc = if_register_bpf(info); - - /* Make sure the BPF version is in range... */ - if (ioctl(info->rfdesc, BIOCVERSION, &v) == -1) - fatal("Can't get BPF version"); - - if (v.bv_major != BPF_MAJOR_VERSION || - v.bv_minor < BPF_MINOR_VERSION) - fatalx("Kernel BPF version out of range - recompile dhcpd!"); - - /* - * Set immediate mode so that reads return as soon as a packet - * comes in, rather than waiting for the input buffer to fill - * with packets. - */ - if (ioctl(info->rfdesc, BIOCIMMEDIATE, &flag) == -1) - fatal("Can't set immediate mode on bpf device"); - - if (ioctl(info->rfdesc, BIOCSFILDROP, &fildrop) == -1) - fatal("Can't set filter-drop mode on bpf device"); - - /* make sure kernel fills in the source ethernet address */ - if (ioctl(info->rfdesc, BIOCSHDRCMPLT, &cmplt) == -1) - fatal("Can't set header complete flag on bpf device"); - - /* Get the required BPF buffer length from the kernel. */ - if (ioctl(info->rfdesc, BIOCGBLEN, &sz) == -1) - fatal("Can't get bpf buffer length"); - info->rbuf_max = sz; - info->rbuf = malloc(info->rbuf_max); - if (!info->rbuf) - fatalx("Can't allocate %lu bytes for bpf input buffer.", - (unsigned long)info->rbuf_max); - info->rbuf_offset = 0; - info->rbuf_len = 0; - - /* Set up the bpf filter program structure. */ - p.bf_len = dhcp_bpf_filter_len; - p.bf_insns = dhcp_bpf_filter; - - if (ioctl(info->rfdesc, BIOCSETF, &p) == -1) - fatal("Can't install packet filter program"); - - /* Set up the bpf write filter program structure. */ - p.bf_len = dhcp_bpf_wfilter_len; - p.bf_insns = dhcp_bpf_wfilter; - - if (ioctl(info->rfdesc, BIOCSETWF, &p) == -1) - fatal("Can't install write filter program"); - - /* make sure these settings cannot be changed after dropping privs */ - if (ioctl(info->rfdesc, BIOCLOCK) == -1) - fatal("Failed to lock bpf descriptor"); -} - -ssize_t -send_packet(struct interface_info *interface, struct dhcp_packet *raw, - size_t len, struct in_addr from, struct sockaddr_in *to, - struct hardware *hto) -{ - unsigned char buf[256]; - struct iovec iov[2]; - ssize_t result; - int bufp = 0; - - /* Assemble the headers... */ - assemble_hw_header(interface, buf, &bufp, hto); - assemble_udp_ip_header(interface, buf, &bufp, from.s_addr, - to->sin_addr.s_addr, to->sin_port, (unsigned char *)raw, len); - - /* Fire it off */ - iov[0].iov_base = (char *)buf; - iov[0].iov_len = bufp; - iov[1].iov_base = (char *)raw; - iov[1].iov_len = len; - - result = writev(interface->wfdesc, iov, 2); - if (result == -1) - log_warn("send_packet"); - return (result); -} - -ssize_t -receive_packet(struct interface_info *interface, unsigned char *buf, - size_t len, struct sockaddr_in *from, struct hardware *hfrom) -{ - int length = 0, offset = 0; - struct bpf_hdr hdr; - - /* - * All this complexity is because BPF doesn't guarantee that - * only one packet will be returned at a time. We're getting - * what we deserve, though - this is a terrible abuse of the BPF - * interface. Sigh. - */ - - /* Process packets until we get one we can return or until we've - * done a read and gotten nothing we can return... - */ - do { - /* If the buffer is empty, fill it. */ - if (interface->rbuf_offset >= interface->rbuf_len) { - length = read(interface->rfdesc, interface->rbuf, - interface->rbuf_max); - if (length <= 0) - return (length); - interface->rbuf_offset = 0; - interface->rbuf_len = length; - } - - /* - * If there isn't room for a whole bpf header, something - * went wrong, but we'll ignore it and hope it goes - * away... XXX - */ - if (interface->rbuf_len - interface->rbuf_offset < - sizeof(hdr)) { - interface->rbuf_offset = interface->rbuf_len; - continue; - } - - /* Copy out a bpf header... */ - memcpy(&hdr, &interface->rbuf[interface->rbuf_offset], - sizeof(hdr)); - - /* - * If the bpf header plus data doesn't fit in what's - * left of the buffer, stick head in sand yet again... - */ - if (interface->rbuf_offset + hdr.bh_hdrlen + hdr.bh_caplen > - interface->rbuf_len) { - interface->rbuf_offset = interface->rbuf_len; - continue; - } - - /* - * If the captured data wasn't the whole packet, or if - * the packet won't fit in the input buffer, all we can - * do is drop it. - */ - if (hdr.bh_caplen != hdr.bh_datalen) { - interface->rbuf_offset = BPF_WORDALIGN( - interface->rbuf_offset + hdr.bh_hdrlen + - hdr.bh_caplen); - continue; - } - - /* Skip over the BPF header... */ - interface->rbuf_offset += hdr.bh_hdrlen; - - /* Decode the physical header... */ - offset = decode_hw_header(interface->rbuf + - interface->rbuf_offset, hdr.bh_caplen, hfrom); - - /* - * If a physical layer checksum failed (dunno of any - * physical layer that supports this, but WTH), skip - * this packet. - */ - if (offset < 0) { - interface->rbuf_offset = BPF_WORDALIGN( - interface->rbuf_offset + hdr.bh_caplen); - continue; - } - interface->rbuf_offset += offset; - hdr.bh_caplen -= offset; - - /* Decode the IP and UDP headers... */ - offset = decode_udp_ip_header(interface->rbuf + - interface->rbuf_offset, hdr.bh_caplen, from, - hdr.bh_csumflags); - - /* If the IP or UDP checksum was bad, skip the packet... */ - if (offset < 0) { - interface->rbuf_offset = BPF_WORDALIGN( - interface->rbuf_offset + hdr.bh_caplen); - continue; - } - interface->rbuf_offset += offset; - hdr.bh_caplen -= offset; - - /* - * If there's not enough room to stash the packet data, - * we have to skip it (this shouldn't happen in real - * life, though). - */ - if (hdr.bh_caplen > len) { - interface->rbuf_offset = BPF_WORDALIGN( - interface->rbuf_offset + hdr.bh_caplen); - continue; - } - - /* Copy out the data in the packet... */ - memcpy(buf, interface->rbuf + interface->rbuf_offset, - hdr.bh_caplen); - interface->rbuf_offset = BPF_WORDALIGN(interface->rbuf_offset + - hdr.bh_caplen); - return (hdr.bh_caplen); - } while (!length); - return (0); -} Index: dhcp.c =================================================================== RCS file: /cvs/src/usr.sbin/dhcpd/dhcp.c,v diff -u -p -r1.57 dhcp.c --- dhcp.c 11 Jul 2017 10:28:24 -0000 1.57 +++ dhcp.c 13 Jun 2025 03:27:14 -0000 @@ -68,9 +68,8 @@ dhcp(struct packet *packet, int is_udpso if (!locate_network(packet) && packet->packet_type != DHCPREQUEST) return; - if (is_udpsock && packet->packet_type != DHCPINFORM) { - log_info("Unable to handle a DHCP message type=%d on UDP " - "socket", packet->packet_type); + if (is_udpsock && packet->packet_type == DHCPDISCOVER) { + log_info("Unable to handle a DHCPDISCOVER on UDP socket"); return; } @@ -529,16 +528,16 @@ dhcpinform(struct packet *packet) cip.len = 4; if (packet->raw->ciaddr.s_addr && !packet->raw->giaddr.s_addr) { if (memcmp(&packet->raw->ciaddr.s_addr, - packet->client_addr.iabuf, 4) != 0) { + &packet->raddr.sin_addr.s_addr, 4) != 0) { log_info("DHCPINFORM from %s but ciaddr %s is not " "consistent with actual address", - piaddr(packet->client_addr), + saname(&packet->raddr), inet_ntoa(packet->raw->ciaddr)); return; } memcpy(cip.iabuf, &packet->raw->ciaddr.s_addr, 4); } else - memcpy(cip.iabuf, &packet->client_addr.iabuf, 4); + memcpy(cip.iabuf, &packet->raddr.sin_addr.s_addr, 4); log_info("DHCPINFORM from %s", piaddr(cip)); @@ -575,8 +574,6 @@ void nak_lease(struct packet *packet, struct iaddr *cip) { struct sockaddr_in to; - struct in_addr from; - ssize_t result; int i; struct dhcp_packet raw; unsigned char nak = DHCPNAK; @@ -589,6 +586,9 @@ nak_lease(struct packet *packet, struct memset(&outgoing, 0, sizeof outgoing); memset(&raw, 0, sizeof raw); outgoing.raw = &raw; + outgoing.laddr = packet->laddr; + outgoing.raddr = packet->raddr; + outgoing.interface = packet->interface; /* Set DHCP_MESSAGE_TYPE to DHCPNAK */ i = DHO_DHCP_MESSAGE_TYPE; @@ -649,7 +649,7 @@ nak_lease(struct packet *packet, struct 0, options, 0, 0, 0, NULL, 0); /* memset(&raw.ciaddr, 0, sizeof raw.ciaddr);*/ - raw.siaddr = packet->interface->primary_address; + raw.siaddr = packet->interface->primary_address.sin_addr; raw.giaddr = packet->raw->giaddr; memcpy(raw.chaddr, packet->raw->chaddr, sizeof raw.chaddr); raw.hlen = packet->raw->hlen; @@ -666,17 +666,15 @@ nak_lease(struct packet *packet, struct packet->raw->chaddr), packet->raw->giaddr.s_addr ? inet_ntoa(packet->raw->giaddr) : packet->interface->name); + /* Make sure that the packet is at least as big as a BOOTP packet. */ + if (outgoing.packet_length < BOOTP_MIN_LEN) + outgoing.packet_length = BOOTP_MIN_LEN; + /* Set up the common stuff... */ memset(&to, 0, sizeof to); to.sin_family = AF_INET; to.sin_len = sizeof to; - from = packet->interface->primary_address; - - /* Make sure that the packet is at least as big as a BOOTP packet. */ - if (outgoing.packet_length < BOOTP_MIN_LEN) - outgoing.packet_length = BOOTP_MIN_LEN; - /* * If this was gatewayed, send it back to the gateway. * Otherwise, broadcast it on the local network. @@ -684,20 +682,13 @@ nak_lease(struct packet *packet, struct if (raw.giaddr.s_addr) { to.sin_addr = raw.giaddr; to.sin_port = server_port; - - result = packet->interface->send_packet(packet->interface, &raw, - outgoing.packet_length, from, &to, packet->haddr); - if (result == -1) - log_warn("send_fallback"); - return; } else { to.sin_addr.s_addr = htonl(INADDR_BROADCAST); to.sin_port = client_port; } errno = 0; - result = packet->interface->send_packet(packet->interface, &raw, - outgoing.packet_length, from, &to, NULL); + packet->interface->send_packet(&outgoing, NULL, &to); } void @@ -760,6 +751,8 @@ ack_lease(struct packet *packet, struct memset(state, 0, sizeof *state); state->got_requested_address = packet->got_requested_address; state->shared_network = packet->interface->shared_network; + state->laddr = packet->laddr; + state->raddr = packet->raddr; /* Remember if we got a server identifier option. */ i = DHO_DHCP_SERVER_IDENTIFIER; @@ -980,7 +973,6 @@ ack_lease(struct packet *packet, struct state->bootp_flags = packet->raw->flags; state->hops = packet->raw->hops; state->offer = offer; - memcpy(&state->haddr, packet->haddr, sizeof state->haddr); /* Figure out what options to send to the client: */ @@ -1070,32 +1062,35 @@ ack_lease(struct packet *packet, struct state->options[i]->tree = NULL; i = DHO_DHCP_SERVER_IDENTIFIER; - if (!state->options[i]) { - use_primary: + if (!state->options[i] || + /* Find the value of the server identifier... */ + !tree_evaluate(state->options[i]) || + !state->options[i]->value || + (state->options[i]->len > + sizeof state->from.iabuf)) { + struct sockaddr_in *sin = + &state->ip->primary_address; state->options[i] = new_tree_cache("server-id"); state->options[i]->value = - (unsigned char *)&state->ip->primary_address; - state->options[i]->len = - sizeof state->ip->primary_address; + (unsigned char *)&sin->sin_addr.s_addr; + state->options[i]->len = sizeof(sin->sin_addr.s_addr); state->options[i]->buf_size = state->options[i]->len; state->options[i]->timeout = -1; state->options[i]->tree = NULL; +#if 0 state->from.len = sizeof state->ip->primary_address; memcpy(state->from.iabuf, &state->ip->primary_address, state->from.len); } else { - /* Find the value of the server identifier... */ - if (!tree_evaluate(state->options[i])) - goto use_primary; - if (!state->options[i]->value || - (state->options[i]->len > - sizeof state->from.iabuf)) - goto use_primary; - state->from.len = state->options[i]->len; memcpy(state->from.iabuf, state->options[i]->value, state->from.len); +#endif } + state->from.len = sizeof state->ip->primary_address.sin_addr; + memcpy(state->from.iabuf, &state->ip->primary_address.sin_addr, + state->from.len); + /* * Do not ACK a REQUEST intended for another server. */ @@ -1279,10 +1274,10 @@ void dhcp_reply(struct lease *lease) { char ciaddrbuf[INET_ADDRSTRLEN]; - int bufs = 0, packet_length, i; + int bufs = 0, i; struct dhcp_packet raw; + struct packet outgoing; struct sockaddr_in to; - struct in_addr from; struct lease_state *state = lease->state; int nulltp, bootpp; u_int8_t *prl; @@ -1292,7 +1287,12 @@ dhcp_reply(struct lease *lease) fatalx("dhcp_reply was supplied lease with no state!"); /* Compose a response for the client... */ + memset(&outgoing, 0, sizeof outgoing); memset(&raw, 0, sizeof raw); + outgoing.raw = &raw; + outgoing.laddr = state->laddr; + outgoing.raddr = state->raddr; + outgoing.interface = state->ip; /* Copy in the filename if given; otherwise, flag the filename buffer as available for options. */ @@ -1338,8 +1338,9 @@ dhcp_reply(struct lease *lease) } /* Insert such options as will fit into the buffer. */ - packet_length = cons_options(NULL, &raw, state->max_message_size, - state->options, bufs, nulltp, bootpp, prl, prl_len); + outgoing.packet_length = cons_options(NULL, &raw, + state->max_message_size, state->options, + bufs, nulltp, bootpp, prl, prl_len); /* Having done the cons_options(), we can release the tree_cache entries. */ @@ -1362,7 +1363,7 @@ dhcp_reply(struct lease *lease) else if (lease->subnet->interface_address.len) memcpy(&raw.siaddr, lease->subnet->interface_address.iabuf, 4); else - raw.siaddr = state->ip->primary_address; + raw.siaddr = state->ip->primary_address.sin_addr; raw.giaddr = state->giaddr; @@ -1401,18 +1402,15 @@ dhcp_reply(struct lease *lease) /* Make sure outgoing packets are at least as big as a BOOTP packet. */ - if (packet_length < BOOTP_MIN_LEN) - packet_length = BOOTP_MIN_LEN; + if (outgoing.packet_length < BOOTP_MIN_LEN) + outgoing.packet_length = BOOTP_MIN_LEN; /* If this was gatewayed, send it back to the gateway... */ if (raw.giaddr.s_addr) { to.sin_addr = raw.giaddr; to.sin_port = server_port; - memcpy(&from, state->from.iabuf, sizeof from); - - (void) state->ip->send_packet(state->ip, &raw, - packet_length, from, &to, &state->haddr); + (void) state->ip->send_packet(&outgoing, lease, &to); free_lease_state(state, "dhcp_reply gateway"); lease->state = NULL; @@ -1450,13 +1448,9 @@ dhcp_reply(struct lease *lease) } else { to.sin_addr.s_addr = htonl(INADDR_BROADCAST); to.sin_port = client_port; - memset(&state->haddr, 0xff, sizeof state->haddr); } - memcpy(&from, state->from.iabuf, sizeof from); - - (void) state->ip->send_packet(state->ip, &raw, packet_length, - from, &to, &state->haddr); + (void) state->ip->send_packet(&outgoing, lease, &to); free_lease_state(state, "dhcp_reply"); lease->state = NULL; Index: dhcpd.c =================================================================== RCS file: /cvs/src/usr.sbin/dhcpd/dhcpd.c,v diff -u -p -r1.62 dhcpd.c --- dhcpd.c 10 Jun 2025 06:29:53 -0000 1.62 +++ dhcpd.c 13 Jun 2025 03:27:14 -0000 @@ -179,6 +179,7 @@ main(int argc, char *argv[]) if (!tmp) fatalx("calloc"); strlcpy(tmp->name, argv[0], sizeof(tmp->name)); + tmp->primary_address.sin_family = AF_UNSPEC; tmp->next = interfaces; interfaces = tmp; argc--; @@ -199,7 +200,7 @@ main(int argc, char *argv[]) exit(0); db_startup(); - if (!udpsockmode || argc > 0) + if (!udpsockmode || interfaces != NULL) discover_interfaces(); if (syncsend || syncrecv) { @@ -245,10 +246,20 @@ main(int argc, char *argv[]) icmp_startup(1, lease_pinged); + if (interfaces != NULL) { + /* do we need a listener for 255.255.255.255? */ + if (!udpsockmode || + (udpaddr.s_addr != htonl(INADDR_BROADCAST) && + udpaddr.s_addr != htonl(INADDR_ANY))) + broadcast_interface(); + + lladdr_helper(pw); /* this will fatal if it has trouble */ + } + if (chroot(pw->pw_dir) == -1) fatal("chroot %s", pw->pw_dir); if (chdir("/") == -1) - fatal("chdir(\"/\")"); + fatal("chdir %s", pw->pw_dir); if (setgroups(1, &pw->pw_gid) || setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) @@ -266,7 +277,7 @@ main(int argc, char *argv[]) dispatch(); /* not reached */ - exit(0); + return (0); } __dead void Index: dhcpd.h =================================================================== RCS file: /cvs/src/usr.sbin/dhcpd/dhcpd.h,v diff -u -p -r1.73 dhcpd.h --- dhcpd.h 10 Jun 2025 06:29:53 -0000 1.73 +++ dhcpd.h 13 Jun 2025 03:27:14 -0000 @@ -38,6 +38,33 @@ * Enterprises, see ``http://www.vix.com''. */ +#ifndef ISSET +#define ISSET(_v, _m) ((_v) & (_m)) +#endif + +#ifndef nitems +#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) +#endif + +#define CMSG_FOREACH(_cmsg, _msgp) \ + for ((_cmsg) = CMSG_FIRSTHDR((_msgp)); \ + (_cmsg) != NULL; \ + (_cmsg) = CMSG_NXTHDR((_msgp), (_cmsg))) + +static inline int +cmsg_match(const struct cmsghdr *cmsg, size_t len, int level, int type) +{ + return (cmsg->cmsg_len == CMSG_LEN(len) && + cmsg->cmsg_level == level && + cmsg->cmsg_type == type); +} + +#define CMSG_MATCH(_cmsg, _len, _level, _type) \ + cmsg_match((_cmsg), (_len), (_level), (_type)) + +struct passwd; +struct sockaddr_dl; + #define ifr_netmask ifr_addr #define HAVE_SA_LEN @@ -79,12 +106,11 @@ struct packet { int packet_length; int packet_type; int options_valid; - int client_port; - struct iaddr client_addr; + struct sockaddr_in raddr; + struct sockaddr_in laddr; + time_t expiry; struct interface_info *interface; /* Interface on which packet was received. */ - struct hardware *haddr; /* Physical link address - of local sender (maybe gateway). */ struct shared_network *shared_network; struct option_data options[256]; int got_requested_address; /* True if client sent the @@ -134,6 +160,8 @@ struct lease { }; struct lease_state { + struct lease_state *next; + struct interface_info *ip; time_t offered_expiry; @@ -144,6 +172,8 @@ struct lease_state { char *server_name; struct iaddr from; + struct sockaddr_in raddr; + struct sockaddr_in laddr; int max_message_size; u_int8_t *prl; @@ -162,7 +192,6 @@ struct lease_state { struct in_addr giaddr; u_int8_t hops; u_int8_t offer; - struct hardware haddr; }; #define ROOT_GROUP 0 @@ -254,7 +283,7 @@ struct interface_info { struct shared_network *shared_network; /* Networks connected to this interface. */ struct hardware hw_address; /* Its physical address. */ - struct in_addr primary_address; /* Primary interface address. */ + struct sockaddr_in primary_address; /* Primary interface address. */ char name[IFNAMSIZ]; /* Its name... */ int rfdesc; /* Its read file descriptor. */ int wfdesc; /* Its write file descriptor, if @@ -264,15 +293,13 @@ struct interface_info { size_t rbuf_offset; /* Current offset into buffer. */ size_t rbuf_len; /* Length of data in buffer. */ - struct ifreq *ifp; /* Pointer to ifreq struct. */ - int noifmedia; int errors; int dead; u_int16_t index; int is_udpsock; - ssize_t (*send_packet)(struct interface_info *, struct dhcp_packet *, - size_t, struct in_addr, struct sockaddr_in *, struct hardware *); + ssize_t (*send_packet)(struct packet *, struct lease *, + const struct sockaddr_in *); }; struct dhcpd_timeout { @@ -311,7 +338,8 @@ void parse_option_buffer(struct packet int cons_options(struct packet *, struct dhcp_packet *, int, struct tree_cache **, int, int, int, u_int8_t *, int); void do_packet(struct interface_info *, struct dhcp_packet *, int, - unsigned int, struct iaddr, struct hardware *); + const struct sockaddr_in *, const struct sockaddr_in *, + const struct sockaddr_dl *); /* dhcpd.c */ extern time_t cur_time; @@ -433,17 +461,17 @@ void free_tree_cache(struct tree_cache * char *print_hw_addr(int, int, unsigned char *); /* bpf.c */ -int if_register_bpf(struct interface_info *); void if_register_send(struct interface_info *); void if_register_receive(struct interface_info *); ssize_t receive_packet(struct interface_info *, unsigned char *, size_t, - struct sockaddr_in *, struct hardware *); + struct sockaddr_in *, struct sockaddr_in *, struct sockaddr_dl *); /* dispatch.c */ extern struct interface_info *interfaces; extern struct protocol *protocols; extern struct dhcpd_timeout *timeouts; void discover_interfaces(void); +void broadcast_interface(void); void dispatch(void); int locate_network(struct packet *); void got_one(struct protocol *); @@ -480,6 +508,7 @@ struct iaddr ip_addr(struct iaddr, struc u_int32_t host_addr(struct iaddr, struct iaddr); int addr_eq(struct iaddr, struct iaddr); char *piaddr(struct iaddr); +const char *saname(const struct sockaddr_in *); /* db.c */ int write_lease(struct lease *); @@ -493,8 +522,8 @@ void assemble_hw_header(struct interface void assemble_udp_ip_header(struct interface_info *, unsigned char *, int *, u_int32_t, u_int32_t, unsigned int, unsigned char *, int); ssize_t decode_hw_header(unsigned char *, u_int32_t, struct hardware *); -ssize_t decode_udp_ip_header(unsigned char *, u_int32_t, struct sockaddr_in *, - u_int16_t); +ssize_t decode_udp_ip_header(unsigned char *, u_int32_t, + struct sockaddr_in *, struct sockaddr_in *, u_int16_t); u_int32_t checksum(unsigned char *, u_int32_t, u_int32_t); u_int32_t wrapsum(u_int32_t); @@ -513,3 +542,7 @@ void pfmsg(char, struct lease *); /* udpsock.c */ void udpsock_startup(struct in_addr); + +/* sockets.c */ +void lladdr_helper(const struct passwd *); +void lladdr_del(struct lease *); Index: dispatch.c =================================================================== RCS file: /cvs/src/usr.sbin/dhcpd/dispatch.c,v diff -u -p -r1.50 dispatch.c --- dispatch.c 10 Jun 2025 06:29:53 -0000 1.50 +++ dispatch.c 13 Jun 2025 03:27:14 -0000 @@ -47,6 +47,7 @@ #include #include #include +#include #include @@ -65,7 +66,6 @@ #include "tree.h" #include "dhcpd.h" #include "log.h" -#include "sync.h" extern int rdomain; @@ -75,7 +75,8 @@ struct dhcpd_timeout *timeouts; static int interfaces_invalidated; static int interface_status(struct interface_info *ifinfo); -int get_rdomain(char *); + +void got_bcast(struct protocol *); /* Use getifaddrs() to get a list of all the attached interfaces. For each interface that's of type INET and not the loopback interface, @@ -89,9 +90,8 @@ discover_interfaces(void) struct interface_info *last, *next; struct subnet *subnet; struct shared_network *share; - struct sockaddr_in foo; + struct sockaddr_in *sin; int ir; - struct ifreq *tif; struct ifaddrs *ifap, *ifa; if (getifaddrs(&ifap) != 0) @@ -105,15 +105,6 @@ discover_interfaces(void) /* Cycle through the list of interfaces looking for IP addresses. */ for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { - /* - * See if this is the sort of interface we want to - * deal with. - */ - if ((ifa->ifa_flags & IFF_LOOPBACK) || - (ifa->ifa_flags & IFF_POINTOPOINT) || - (!(ifa->ifa_flags & IFF_BROADCAST))) - continue; - /* See if we've seen an interface that matches this one. */ for (tmp = interfaces; tmp; tmp = tmp->next) if (!strcmp(tmp->name, ifa->ifa_name)) @@ -142,37 +133,43 @@ discover_interfaces(void) struct if_data *ifi = ifa->ifa_data; struct sockaddr_dl *sdl; - if (rdomain != ifi->ifi_rdomain) + /* + * See if this is the sort of interface we want to + * deal with. + */ + if (ifi->ifi_type != IFT_ETHER) + continue; + + if (rdomain != ifi->ifi_rdomain) { + if (ir) { + fatalx("Interface %s " + "is not in rdomain %d", + tmp->name, rdomain); + } continue; + } sdl = (struct sockaddr_dl *)ifa->ifa_addr; tmp->index = sdl->sdl_index; tmp->hw_address.hlen = sdl->sdl_alen; - tmp->hw_address.htype = HTYPE_ETHER; /* XXX */ + tmp->hw_address.htype = HTYPE_ETHER; memcpy(tmp->hw_address.haddr, LLADDR(sdl), sdl->sdl_alen); } else if (ifa->ifa_addr->sa_family == AF_INET) { /* Get a pointer to the address... */ - memcpy(&foo, ifa->ifa_addr, sizeof(foo)); + sin = (struct sockaddr_in *)ifa->ifa_addr; /* We don't want the loopback interface. */ - if (foo.sin_addr.s_addr == htonl (INADDR_LOOPBACK)) + if (sin->sin_addr.s_addr == htonl(INADDR_LOOPBACK) || + sin->sin_addr.s_addr == htonl(INADDR_ANY)) continue; /* If this is the first real IP address we've found, keep a pointer to ifreq structure in which we found it. */ - if (!tmp->ifp) { - int len = (IFNAMSIZ + ifa->ifa_addr->sa_len); - tif = malloc(len); - if (!tif) - fatalx("no space to remember ifp."); - strlcpy(tif->ifr_name, ifa->ifa_name, - IFNAMSIZ); - memcpy(&tif->ifr_addr, ifa->ifa_addr, - ifa->ifa_addr->sa_len); - tmp->ifp = tif; - tmp->primary_address = foo.sin_addr; + if (tmp->primary_address.sin_family == AF_UNSPEC) { + tmp->primary_address = *sin; + tmp->primary_address.sin_port = server_port; } } } @@ -181,11 +178,10 @@ discover_interfaces(void) last = NULL; for (tmp = interfaces; tmp; tmp = next) { struct iaddr addr; - next = tmp->next; if (tmp->index == 0) { - log_warnx("Can't listen on %s - wrong rdomain", + log_warnx("Can't listen on %s - wrong type or rdomain.", tmp->name); /* Remove tmp from the list of interfaces. */ if (!last) @@ -195,7 +191,8 @@ discover_interfaces(void) continue; } - if (!tmp->ifp) { + sin = &tmp->primary_address; + if (sin->sin_family == AF_UNSPEC) { log_warnx("Can't listen on %s - it has no IP address.", tmp->name); /* Remove tmp from the list of interfaces. */ @@ -206,11 +203,9 @@ discover_interfaces(void) continue; } - memcpy(&foo, &tmp->ifp->ifr_addr, sizeof tmp->ifp->ifr_addr); - /* Grab the address... */ addr.len = 4; - memcpy(addr.iabuf, &foo.sin_addr.s_addr, addr.len); + memcpy(addr.iabuf, &sin->sin_addr.s_addr, addr.len); /* If there's a registered subnet for this address, connect it together... */ @@ -252,7 +247,7 @@ discover_interfaces(void) if (!tmp->shared_network) { log_warnx("Can't listen on %s - dhcpd.conf has no " "subnet declaration for %s.", tmp->name, - inet_ntoa(foo.sin_addr)); + inet_ntoa(sin->sin_addr)); /* Remove tmp from the list of interfaces. */ if (!last) interfaces = interfaces->next; @@ -274,7 +269,7 @@ discover_interfaces(void) */ subnet->interface_address.len = 4; memcpy(subnet->interface_address.iabuf, - &foo.sin_addr.s_addr, 4); + &sin->sin_addr.s_addr, 4); } } @@ -282,7 +277,7 @@ discover_interfaces(void) if_register_receive(tmp); if_register_send(tmp); log_info("Listening on %s (%s).", tmp->name, - inet_ntoa(foo.sin_addr)); + inet_ntoa(sin->sin_addr)); } if (interfaces == NULL) @@ -295,6 +290,30 @@ discover_interfaces(void) freeifaddrs(ifap); } +void +broadcast_interface(void) +{ + struct interface_info *bif; + + /* Create a listener for packets to the broadcast address */ + bif = calloc(1, sizeof(*bif)); + if (bif == NULL) + fatalx("Insufficient memory for broadcast interface"); + + strlcpy(bif->name, "broadcast", sizeof(bif->name)); + bif->primary_address.sin_family = AF_INET; + bif->primary_address.sin_addr.s_addr = htonl(INADDR_BROADCAST); + bif->primary_address.sin_port = server_port; + + if_register_receive(bif); + if_register_send(bif); + + if (shutdown(bif->wfdesc, SHUT_WR) == -1) + log_warn("broadcast write shutdown"); + + add_protocol(bif->name, bif->rfdesc, got_bcast, bif); +} + /* * Wait for packets to come in using poll(). When a packet comes in, * call receive_packet to receive the packet and possibly strip hardware @@ -308,7 +327,6 @@ dispatch(void) static struct pollfd *fds; static int nfds_max; time_t howlong; - int nifaces; for (nfds = 0, l = protocols; l; l = l->next) nfds++; @@ -350,15 +368,13 @@ another: to_msec = -1; /* Set up the descriptors to be polled. */ - nifaces = 0; for (i = 0, l = protocols; l; l = l->next) { if (l->handler == got_one) { struct interface_info *ip = l->local; if (ip->dead) { l->pfd = -1; continue; - } else - nifaces++; + } } fds[i].fd = l->fd; @@ -366,9 +382,6 @@ another: l->pfd = i++; } - if (nifaces == 0) - fatalx("No live interfaces to poll on - exiting."); - /* Wait for a packet or a timeout... */ switch (poll(fds, nfds, to_msec)) { case -1: @@ -392,13 +405,55 @@ another: } } +void +got_bcast(struct protocol *l) +{ + struct sockaddr_in src = { .sin_family = AF_INET }; + struct sockaddr_in dst = { .sin_family = AF_INET }; + struct sockaddr_dl sdl = { .sdl_family = AF_UNSPEC }; + ssize_t result; + union { + unsigned char packbuf[4095]; + struct dhcp_packet packet; + } u; + struct interface_info *ip = l->local; + + memset(&u, 0, sizeof(u)); + + result = receive_packet(ip, u.packbuf, sizeof(u), + &src, &dst, &sdl); + if (result == -1) { + log_warn("broadcast receive_packet failed"); + return; + } + if (result == 0) + return; + + /* + * We need the RECVIF information to figure out where it + * actually came from. + */ + if (sdl.sdl_family == AF_UNSPEC || sdl.sdl_index == 0) + return; + + for (ip = interfaces; ip != NULL; ip = ip->next) { + if (ip->index == sdl.sdl_index) + break; + } + /* drop broadcast packet from unknown interface */ + if (ip == NULL) { + return; + } + + do_packet(ip, &u.packet, result, &src, &dst, &sdl); +} void got_one(struct protocol *l) { - struct sockaddr_in from; - struct hardware hfrom; - struct iaddr ifrom; + struct sockaddr_in src = { .sin_family = AF_INET }; + struct sockaddr_in dst = { .sin_family = AF_INET }; + struct sockaddr_dl sdl = { .sdl_family = AF_UNSPEC }; ssize_t result; union { unsigned char packbuf[4095]; @@ -408,8 +463,8 @@ got_one(struct protocol *l) memset(&u, 0, sizeof(u)); - if ((result = receive_packet(ip, u.packbuf, sizeof u, - &from, &hfrom)) == -1) { + if ((result = receive_packet(ip, u.packbuf, sizeof(u), + &src, &dst, &sdl)) == -1) { log_warn("receive_packet failed on %s", ip->name); ip->errors++; if ((!interface_status(ip)) || @@ -428,10 +483,7 @@ got_one(struct protocol *l) if (result == 0) return; - ifrom.len = 4; - memcpy(ifrom.iabuf, &from.sin_addr, ifrom.len); - - do_packet(ip, &u.packet, result, from.sin_port, ifrom, &hfrom); + do_packet(ip, &u.packet, result, &src, &dst, &sdl); } int Index: inet.c =================================================================== RCS file: /cvs/src/usr.sbin/dhcpd/inet.c,v diff -u -p -r1.6 inet.c --- inet.c 6 Feb 2016 23:50:10 -0000 1.6 +++ inet.c 13 Jun 2025 03:27:14 -0000 @@ -166,3 +166,15 @@ piaddr(struct iaddr addr) } return (pbuf); } + +const char * +saname(const struct sockaddr_in *sin) +{ + static char pbuf[32]; + + if (inet_ntop(AF_INET, &sin->sin_addr.s_addr, + pbuf, sizeof(pbuf)) == NULL) + return (""); + + return (pbuf); +} Index: memory.c =================================================================== RCS file: /cvs/src/usr.sbin/dhcpd/memory.c,v diff -u -p -r1.31 memory.c --- memory.c 28 Jan 2022 06:33:27 -0000 1.31 +++ memory.c 13 Jun 2025 03:27:14 -0000 @@ -644,6 +644,7 @@ release_lease(struct lease *lease) lt = *lease; if (lt.ends > cur_time) { lt.ends = cur_time; + lladdr_del(lease); supersede_lease(lease, <, 1); log_info("Released lease for IP address %s", piaddr(lease->ip_addr)); @@ -662,6 +663,8 @@ abandon_lease(struct lease *lease, char { struct lease lt; time_t abtime; + + lladdr_del(lease); abtime = lease->subnet->group->default_lease_time; lease->flags |= ABANDONED_LEASE; Index: options.c =================================================================== RCS file: /cvs/src/usr.sbin/dhcpd/options.c,v diff -u -p -r1.35 options.c --- options.c 13 Feb 2017 22:33:39 -0000 1.35 +++ options.c 13 Jun 2025 03:27:14 -0000 @@ -527,8 +527,10 @@ zapfrags: } void -do_packet(struct interface_info *interface, struct dhcp_packet *packet, - int len, unsigned int from_port, struct iaddr from, struct hardware *hfrom) +do_packet(struct interface_info *interface, + struct dhcp_packet *packet, int len, + const struct sockaddr_in *raddr, const struct sockaddr_in *laddr, + const struct sockaddr_dl *sdl) { struct packet tp; int i; @@ -541,10 +543,9 @@ do_packet(struct interface_info *interfa memset(&tp, 0, sizeof(tp)); tp.raw = packet; tp.packet_length = len; - tp.client_port = from_port; - tp.client_addr = from; + tp.raddr = *raddr; + tp.laddr = *laddr; tp.interface = interface; - tp.haddr = hfrom; parse_options(&tp); if (tp.options_valid && Index: packet.c =================================================================== RCS file: /cvs/src/usr.sbin/dhcpd/packet.c,v diff -u -p -r1.15 packet.c --- packet.c 7 Feb 2025 21:48:26 -0000 1.15 +++ packet.c 13 Jun 2025 03:27:14 -0000 @@ -169,7 +169,7 @@ decode_hw_header(unsigned char *buf, u_i ssize_t decode_udp_ip_header(unsigned char *buf, u_int32_t buflen, - struct sockaddr_in *from, u_int16_t csumflags) + struct sockaddr_in *src, struct sockaddr_in *dst, u_int16_t csumflags) { struct ip *ip; struct udphdr *udp; @@ -206,7 +206,8 @@ decode_udp_ip_header(unsigned char *buf, return (-1); } - memcpy(&from->sin_addr, &ip->ip_src, sizeof(from->sin_addr)); + memcpy(&src->sin_addr, &ip->ip_src, sizeof(src->sin_addr)); + memcpy(&dst->sin_addr, &ip->ip_dst, sizeof(dst->sin_addr)); #ifdef DEBUG if (ntohs(ip->ip_len) != buflen) @@ -276,7 +277,8 @@ decode_udp_ip_header(unsigned char *buf, } } - memcpy(&from->sin_port, &udp->uh_sport, sizeof(udp->uh_sport)); + memcpy(&src->sin_port, &udp->uh_sport, sizeof(src->sin_port)); + memcpy(&dst->sin_port, &udp->uh_sport, sizeof(dst->sin_port)); return (ip_len + sizeof(*udp)); } Index: sockets.c =================================================================== RCS file: sockets.c diff -N sockets.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sockets.c 13 Jun 2025 03:27:14 -0000 @@ -0,0 +1,563 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2025 The University of Queensland + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dhcp.h" +#include "tree.h" +#include "dhcpd.h" +#include "log.h" + +extern int rdomain; + +ssize_t send_packet(struct packet *, struct lease *, + const struct sockaddr_in *); +void lladdr_reply(struct protocol *); + +int rtseq; +int llhsock = -1; + +/* + * Called by get_interface_list for each interface that's discovered. + */ + +void +if_register_receive(struct interface_info *info) +{ + struct sockaddr_in *sin = &info->primary_address; + struct ip_mreqn mreqn = { + .imr_ifindex = info->index, + }; + int flag; + u_char loop; + int s; + + if (sin->sin_addr.s_addr == htonl(INADDR_ANY)) { + fatalx("Can't bind to %s for %s", inet_ntoa(sin->sin_addr), + info->name); + } + + s = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP); + if (s == -1) + fatal("Can't create socket for %s", info->name); + + flag = 1; + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) == -1) + fatal("Can't enable SO_REUSEADDR"); + + flag = 1; + if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &flag, sizeof(flag)) == -1) + fatal("Can't enable SO_BROADCAST"); + + if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, + &mreqn, sizeof(mreqn)) == -1) + fatal("Can't set IP_MULTICAST_IF"); + + flag = 1; + if (setsockopt(s, IPPROTO_IP, IP_RECVIF, &flag, sizeof(flag)) == -1) + fatal("Can't enable IP_RECVIF"); + + loop = 0; + if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, + &loop, sizeof(loop)) == -1) + fatal("Can't disable IP_MULTICAST_LOOP"); + + if (bind(s, (struct sockaddr *)sin, sizeof(*sin)) == -1) { + fatal("Can't bind to %s for %s", inet_ntoa(sin->sin_addr), + info->name); + } + + info->rfdesc = s; + info->send_packet = send_packet; +} + +void +if_register_send(struct interface_info *info) +{ + info->wfdesc = info->rfdesc; +} + +struct lladdr_deferred { + SIMPLEQ_ENTRY(lladdr_deferred) entry; + struct interface_info *interface; + struct dhcp_packet *raw; + size_t len; + struct sockaddr_in dst; + unsigned int seq; +}; + +SIMPLEQ_HEAD(lladdr_deferrals, lladdr_deferred); +struct lladdr_deferrals llds = SIMPLEQ_HEAD_INITIALIZER(llds); + +#ifndef roundup +#define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) +#endif + +#define RTMSG_SPACE(_s) roundup(_s, sizeof(long)) +#define RTMSG_NEXT(_s) (void *)((uint8_t *)(_s) + RTMSG_SPACE(sizeof(*_s))) + +ssize_t +send_packet(struct packet *packet, struct lease *lease, + const struct sockaddr_in *dst) +{ + struct interface_info *interface = packet->interface; + struct dhcp_packet *raw = packet->raw; + ssize_t result; + + /* should we fake an arp entry for this? */ + if (lease != NULL && + raw->giaddr.s_addr == htonl(INADDR_ANY) && + !ISSET(raw->flags, htonl(BOOTP_BROADCAST))) { + struct rt_msghdr *rtm; + struct sockaddr_in *sin; + struct sockaddr_dl *sdl; + struct lladdr_deferred *d; + + uint8_t rtmsg[RTMSG_SPACE(sizeof(*rtm)) + + RTMSG_SPACE(sizeof(*sin)) + /* RTA_DST */ + RTMSG_SPACE(sizeof(*sdl)) + /* RTA_GATEWAY */ + RTMSG_SPACE(sizeof(*sin)) + /* RTA_NETMASK */ + RTMSG_SPACE(sizeof(*sdl)) + /* RTA_IFP */ + RTMSG_SPACE(sizeof(*sin))]; /* RTA_IFA */ + + memset(rtmsg, 0, sizeof(rtmsg)); + + rtm = (struct rt_msghdr *)rtmsg; + rtm->rtm_msglen = sizeof(rtmsg); + rtm->rtm_version = RTM_VERSION; + rtm->rtm_type = RTM_ADD; + rtm->rtm_hdrlen = sizeof(*rtm); + rtm->rtm_index = interface->index; + rtm->rtm_tableid = rdomain; + rtm->rtm_priority = RTP_CONNECTED - 1; /* XXX */ + rtm->rtm_flags = + RTF_UP | RTF_HOST | RTF_LLINFO; + rtm->rtm_addrs = + RTA_DST | RTA_GATEWAY | RTA_NETMASK | RTA_IFP | RTA_IFA; + rtm->rtm_seq = ++rtseq; + + if (lease->ends != 0) { + rtm->rtm_inits = RTV_EXPIRE; + rtm->rtm_rmx.rmx_expire = lease->ends + 1; + } + + /* DST */ + sin = RTMSG_NEXT(rtm); + sin->sin_len = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_addr = dst->sin_addr; + + /* GATEWAY */ + sdl = RTMSG_NEXT(sin); + sdl->sdl_len = sizeof(*sdl); + sdl->sdl_family = AF_LINK; + sdl->sdl_alen = raw->hlen; + memcpy(LLADDR(sdl), raw->chaddr, sdl->sdl_alen); + + /* NETMASK */ + sin = RTMSG_NEXT(sdl); + sin->sin_len = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = htonl(0xffffffff); + + /* IFP */ + sdl = RTMSG_NEXT(sin); + sdl->sdl_len = sizeof(*sdl); + sdl->sdl_family = AF_LINK; + sdl->sdl_index = interface->index; + sdl->sdl_type = IFT_ETHER; + sdl->sdl_nlen = strlen(interface->name); + memcpy(sdl->sdl_data, interface->name, sdl->sdl_nlen); + sdl->sdl_alen = interface->hw_address.hlen; + memcpy(LLADDR(sdl), interface->hw_address.haddr, sdl->sdl_alen); + + /* IFA */ + sin = RTMSG_NEXT(sdl); + *sin = interface->primary_address; + + d = malloc(sizeof(*d) + packet->packet_length); + if (d == NULL) { + log_warn("cannot defer packet for lladdr help"); + return (-1); + } + + result = send(llhsock, rtmsg, sizeof(rtmsg), 0); + if (result == -1) { + log_warn("lladdr helper send"); + return (-1); + } + + d->interface = interface; + d->raw = (struct dhcp_packet *)(d + 1); + d->len = packet->packet_length; + d->dst = *dst; + d->seq = rtm->rtm_seq; + + memcpy(d->raw, raw, d->len); + + SIMPLEQ_INSERT_TAIL(&llds, d, entry); + return (0); + } + + result = sendto(interface->wfdesc, raw, packet->packet_length, 0, + (const struct sockaddr *)dst, sizeof(*dst)); + if (result == -1) + log_warn("send_packet"); + return (result); +} + +void +lladdr_reply(struct protocol *protocol) +{ + struct rt_msghdr rtm; + struct lladdr_deferred *d; + struct interface_info *interface; + ssize_t result; + + result = recv(llhsock, &rtm, sizeof(rtm), 0); + if (result == -1) { + log_info("lladdr helper reply"); + return; + } + + log_debug("got a reply from lladdr helper for %u", rtm.rtm_seq); + + d = SIMPLEQ_FIRST(&llds); + SIMPLEQ_REMOVE_HEAD(&llds, entry); + + interface = d->interface; + + result = sendto(interface->wfdesc, d->raw, d->len, 0, + (const struct sockaddr *)&d->dst, sizeof(d->dst)); + free(d); + if (result == -1) + log_warn("send_packet"); +} + +ssize_t +receive_packet(struct interface_info *interface, unsigned char *buf, + size_t len, struct sockaddr_in *src, struct sockaddr_in *dst, + struct sockaddr_dl *sdl) +{ + struct iovec iov[1] = { + { .iov_base = buf, .iov_len = len }, + }; + union { + struct cmsghdr hdr; + char buf[CMSG_SPACE(sizeof(*sdl))]; + } cmsgbuf; + struct msghdr msg = { + .msg_name = src, + .msg_namelen = sizeof(*src), + .msg_iov = iov, + .msg_iovlen = nitems(iov), + .msg_control = &cmsgbuf.buf, + .msg_controllen = sizeof(cmsgbuf.buf), + }; + struct cmsghdr *cmsg; + ssize_t result; + + result = recvmsg(interface->rfdesc, &msg, 0); + if (result == -1) { + switch (errno) { + case EINTR: + case EAGAIN: + return (0); + default: + break; + } + return (result); + } + + *dst = interface->primary_address; + + CMSG_FOREACH(cmsg, &msg) { + if (CMSG_MATCH(cmsg, sizeof(*sdl), IPPROTO_IP, IP_RECVIF)) + *sdl = *(struct sockaddr_dl *)CMSG_DATA(cmsg); + } + + return (result); +} + +void +lladdr_del(struct lease *l) +{ + struct rt_msghdr *rtm; + struct sockaddr_in *sin; + struct sockaddr_dl *sdl; + + uint8_t rtmsg[RTMSG_SPACE(sizeof(*rtm)) + + RTMSG_SPACE(sizeof(*sin)) + /* RTA_DST */ + RTMSG_SPACE(sizeof(*sdl)) + /* RTA_GATEWAY */ + RTMSG_SPACE(sizeof(*sin)) + /* RTA_NETMASK */ + RTMSG_SPACE(sizeof(*sdl)) + /* RTA_IFP */ + RTMSG_SPACE(sizeof(*sin))]; /* RTA_IFA */ + ssize_t result; + + struct interface_info *interface; + struct lease_state *s = l->state; + + if (llhsock == -1 || s == NULL) + return; + + /* + * Avoid dereferencing s->ip until we're sure it's a local interface. + */ + for (interface = interfaces; interface != NULL; + interface = interface->next) { + if (s->ip == interface) + break; + } + if (interface == NULL) + return; + + memset(rtmsg, 0, sizeof(rtmsg)); + + rtm = (struct rt_msghdr *)rtmsg; + rtm->rtm_msglen = sizeof(rtmsg); + rtm->rtm_version = RTM_VERSION; + rtm->rtm_type = RTM_DELETE; + rtm->rtm_hdrlen = sizeof(*rtm); + rtm->rtm_index = interface->index; + rtm->rtm_tableid = rdomain; + rtm->rtm_priority = RTP_CONNECTED - 1; /* XXX */ + rtm->rtm_flags = + RTF_UP | RTF_HOST | RTF_LLINFO; + rtm->rtm_addrs = + RTA_DST | RTA_GATEWAY | RTA_NETMASK | RTA_IFP | RTA_IFA; + rtm->rtm_seq = ++rtseq; + + /* DST */ + sin = RTMSG_NEXT(rtm); + sin->sin_len = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_addr = s->ciaddr; + + /* GATEWAY */ + sdl = RTMSG_NEXT(sin); + sdl->sdl_len = sizeof(*sdl); + sdl->sdl_family = AF_LINK; + sdl->sdl_alen = l->hardware_addr.hlen; + memcpy(LLADDR(sdl), l->hardware_addr.haddr, sdl->sdl_alen); + + /* NETMASK */ + sin = RTMSG_NEXT(sdl); + sin->sin_len = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_addr.s_addr = htonl(0xffffffff); + + /* IFP */ + sdl = RTMSG_NEXT(sin); + sdl->sdl_len = sizeof(*sdl); + sdl->sdl_family = AF_LINK; + sdl->sdl_index = interface->index; + sdl->sdl_type = IFT_ETHER; + sdl->sdl_nlen = strlen(interface->name); + memcpy(sdl->sdl_data, interface->name, sdl->sdl_nlen); + sdl->sdl_alen = interface->hw_address.hlen; + memcpy(LLADDR(sdl), interface->hw_address.haddr, sdl->sdl_alen); + + /* IFA */ + sin = RTMSG_NEXT(sdl); + *sin = interface->primary_address; + + result = send(llhsock, rtmsg, sizeof(rtmsg), 0); + if (result == -1) + log_warn("lladdr del send"); +} + +/* + * lladdr helper process code + */ + +static void +lladdr_rtmsg(int fd, int rtsock) +{ + char rtmsg[1024]; /* how long is a piece of string? */ + struct rt_msghdr *rtm = (struct rt_msghdr *)rtmsg; + ssize_t rv; + size_t len; + unsigned int seq; + + rv = recv(fd, rtmsg, sizeof(rtmsg), 0); + if (rv == -1) { + if (errno != EINTR && errno != EAGAIN) + log_warn("lladdr helper request"); + return; + } + len = rv; + + if (rtm->rtm_version != RTM_VERSION) + fatalx("lladdr helper: request rtm_version invalid"); + if (rtm->rtm_type != RTM_ADD && rtm->rtm_type != RTM_DELETE) + fatalx("lladdr helper: request rtm_type invalid"); + if (rtm->rtm_hdrlen != sizeof(*rtm)) + fatalx("lladdr helper: request rtm_hdrlen invalid"); + if (rtm->rtm_msglen >= sizeof(rtmsg)) + fatalx("lladdr helper: request rtm_msglen is too long"); + if (rtm->rtm_msglen != len) + fatalx("lladdr helper: request rtm_msglen is wrong"); + if (rtm->rtm_flags & + ~(RTF_UP | RTF_HOST | RTF_LLINFO | RTF_STATIC | RTF_MPATH)) + fatalx("lladdr helper: request rtm_flags has unexpected bits"); + if (rtm->rtm_addrs & + ~(RTA_DST | RTA_GATEWAY | RTA_NETMASK | RTA_IFP | RTA_IFA)) + fatalx("lladdr helper: request rtm_addrs has unexpected bits"); + if (rtm->rtm_inits & ~(RTV_EXPIRE)) + fatalx("lladdr helper: request rtm_inits has unexpected bits"); + + /* XXX check more? */ + + seq = rtm->rtm_seq; + log_debug("lladdr helper: got %u to push into the kernel", seq); + + rtm->rtm_errno = 0; + rv = send(rtsock, rtmsg, len, 0); + if (rv == -1) { + rtm->rtm_errno = errno; + /* + * IF this is a dynamic lease, we've tried to ping + * the IP to see if anything is camping on it already. + * Sending the ping packet has triggered ARP in the + * kernel, which creates a route entry to handle the + * state of the ARP resolution. If there's no pong, + * then we'll end up here trying to create the LLINFO + * route entry and failing because it already exists. + * Switch to trying to change the existing entry. + */ + if (errno == EEXIST && rtm->rtm_type == RTM_ADD) { + rtm->rtm_type = RTM_CHANGE; + /* We want this flag clear if the kernel has it set. */ + rtm->rtm_fmask = RTF_REJECT; + + rv = send(rtsock, rtmsg, len, 0); + if (len == -1) + rtm->rtm_errno = errno; + } + } + + /* Report back to the main process what happened */ + if (rtm->rtm_type == RTM_DELETE) + return; + + rv = send(fd, rtm, sizeof(*rtm), 0); + if (rv == -1) + fatal("lladdr helper: unable to reply to dhcpd"); +} + +void +lladdr_helper(const struct passwd *pw) +{ + int llhpair[2]; + int rtsock; + + if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK, 0, + llhpair) == -1) + fatal("lladdr handler socketpair"); + + switch (fork()) { + case -1: + fatal("lladdr hander fork"); + /* NOTREACHED */ + + case 0: /* child */ + break; + + default: /* parent */ + close(llhpair[0]); + llhsock = llhpair[1]; + add_protocol("lladdr", llhsock, lladdr_reply, NULL); + return; + } + + llhsock = 3; /* different address space to the parent */ + if (dup2(llhpair[0], llhsock) == -1) + fatal("lladdr handler dup2"); + if (closefrom(llhsock + 1) == -1) { + if (errno != EBADF) + fatal("lladdr helper closefrom"); + } + + rtsock = socket(AF_ROUTE, SOCK_RAW, 0); /* we can block on rtsock */ + if (rtsock == -1) + fatal("route socket"); + + if (chroot(pw->pw_dir) == -1) + fatal("lladdr helper chroot %s", pw->pw_dir); + if (chdir("/") == -1) + fatal("lladdr helper chdir %s", pw->pw_dir); + + /* we can drop some privs */ + if (setgroups(1, &pw->pw_gid) || + setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid)) + fatal("lladdr helper can't drop privileges"); + + /* no filesystem visibility */ + if (unveil("/", "") == -1) + fatal("lladdr helper unveil %s", pw->pw_dir); + if (unveil(NULL, NULL) == -1) + fatal("router helper unveil"); + +#if 0 + setproctitle("lladdr helper"); +#else + setproctitle("arpendage"); +#endif + + for (;;) { + struct pollfd pfd[] = { + { .fd = llhsock, .events = POLLIN }, + }; + int nfds = poll(pfd, nitems(pfd), -1); + if (nfds == -1) { + if (errno != EINTR) + log_warn("poll"); + continue; + } + if (nfds == 0) + continue; + + if (pfd[0].revents & POLLHUP) + fatalx("lladdr helper: ipc socket closed"); + + if (pfd[0].revents & POLLIN) + lladdr_rtmsg(llhsock, rtsock); + } + + exit(1); +} Index: udpsock.c =================================================================== RCS file: /cvs/src/usr.sbin/dhcpd/udpsock.c,v diff -u -p -r1.12 udpsock.c --- udpsock.c 22 May 2025 04:24:11 -0000 1.12 +++ udpsock.c 13 Jun 2025 03:27:14 -0000 @@ -39,11 +39,12 @@ #include "dhcpd.h" #include "log.h" -void udpsock_handler (struct protocol *); -ssize_t udpsock_send_packet(struct interface_info *, struct dhcp_packet *, - size_t, struct in_addr, struct sockaddr_in *, struct hardware *); +void udpsock_handler(struct protocol *); +ssize_t udpsock_send_packet(struct packet *, struct lease *, + const struct sockaddr_in *); struct udpsock { + struct in_addr bindaddr; int sock; }; @@ -51,132 +52,198 @@ void udpsock_startup(struct in_addr bindaddr) { int sock, onoff; - struct sockaddr_in sin4; + struct sockaddr_in sin = { + .sin_family = AF_INET, + .sin_addr = bindaddr, + .sin_port = server_port, + }; struct udpsock *udpsock; if ((udpsock = calloc(1, sizeof(struct udpsock))) == NULL) fatal("could not create udpsock"); - memset(&sin4, 0, sizeof(sin4)); if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) fatal("creating a socket failed for udp"); + if (bindaddr.s_addr == htonl(INADDR_ANY) || + bindaddr.s_addr == htonl(INADDR_BROADCAST)) { + onoff = 1; + if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, + &onoff, sizeof(onoff)) == -1) + fatal("Can't enable SO_BROADCAST for udp"); + + onoff = 1; + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, + &onoff, sizeof(onoff)) == -1) + fatal("Can't enable SO_REUSEADDR for udp"); + } + onoff = 1; - if (setsockopt(sock, IPPROTO_IP, IP_RECVIF, &onoff, sizeof(onoff)) != - 0) + if (setsockopt(sock, IPPROTO_IP, IP_RECVIF, + &onoff, sizeof(onoff)) != 0) fatal("setsocketopt IP_RECVIF failed for udp"); - sin4.sin_family = AF_INET; - sin4.sin_len = sizeof(sin4); - sin4.sin_addr = bindaddr; - sin4.sin_port = server_port; + onoff = 1; + if (setsockopt(sock, IPPROTO_IP, IP_RECVDSTADDR, + &onoff, sizeof(onoff)) != 0) + fatal("setsocketopt IP_RECVDSTADDR failed for udp"); + +#if 0 + onoff = 1; + if (setsockopt(sock, IPPROTO_IP, IP_RECVDSTPORT, + &onoff, sizeof(onoff)) != 0) + fatal("setsocketopt IP_RECVDSTPORT failed for udp"); +#endif - if (bind(sock, (struct sockaddr *)&sin4, sizeof(sin4)) != 0) - fatal("bind failed for udp"); + if (bind(sock, (struct sockaddr *)&sin, sizeof(sin)) == -1) { + fatal("Bind failed for %s:%d/udp.", inet_ntoa(sin.sin_addr), + ntohs(server_port)); + } + udpsock->bindaddr = bindaddr; udpsock->sock = sock; add_protocol("udp", sock, udpsock_handler, udpsock); - log_info("Listening on %s:%d/udp.", inet_ntoa(sin4.sin_addr), + log_info("Listening on %s:%d/udp.", inet_ntoa(sin.sin_addr), ntohs(server_port)); } void udpsock_handler(struct protocol *protocol) { - int sockio; - char cbuf[256], ifname[IF_NAMESIZE]; - ssize_t len; - struct udpsock *udpsock = protocol->local; - struct msghdr m; - struct cmsghdr *cm; - struct iovec iov[1]; - struct sockaddr_storage ss; - struct sockaddr_in *sin4; - struct sockaddr_dl *sdl = NULL; - struct interface_info iface; - struct iaddr from, addr; - unsigned char packetbuf[4095]; + struct udpsock *udpsock = protocol->local; + struct interface_info iface = { + .primary_address = { + .sin_family = AF_INET, + .sin_addr = udpsock->bindaddr, + .sin_port = server_port, + }, + + .is_udpsock = 1, + .wfdesc = udpsock->sock, + .send_packet = udpsock_send_packet, + }; + struct interface_info *interface = NULL; + + unsigned char packetbuf[4096]; + struct iovec iov[1] = { + { .iov_base = packetbuf, .iov_len = sizeof(packetbuf) }, + }; + struct sockaddr_dl *sdl = NULL; + struct sockaddr_in raddr; + struct sockaddr_in *laddr = &iface.primary_address; + union { + struct cmsghdr hdr; + char buf[CMSG_SPACE(sizeof(*sdl)) + + CMSG_SPACE(sizeof(laddr->sin_addr)) + + CMSG_SPACE(sizeof(laddr->sin_port))]; + } cmsgbuf; + struct cmsghdr *cmsg; + struct msghdr msg = { + .msg_name = &raddr, + .msg_namelen = sizeof(raddr), + .msg_iov = iov, + .msg_iovlen = nitems(iov), + .msg_control = &cmsgbuf.buf, + .msg_controllen = sizeof(cmsgbuf.buf), + }; + ssize_t result; + struct dhcp_packet *packet = (struct dhcp_packet *)packetbuf; - struct hardware hw; - struct ifreq ifr; struct subnet *subnet; + struct iaddr addr; - memset(&hw, 0, sizeof(hw)); - - iov[0].iov_base = packetbuf; - iov[0].iov_len = sizeof(packetbuf); - memset(&m, 0, sizeof(m)); - m.msg_name = &ss; - m.msg_namelen = sizeof(ss); - m.msg_iov = iov; - m.msg_iovlen = 1; - m.msg_control = cbuf; - m.msg_controllen = sizeof(cbuf); - - memset(&iface, 0, sizeof(iface)); - if ((len = recvmsg(udpsock->sock, &m, 0)) == -1) { + result = recvmsg(udpsock->sock, &msg, 0); + if (result == -1) { log_warn("receiving a DHCP message failed"); return; } - if (ss.ss_family != AF_INET) { - log_warnx("received DHCP message is not AF_INET"); - return; - } - sin4 = (struct sockaddr_in *)&ss; - for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&m); - m.msg_controllen != 0 && cm; - cm = (struct cmsghdr *)CMSG_NXTHDR(&m, cm)) { - if (cm->cmsg_level == IPPROTO_IP && - cm->cmsg_type == IP_RECVIF) - sdl = (struct sockaddr_dl *)CMSG_DATA(cm); - } - if (sdl == NULL) { - log_warnx("could not get the received interface by IP_RECVIF"); - return; - } - if_indextoname(sdl->sdl_index, ifname); - if ((sockio = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { - log_warn("socket creation failed"); - return; - } - strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); - if (ioctl(sockio, SIOCGIFADDR, &ifr, sizeof(ifr)) == -1) { - log_warn("Failed to get address for %s", ifname); - close(sockio); + if (raddr.sin_family != AF_INET) { + log_warnx("received DHCP message is not AF_INET"); return; } - close(sockio); - if (ifr.ifr_addr.sa_family != AF_INET) - return; + CMSG_FOREACH(cmsg, &msg) { + if (CMSG_MATCH(cmsg, + sizeof(*sdl), IPPROTO_IP, IP_RECVIF)) + sdl = (struct sockaddr_dl *)CMSG_DATA(cmsg); + else if (CMSG_MATCH(cmsg, + sizeof(laddr->sin_addr), IPPROTO_IP, IP_RECVDSTADDR)) + laddr->sin_addr = *(struct in_addr *)CMSG_DATA(cmsg); + else if (CMSG_MATCH(cmsg, + sizeof(laddr->sin_port), IPPROTO_IP, IP_RECVDSTPORT)) + laddr->sin_port = *(in_port_t *)CMSG_DATA(cmsg); + } + + /* + * We might be handling broadcasts for real interfaces. + */ + if (laddr->sin_addr.s_addr == htonl(INADDR_BROADCAST) && + sdl != NULL && + sdl->sdl_family == AF_LINK && + sdl->sdl_index != 0) { + struct interface_info *ip; + + for (ip = interfaces; ip != NULL; ip = ip->next) { + if (ip->index == sdl->sdl_index) { + interface = ip; + break; + } + } + } + + if (interface == NULL) { + addr.len = sizeof(raddr.sin_addr.s_addr); + memcpy(&addr.iabuf, &raddr.sin_addr.s_addr, addr.len); + + if ((subnet = find_subnet(addr)) == NULL) + return; + iface.shared_network = subnet->shared_network; - iface.is_udpsock = 1; - iface.send_packet = udpsock_send_packet; - iface.wfdesc = udpsock->sock; - iface.ifp = 𝔦 - iface.index = sdl->sdl_index; - iface.primary_address = - ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr; - strlcpy(iface.name, ifname, sizeof(iface.name)); + inet_ntop(AF_INET, &raddr.sin_addr, + iface.name, sizeof(iface.name)); - addr.len = 4; - memcpy(&addr.iabuf, &iface.primary_address, addr.len); + interface = &iface; + } - if ((subnet = find_subnet(addr)) == NULL) - return; - iface.shared_network = subnet->shared_network ; - from.len = 4; - memcpy(&from.iabuf, &sin4->sin_addr, from.len); - do_packet(&iface, packet, len, sin4->sin_port, from, &hw); + do_packet(interface, packet, result, &raddr, laddr, sdl); } ssize_t -udpsock_send_packet(struct interface_info *interface, struct dhcp_packet *raw, - size_t len, struct in_addr from, struct sockaddr_in *to, - struct hardware *hto) +udpsock_send_packet(struct packet *packet, struct lease *lease, + const struct sockaddr_in *dstp) { - return (sendto(interface->wfdesc, raw, len, 0, (struct sockaddr *)to, - sizeof(struct sockaddr_in))); + struct interface_info *interface = packet->interface; + const struct sockaddr_in *laddr = &packet->laddr; + + struct iovec iov[1] = { + { .iov_base = packet->raw, .iov_len = packet->packet_length }, + }; + union { + struct cmsghdr hdr; + char buf[CMSG_SPACE(sizeof(laddr->sin_addr))]; + } cmsgbuf; + struct cmsghdr *cmsg; + struct sockaddr_in dst = *dstp; /* avoid dropping const below */ + struct msghdr msg = { + .msg_name = &dst, + .msg_namelen = sizeof(dst), + .msg_iov = iov, + .msg_iovlen = nitems(iov), + }; + + if (laddr != NULL && + laddr->sin_addr.s_addr != htonl(INADDR_BROADCAST)) { + msg.msg_control = &cmsgbuf.buf; + msg.msg_controllen = sizeof(cmsgbuf.buf); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(sizeof(laddr->sin_addr)); + cmsg->cmsg_level = IPPROTO_IP; + cmsg->cmsg_type = IP_SENDSRCADDR; + *(struct in_addr *)CMSG_DATA(cmsg) = laddr->sin_addr; + } + + return sendmsg(interface->wfdesc, &msg, 0); }