Hi,

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.

worked with Yuuichi Someya

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        9 Jul 2014 13:59:31 -0000
@@ -52,6 +52,9 @@
 
 #define BPF_FORMAT "/dev/bpf%d"
 
+void dhcp_bpf_filter    (struct bpf_program *, int);
+void dhcp_bpf_wfilter   (struct bpf_program *, int);
+
 /*
  * Called by get_interface_list for each interface that's discovered.
  * Opens a packet filter for each interface and adds it to the select
@@ -94,76 +97,116 @@ if_register_send(struct interface_info *
        info->wfdesc = info->rfdesc;
 }
 
+#define SUBST_BPF_STMT(_insns, _code, _k)                               \
+        do {                                                            \
+                struct bpf_insn insnx = BPF_STMT((_code), (_k));        \
+                (_insns) = insnx;                                       \
+        } while (0 /* CONSTCOND */)
+
+#define SUBST_BPF_JUMP(_insns, _code, _k, _jt, _jf)                     \
+        do {                                                            \
+                struct bpf_insn insnx =                                 \
+                    BPF_JUMP((_code), (_k), (_jt), (_jf));              \
+                (_insns) = insnx;                                       \
+        } while (0 /* CONSTCOND */)
+
 /*
  * Packet read filter program: 'ip and udp and dst port bootps'
  */
-struct bpf_insn dhcp_bpf_filter[] = {
+void
+dhcp_bpf_filter(struct bpf_program *p, int dlt)
+{
+       int hdrlen;
+       static struct bpf_insn insns[11];
+
        /* 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),
+       if (dlt == DLT_LOOP) {
+               SUBST_BPF_STMT(insns[0], BPF_LD + BPF_W + BPF_ABS, 0);
+               SUBST_BPF_JUMP(insns[1], BPF_JMP + BPF_JEQ + BPF_K, AF_INET, 0, 
8);
+               hdrlen = 4;
+       } else {
+               /* Make sure this is an IP packet... */
+               SUBST_BPF_STMT(insns[0], BPF_LD + BPF_H + BPF_ABS, 12);
+               SUBST_BPF_JUMP(insns[1], BPF_JMP + BPF_JEQ + BPF_K, 
ETHERTYPE_IP, 0, 8);
+               hdrlen = 14;
+       }
 
        /* 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),
+       SUBST_BPF_STMT(insns[2], BPF_LD + BPF_B + BPF_ABS, hdrlen + 9);
+       SUBST_BPF_JUMP(insns[3], 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),
+       SUBST_BPF_STMT(insns[4], BPF_LD + BPF_H + BPF_ABS, hdrlen + 6);
+       SUBST_BPF_JUMP(insns[5], BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 4, 0);
 
        /* Get the IP header length... */
-       BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14),
+       SUBST_BPF_STMT(insns[6], BPF_LDX + BPF_B + BPF_MSH, hdrlen);
 
        /* 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),
+       SUBST_BPF_STMT(insns[7], BPF_LD + BPF_H + BPF_IND, hdrlen + 2);
+       SUBST_BPF_JUMP(insns[8], 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),
+       SUBST_BPF_STMT(insns[9], 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);
+       SUBST_BPF_STMT(insns[10], BPF_RET+BPF_K, 0);
 
+       p->bf_insns = insns;
+       p->bf_len = 11;
+}
 
 /*
  * Packet write filter program:
  * 'ip and udp and src port bootps and dst port (bootps or bootpc)'
  */
-struct bpf_insn dhcp_bpf_wfilter[] = {
+void
+dhcp_bpf_wfilter(struct bpf_program *p, int dlt)
+{
+       int hdrlen;
+       static struct bpf_insn insns[14];
+
        /* 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),
+       if (dlt == DLT_LOOP) {
+               SUBST_BPF_STMT(insns[0], BPF_LD + BPF_W + BPF_ABS, 0);
+               SUBST_BPF_JUMP(insns[1], BPF_JMP + BPF_JEQ + BPF_K, AF_INET, 0, 
11);
+               hdrlen = 4;
+       } else {
+               /* Make sure this is an IP packet... */
+               SUBST_BPF_STMT(insns[0], BPF_LD + BPF_H + BPF_ABS, 12);
+               SUBST_BPF_JUMP(insns[1], BPF_JMP + BPF_JEQ + BPF_K, 
ETHERTYPE_IP, 0, 11);
+               hdrlen = 14;
+       }
 
        /* 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),
+       SUBST_BPF_STMT(insns[2], BPF_LD + BPF_B + BPF_ABS, hdrlen + 9);
+       SUBST_BPF_JUMP(insns[3], 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),
+       SUBST_BPF_STMT(insns[4], BPF_LD + BPF_H + BPF_ABS, hdrlen + 6);
+       SUBST_BPF_JUMP(insns[5], BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 7, 0);
 
        /* Get the IP header length... */
-       BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14),
+       SUBST_BPF_STMT(insns[6], BPF_LDX + BPF_B + BPF_MSH, hdrlen);
 
        /* 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),
+       SUBST_BPF_STMT(insns[7], BPF_LD + BPF_H + BPF_IND, hdrlen);
+       SUBST_BPF_JUMP(insns[8], 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),
+       SUBST_BPF_STMT(insns[9], BPF_LD + BPF_H + BPF_IND, hdrlen + 2);
+       SUBST_BPF_JUMP(insns[10], BPF_JMP + BPF_JEQ + BPF_K, CLIENT_PORT, 1, 0);
+       SUBST_BPF_JUMP(insns[11], 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),
+       SUBST_BPF_STMT(insns[12], BPF_RET+BPF_K, (u_int)-1);
 
        /* Otherwise, drop it. */
