On Thu, 10 Jul 2014 10:17:29 +0200 (CEST)
YASUOKA Masahiko <[email protected]> wrote:
> On Wed, 9 Jul 2014 16:57:44 +0200
> Kenneth Westerback <[email protected]> wrote:
>> On 9 July 2014 16:23, YASUOKA Masahiko <[email protected]> wrote:
>>> Some users of npppd(8) want to use dhcpd(8) on tunneling interface to
>>> provide additional routing entries and so on to VPN clients. So I'd
>>> like to make the dhcpd work on the DLT_LOOP interfaces.
>>>
>>> comment or ok?
>>>
>>> Make dhcpd(8) work on the DLT_LOOP interfaces.
> (snip)
>> Unfortunately all this bpf stuff is black magic to me. Ditto DLT_LOOP.
>> If it works I have no objection, but I'm not able to ok it.
>
> This reminded me that I had another way.
>
> This bpf magic was to use dhcpd(8) on tunneling interfaces. But on
> tunnel interfaces, client's IP address must be configured before the
> DHCP. Therefore
>
> - only DHCPINFORM is usable
> - bpf is not required. a normal UDP socket can be used instead
>
> I added udp socket mode to dhcpd(8). This also makes dhcpd answer
> DHCP inform on tunnel interfaces. I've tested this with the dhcp
> inform diff and iPad already.
>
> How about this direction?
Let me update the diff
Fixing the byte order of the server_port and small changes.
Index: usr.sbin/dhcpd/Makefile
===================================================================
RCS file: /disk/cvs/openbsd/src/usr.sbin/dhcpd/Makefile,v
retrieving revision 1.4
diff -u -p -r1.4 Makefile
--- usr.sbin/dhcpd/Makefile 7 May 2008 12:19:20 -0000 1.4
+++ usr.sbin/dhcpd/Makefile 10 Jul 2014 08:59:03 -0000
@@ -4,7 +4,8 @@
SRCS= bootp.c confpars.c db.c dhcp.c dhcpd.c bpf.c packet.c errwarn.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
+ alloc.c tables.c tree.c hash.c convert.c icmp.c pfutils.c sync.c \
+ udpsock.c
PROG= dhcpd
MAN= dhcpd.8 dhcpd.conf.5 dhcpd.leases.5 dhcp-options.5
Index: usr.sbin/dhcpd/bootp.c
===================================================================
RCS file: /disk/cvs/openbsd/src/usr.sbin/dhcpd/bootp.c,v
retrieving revision 1.14
diff -u -p -r1.14 bootp.c
--- usr.sbin/dhcpd/bootp.c 11 Jun 2014 16:45:15 -0000 1.14
+++ usr.sbin/dhcpd/bootp.c 10 Jul 2014 08:59:03 -0000
@@ -325,7 +325,7 @@ lose:
to.sin_addr = raw.giaddr;
to.sin_port = server_port;
- (void) send_packet(packet->interface, &raw,
+ (void) packet->interface->send_packet(packet->interface, &raw,
outgoing.packet_length, from, &to, packet->haddr);
return;
}
@@ -345,6 +345,6 @@ lose:
}
errno = 0;
- (void) send_packet(packet->interface, &raw,
+ (void) packet->interface->send_packet(packet->interface, &raw,
outgoing.packet_length, from, &to, packet->haddr);
}
Index: usr.sbin/dhcpd/bpf.c
===================================================================
RCS file: /disk/cvs/openbsd/src/usr.sbin/dhcpd/bpf.c,v
retrieving revision 1.10
diff -u -p -r1.10 bpf.c
--- usr.sbin/dhcpd/bpf.c 5 Apr 2013 19:31:36 -0000 1.10
+++ usr.sbin/dhcpd/bpf.c 10 Jul 2014 08:59:03 -0000
@@ -52,6 +52,9 @@
#define BPF_FORMAT "/dev/bpf%d"
+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
@@ -81,6 +84,7 @@ if_register_bpf(struct interface_info *i
error("Can't attach interface %s to bpf device %s: %m",
info->name, filename);
+ info->send_packet = send_packet;
return (sock);
}
Index: usr.sbin/dhcpd/dhcp.c
===================================================================
RCS file: /disk/cvs/openbsd/src/usr.sbin/dhcpd/dhcp.c,v
retrieving revision 1.36
diff -u -p -r1.36 dhcp.c
--- usr.sbin/dhcpd/dhcp.c 5 Apr 2013 19:31:36 -0000 1.36
+++ usr.sbin/dhcpd/dhcp.c 10 Jul 2014 08:59:04 -0000
@@ -652,7 +652,7 @@ nak_lease(struct packet *packet, struct
to.sin_addr = raw.giaddr;
to.sin_port = server_port;
- result = send_packet(packet->interface, &raw,
+ result = packet->interface->send_packet(packet->interface, &raw,
outgoing.packet_length, from, &to, packet->haddr);
if (result == -1)
warning("send_fallback: %m");
@@ -663,7 +663,7 @@ nak_lease(struct packet *packet, struct
}
errno = 0;
- result = send_packet(packet->interface, &raw,
+ result = packet->interface->send_packet(packet->interface, &raw,
outgoing.packet_length, from, &to, NULL);
}
@@ -1327,7 +1327,7 @@ dhcp_reply(struct lease *lease)
memcpy(&from, state->from.iabuf, sizeof from);
- (void) send_packet(state->ip, &raw,
+ (void) state->ip->send_packet(state->ip, &raw,
packet_length, from, &to, &state->haddr);
free_lease_state(state, "dhcp_reply gateway");
@@ -1371,7 +1371,7 @@ dhcp_reply(struct lease *lease)
memcpy(&from, state->from.iabuf, sizeof from);
- (void) send_packet(state->ip, &raw, packet_length,
+ (void) state->ip->send_packet(state->ip, &raw, packet_length,
from, &to, &state->haddr);
free_lease_state(state, "dhcp_reply");
Index: usr.sbin/dhcpd/dhcpd.8
===================================================================
RCS file: /disk/cvs/openbsd/src/usr.sbin/dhcpd/dhcpd.8,v
retrieving revision 1.22
diff -u -p -r1.22 dhcpd.8
--- usr.sbin/dhcpd/dhcpd.8 3 Jan 2014 16:21:58 -0000 1.22
+++ usr.sbin/dhcpd/dhcpd.8 10 Jul 2014 08:59:04 -0000
@@ -45,9 +45,10 @@
.Sh SYNOPSIS
.Nm dhcpd
.Bk -words
-.Op Fl dfn
+.Op Fl dfnu
.Op Fl A Ar abandoned_ip_table
.Op Fl C Ar changed_ip_table
+.Op Fl b Ar bind_address
.Op Fl c Ar config-file
.Op Fl L Ar leased_ip_table
.Op Fl l Ar lease-file
@@ -149,7 +150,9 @@ This should be done on systems where
.Nm
is unable to identify non-broadcast interfaces,
but should not be required on other systems.
-If no interface names are specified on the command line,
+If any interface name and
+.Fl u
+is not specified on the command line,
.Nm
will identify all network interfaces which are up, eliminating non-broadcast
interfaces if possible, and listen for DHCP broadcasts on each interface.
@@ -180,6 +183,23 @@ When the address is leased to a differen
.Nm
can remove the address from the overload table, thus allowing a well-behaved
machine to reuse the address.
+.It Fl b Ar bind_address
+When
+.Fl u
+option is specified,
+.Nm
+will bind the limited broadcast address (255.255.255.255) for the UDP socket
+by default.
+The
+.Fl b
+option, followed by
+.Ar bind_address
+specifies the address
+.Nm
+should bind.
+This option must be combined with
+.Fl u
+option.
.It Fl c Ar config-file
Use an alternate configuration file,
.Ar config-file .
@@ -239,6 +259,8 @@ for testing lease files in a non-product
.It Fl n
Only test configuration, do not run
.Nm .
+.It Fl u
+Use an UDP socket instead of BPF for receiving and sending packets.
.It Fl Y Ar synctarget
Add target
.Ar synctarget
Index: usr.sbin/dhcpd/dhcpd.c
===================================================================
RCS file: /disk/cvs/openbsd/src/usr.sbin/dhcpd/dhcpd.c,v
retrieving revision 1.44
diff -u -p -r1.44 dhcpd.c
--- usr.sbin/dhcpd/dhcpd.c 7 May 2014 13:20:47 -0000 1.44
+++ usr.sbin/dhcpd/dhcpd.c 10 Jul 2014 08:59:04 -0000
@@ -71,18 +71,19 @@ struct syslog_data sdata = SYSLOG_DATA_I
int
main(int argc, char *argv[])
{
- int ch, cftest = 0, daemonize = 1, rdomain = -1;
+ int ch, cftest = 0, daemonize = 1, rdomain = -1, udpsockmode = 0;
extern char *__progname;
char *sync_iface = NULL;
char *sync_baddr = NULL;
u_short sync_port = 0;
struct servent *ent;
+ struct in_addr udpaddr;
/* Initially, log errors to stderr as well as to syslogd. */
openlog_r(__progname, LOG_PID | LOG_NDELAY, DHCPD_LOG_FACILITY, &sdata);
opterr = 0;
- while ((ch = getopt(argc, argv, "A:C:L:c:dfl:nY:y:")) != -1)
+ while ((ch = getopt(argc, argv, "A:C:L:b:c:dfl:nuY:y:")) != -1)
switch (ch) {
case 'Y':
syncsend = 1;
@@ -98,8 +99,10 @@ main(int argc, char *argv[])
sync_port = ntohs(ent->s_port);
}
+ udpaddr.s_addr = htonl(INADDR_BROADCAST);
+
optreset = optind = opterr = 1;
- while ((ch = getopt(argc, argv, "A:C:L:c:dfl:nY:y:")) != -1)
+ while ((ch = getopt(argc, argv, "A:C:L:b:c:dfl:nuY:y:")) != -1)
switch (ch) {
case 'A':
abandoned_tab = optarg;
@@ -110,6 +113,11 @@ main(int argc, char *argv[])
case 'L':
leased_tab = optarg;
break;
+ case 'b':
+ if (inet_aton(optarg, &udpaddr) != 1)
+ errx(1, "Cannot parse binding IP address: %s",
+ optarg);
+ break;
case 'c':
path_dhcpd_conf = optarg;
break;
@@ -128,6 +136,9 @@ main(int argc, char *argv[])
cftest = 1;
log_perror = 1;
break;
+ case 'u':
+ udpsockmode = 1;
+ break;
case 'Y':
if (sync_addhost(optarg, sync_port) != 0)
sync_iface = optarg;
@@ -169,12 +180,15 @@ main(int argc, char *argv[])
exit(0);
db_startup();
- discover_interfaces(&rdomain);
+ if (!udpsockmode || argc > 0)
+ discover_interfaces(&rdomain);
if (rdomain != -1)
if (setrtable(rdomain) == -1)
error("setrtable (%m)");
+ if (udpsockmode)
+ udpsock_startup(udpaddr);
icmp_startup(1, lease_pinged);
if (syncsend || syncrecv) {
@@ -232,11 +246,13 @@ usage(void)
{
extern char *__progname;
- fprintf(stderr, "usage: %s [-dfn] [-A abandoned_ip_table]", __progname);
+ fprintf(stderr, "usage: %s [-dfnu] [-A abandoned_ip_table]",
+ __progname);
fprintf(stderr, " [-C changed_ip_table]\n");
- fprintf(stderr, "\t[-c config-file] [-L leased_ip_table]");
- fprintf(stderr, " [-l lease-file] [-Y synctarget] [-y synclisten]\n");
- fprintf(stderr, "\t[if0 [... ifN]]\n");
+ fprintf(stderr, "\t[-b bind_address] [-c config-file]");
+ fprintf(stderr, " [-L leased_ip_table]\n");
+ fprintf(stderr, "\t[-l lease-file] [-Y synctarget] [-y synclisten]");
+ fprintf(stderr, " [if0 [... ifN]]\n");
exit(1);
}
Index: usr.sbin/dhcpd/dhcpd.h
===================================================================
RCS file: /disk/cvs/openbsd/src/usr.sbin/dhcpd/dhcpd.h,v
retrieving revision 1.50
diff -u -p -r1.50 dhcpd.h
--- usr.sbin/dhcpd/dhcpd.h 7 May 2014 13:20:47 -0000 1.50
+++ usr.sbin/dhcpd/dhcpd.h 10 Jul 2014 08:59:04 -0000
@@ -424,6 +424,8 @@ struct interface_info {
int errors;
int dead;
u_int16_t index;
+ ssize_t (*send_packet)(struct interface_info *, struct dhcp_packet *,
+ size_t, struct in_addr, struct sockaddr_in *, struct hardware *);
};
struct hardware_link {
@@ -616,8 +618,6 @@ char *print_hw_addr(int, int, unsigned c
/* bpf.c */
int if_register_bpf(struct interface_info *);
void if_register_send(struct interface_info *);
-ssize_t send_packet(struct interface_info *, struct dhcp_packet *, size_t,
- struct in_addr, struct sockaddr_in *, struct hardware *);
void if_register_receive(struct interface_info *);
ssize_t receive_packet(struct interface_info *, unsigned char *, size_t,
struct sockaddr_in *, struct hardware *);
@@ -697,3 +697,6 @@ size_t atomicio(ssize_t (*)(int, void *,
#define vwrite (ssize_t (*)(int, void *, size_t))write
void pfmsg(char, struct lease *);
extern struct syslog_data sdata;
+
+/* udpsock.c */
+void udpsock_startup(struct in_addr);
Index: usr.sbin/dhcpd/udpsock.c
===================================================================
RCS file: usr.sbin/dhcpd/udpsock.c
diff -N usr.sbin/dhcpd/udpsock.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ usr.sbin/dhcpd/udpsock.c 10 Jul 2014 08:59:04 -0000
@@ -0,0 +1,341 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2014 YASUOKA Masahiko <[email protected]>
+ *
+ * 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 <sys/param.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "dhcpd.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 *);
+
+struct udpsock {
+ int sock;
+};
+
+void
+udpsock_startup(struct in_addr bindaddr)
+{
+ int sock, onoff;
+ struct sockaddr_in sin4;
+ struct udpsock *udpsock;
+
+ if ((udpsock = calloc(1, sizeof(struct udpsock))) == NULL)
+ error("could not create udpsock: %s", strerror(errno));
+
+ memset(&sin4, 0, sizeof(sin4));
+ if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
+ error("creating a socket failed for udp: %s", strerror(errno));
+
+ onoff = 1;
+ if (setsockopt(sock, IPPROTO_IP, IP_RECVIF, &onoff, sizeof(onoff)) != 0)
+ error("setsocketopt IP_RECVIF failed for udp: %s",
+ strerror(errno));
+
+ sin4.sin_family = AF_INET;
+ sin4.sin_len = sizeof(sin4);
+ sin4.sin_addr = bindaddr;
+ sin4.sin_port = server_port;
+
+ if (bind(sock, (struct sockaddr *)&sin4, sizeof(sin4)) != 0)
+ error("bind failed for udp: %s", strerror(errno));
+
+ add_protocol("udp", sock, udpsock_handler, (void *)(intptr_t)udpsock);
+ note("Listening on %s:%d/udp.", inet_ntoa(sin4.sin_addr),
+ ntohs(server_port));
+
+ udpsock->sock = sock;
+}
+
+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 dhcp_packet *packet = (struct dhcp_packet *)packetbuf;
+ struct hardware hw;
+ struct ifreq ifr;
+ struct subnet *subnet;
+
+ 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 = nitems(iov);
+ m.msg_control = cbuf;
+ m.msg_controllen = sizeof(cbuf);
+
+ memset(&iface, 0, sizeof(iface));
+ if ((len = recvmsg(udpsock->sock, &m, 0)) < 0) {
+ warning("receiving a DHCP message failed: %s", strerror(errno));
+ return;
+ }
+ if (ss.ss_family != AF_INET) {
+ warning("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) {
+ warning("could not get the received interface by IP_RECVIF");
+ return;
+ }
+ if_indextoname(sdl->sdl_index, ifname);
+
+ if ((sockio = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ warning("socket creation failed: %s", strerror(errno));
+ return;
+ }
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(sockio, SIOCGIFADDR, &ifr, sizeof(ifr)) != 0) {
+ warning("Failed to get address for %s: %s", ifname,
+ strerror(errno));
+ close(sockio);
+ return;
+ }
+ close(sockio);
+
+ if (ifr.ifr_addr.sa_family != AF_INET)
+ return;
+
+ 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));
+
+ addr.len = 4;
+ memcpy(&addr.iabuf, &iface.primary_address, addr.len);
+
+ 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);
+}
+
+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)
+{
+ return (sendto(interface->wfdesc, raw, len, 0, (struct sockaddr *)to,
+ sizeof(struct sockaddr_in)));
+}
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2014 YASUOKA Masahiko <[email protected]>
+ *
+ * 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 <sys/param.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "dhcpd.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 *);
+
+struct udpsock {
+ int sock;
+};
+
+void
+udpsock_startup(struct in_addr listenaddr)
+{
+ int sock, onoff;
+ struct sockaddr_in sin4;
+ struct udpsock *udpsock;
+
+ if ((udpsock = calloc(1, sizeof(struct udpsock))) == NULL)
+ error("could not create udpsock: %s", strerror(errno));
+
+ memset(&sin4, 0, sizeof(sin4));
+ if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
+ error("creating a socket failed for udp: %s", strerror(errno));
+
+ onoff = 1;
+ if (setsockopt(sock, IPPROTO_IP, IP_RECVIF, &onoff, sizeof(onoff)) != 0)
+ error("setsocketopt IP_RECVIF failed for udp: %s",
+ strerror(errno));
+
+ sin4.sin_family = AF_INET;
+ sin4.sin_len = sizeof(sin4);
+ sin4.sin_addr = listenaddr;
+ sin4.sin_port = htons(server_port);
+
+ if (bind(sock, (struct sockaddr *)&sin4, sizeof(sin4)) != 0)
+ error("bind failed for udp: %s", strerror(errno));
+
+ add_protocol("udp", sock, udpsock_handler, (void *)(intptr_t)udpsock);
+ note("Listening as UDP socket on %s.", inet_ntoa(sin4.sin_addr));
+
+ udpsock->sock = sock;
+}
+
+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 dhcp_packet *packet = (struct dhcp_packet *)packetbuf;
+ struct hardware hw;
+ struct ifreq ifr;
+ struct subnet *subnet;
+
+ 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 = nitems(iov);
+ m.msg_control = cbuf;
+ m.msg_controllen = sizeof(cbuf);
+
+ memset(&iface, 0, sizeof(iface));
+ if ((len = recvmsg(udpsock->sock, &m, 0)) < 0) {
+ warning("receiving a DHCP message failed: %s", strerror(errno));
+ return;
+ }
+ if (ss.ss_family != AF_INET) {
+ warning("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) {
+ warning("could not get the received interface by IP_RECVIF");
+ return;
+ }
+ if_indextoname(sdl->sdl_index, ifname);
+
+ if ((sockio = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ warning("socket creation failed: %s", strerror(errno));
+ return;
+ }
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(sockio, SIOCGIFADDR, &ifr, sizeof(ifr)) != 0) {
+ warning("Failed to get address for %s: %s", ifname,
+ strerror(errno));
+ close(sockio);
+ return;
+ }
+ close(sockio);
+
+ if (ifr.ifr_addr.sa_family != AF_INET)
+ return;
+
+ 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));
+
+ addr.len = 4;
+ memcpy(&addr.iabuf, &iface.primary_address, addr.len);
+
+ 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);
+}
+
+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)
+{
+ return (sendto(interface->wfdesc, raw, len, 0, (struct sockaddr *)to,
+ sizeof(struct sockaddr_in)));
+}