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?


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:05:48 -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:05:48 -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:05:48 -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:05:49 -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:05:49 -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:05:49 -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 listening 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:05:49 -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:05:49 -0000
@@ -0,0 +1,170 @@
+/*     $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