-       BPF_STMT(BPF_RET+BPF_K, 0),
-};
+       SUBST_BPF_STMT(insns[13], BPF_RET+BPF_K, 0);
 
-int dhcp_bpf_wfilter_len = sizeof(dhcp_bpf_wfilter) / sizeof(struct bpf_insn);
+       p->bf_insns = insns;
+       p->bf_len = 14;
+}
 
 void
 if_register_receive(struct interface_info *info)
@@ -183,6 +226,8 @@ if_register_receive(struct interface_inf
            v.bv_minor < BPF_MINOR_VERSION)
                error("Kernel BPF version out of range - recompile dhcpd!");
 
+       if (ioctl(info->rfdesc, BIOCGDLT, &info->dlt) == -1)
+               error("Can't get DLT: %m");
        /*
         * Set immediate mode so that reads return as soon as a packet
         * comes in, rather than waiting for the input buffer to fill
@@ -210,15 +255,13 @@ if_register_receive(struct interface_inf
        info->rbuf_len = 0;
 
        /* Set up the bpf filter program structure. */
-       p.bf_len = dhcp_bpf_filter_len;
-       p.bf_insns = dhcp_bpf_filter;
+       dhcp_bpf_filter(&p, info->dlt);
 
        if (ioctl(info->rfdesc, BIOCSETF, &p) == -1)
                error("Can't install packet filter program: %m");
 
        /* Set up the bpf write filter program structure. */
-       p.bf_len = dhcp_bpf_wfilter_len;
-       p.bf_insns = dhcp_bpf_wfilter;
+       dhcp_bpf_wfilter(&p, info->dlt);
 
        if (ioctl(info->rfdesc, BIOCSETWF, &p) == -1)
                error("Can't install write filter program: %m");
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      9 Jul 2014 13:59:31 -0000
@@ -424,6 +424,7 @@ struct interface_info {
        int errors;
        int dead;
        u_int16_t       index;
+       u_int           dlt;                    /* dlt of bpf */
 };
 
 struct hardware_link {
Index: usr.sbin/dhcpd/packet.c
===================================================================
RCS file: /disk/cvs/openbsd/src/usr.sbin/dhcpd/packet.c,v
retrieving revision 1.6
diff -u -p -r1.6 packet.c
--- usr.sbin/dhcpd/packet.c     5 Dec 2013 21:03:40 -0000       1.6
+++ usr.sbin/dhcpd/packet.c     9 Jul 2014 13:59:31 -0000
@@ -46,6 +46,7 @@
 #include <netinet/ip.h>
 #include <netinet/udp.h>
 #include <netinet/if_ether.h>
+#include <net/bpf.h>
 
 u_int32_t      checksum(unsigned char *, unsigned, u_int32_t);
 u_int32_t      wrapsum(u_int32_t);
@@ -89,6 +90,14 @@ assemble_hw_header(struct interface_info
 {
        struct ether_header eh;
 
+       if (interface->dlt == DLT_LOOP) {
+               u_int32_t af = htonl(AF_INET);
+
+               memcpy(buf, &af, sizeof(af));
+               *bufix += sizeof(af);
+               return;
+       }
+
        if (to != NULL && to->hlen == 6) /* XXX */
                memcpy(eh.ether_dhost, to->haddr, sizeof(eh.ether_dhost));
        else
@@ -146,6 +155,13 @@ decode_hw_header(struct interface_info *
     int bufix, struct hardware *from)
 {
        struct ether_header eh;
+
+       if (interface->dlt == DLT_LOOP) {
+               /* DLT_LOOP doesn't have hardware address */
+               from->htype = 0;
+               from->hlen = 0;
+               return (sizeof(u_int32_t));
+       }
 
        memcpy(&eh, buf + bufix, ETHER_HDR_LEN);
 

Reply via email to