On Thu, 10 Jul 2014 10:17:29 +0200 (CEST)
YASUOKA Masahiko <yasu...@yasuoka.net> wrote:
> On Wed, 9 Jul 2014 16:57:44 +0200
> Kenneth Westerback <kwesterb...@gmail.com> wrote:
>> On 9 July 2014 16:23, YASUOKA Masahiko <yasu...@yasuoka.net> 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 <yasu...@openbsd.org>
+ *
+ * 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 = &ifr;
+       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 <yasu...@openbsd.org>
+ *
+ * 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 = &ifr;
+       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)));
+}

Reply via email to