Hi,
here is why I need this:
http://marc.info/?l=openbsd-misc&m=134187877220567&w=2 

Benchmarks look promissing, meassuring queries per second:

IPv4 divert: 54.03%
IPv4 divert only queries: 89.92%

IPv6 divert: 57.33%
IPv6 divert only queries: 96.1%

(Where 100% is just passing traffic in pf)

Henning sugested to Benno in Budapest to use an ioctl for this instead
of extending pf.conf.
This is what I came up with. I added XXXs where I'm unsure how it's
supposed to work.  Comments from someone who actually knows how ioctls
work are highly appriciated, I'm just guessing here ;)

Thanks,
Florian

-- 
I'm not entirely sure you are real.

Index: share/man/man4/divert.4
===================================================================
RCS file: /opt/OpenBSD-CVS/src/share/man/man4/divert.4,v
retrieving revision 1.8
diff -u -p -r1.8 divert.4
--- share/man/man4/divert.4     29 Mar 2012 17:09:41 -0000      1.8
+++ share/man/man4/divert.4     20 Jul 2012 13:36:39 -0000
@@ -77,6 +77,17 @@ Receive and send divert socket buffer sp
 .Xr sysctl 8 .
 .Xr netstat 1
 shows information relevant to divert sockets.
+.Pp
+The following ioctl controlls if inbound and outpound patckets are diverted
+(the default) or only one direction.
+.Bl -tag -width "xxxxxx"
+.It Dv SIOCSETDVRTFL "int"
+Set direction in which packets should be diverted.
+Valid flags are
+.Dv DIVERT_IN
+and
+.Dv DIVERT_OUT .
+.El
 .Sh EXAMPLES
 The following PF rule queues outbound IPv4 packets to TCP port 80,
 as well as the return traffic, on the em0 interface to divert port 700:
Index: sys/net/pf.c
===================================================================
RCS file: /opt/OpenBSD-CVS/src/sys/net/pf.c,v
retrieving revision 1.808
diff -u -p -r1.808 pf.c
--- sys/net/pf.c        10 Jul 2012 17:33:48 -0000      1.808
+++ sys/net/pf.c        20 Jul 2012 13:36:39 -0000
@@ -6977,15 +6977,16 @@ done:
        case PF_DIVERT:
                switch (pd.af) {
                case AF_INET:
-                       divert_packet(pd.m, pd.dir);
+                       if (divert_packet(pd.m, pd.dir) == 0)
+                               *m0 = NULL;
                        break;
 #ifdef INET6
                case AF_INET6:
-                       divert6_packet(pd.m, pd.dir);
+                       if (divert6_packet(pd.m, pd.dir) == 0)
+                               *m0 = NULL;
                        break;
 #endif /* INET6 */
                }
-               *m0 = NULL;
                action = PF_PASS;
                break;
 #if INET && INET6
Index: sys/netinet/in.h
===================================================================
RCS file: /opt/OpenBSD-CVS/src/sys/netinet/in.h,v
retrieving revision 1.93
diff -u -p -r1.93 in.h
--- sys/netinet/in.h    16 Jul 2012 18:05:36 -0000      1.93
+++ sys/netinet/in.h    20 Jul 2012 13:36:39 -0000
@@ -89,6 +89,9 @@
 /* Only used internally, so it can be outside the range of valid IP protocols 
*/
 #define        IPPROTO_DIVERT          258             /* Divert sockets */
 
+/* XXX this should probably be defined somewhere else */
+#define DIVERT_IN                1
+#define DIVERT_OUT               2
 
 /*
  * From FreeBSD:
Index: sys/netinet/in_pcb.h
===================================================================
RCS file: /opt/OpenBSD-CVS/src/sys/netinet/in_pcb.h,v
retrieving revision 1.72
diff -u -p -r1.72 in_pcb.h
--- sys/netinet/in_pcb.h        16 Jul 2012 18:05:36 -0000      1.72
+++ sys/netinet/in_pcb.h        20 Jul 2012 13:36:39 -0000
@@ -148,6 +148,8 @@ struct inpcb {
        void    *inp_pf_sk;
        u_int   inp_rtableid;
        int     inp_pipex;              /* pipex indication */
+       /* XXX ok to extend inpcb? */
+       int     divert_flags;
 };
 
 struct inpcbtable {
Index: sys/netinet/ip_divert.c
===================================================================
RCS file: /opt/OpenBSD-CVS/src/sys/netinet/ip_divert.c,v
retrieving revision 1.9
diff -u -p -r1.9 ip_divert.c
--- sys/netinet/ip_divert.c     13 Jul 2012 16:27:30 -0000      1.9
+++ sys/netinet/ip_divert.c     20 Jul 2012 13:36:39 -0000
@@ -20,6 +20,7 @@
 #include <sys/systm.h>
 #include <sys/mbuf.h>
 #include <sys/protosw.h>
+#include <sys/sockio.h>
 #include <sys/socket.h>
 #include <sys/socketvar.h>
 #include <sys/proc.h>
@@ -133,27 +134,40 @@ divert_output(struct mbuf *m, ...)
        return (error);
 }
 
-void
+/* XXX return enum? */
+int
 divert_packet(struct mbuf *m, int dir)
 {
-       struct inpcb *inp;
+       struct inpcb *inp, *inp_np;
        struct socket *sa = NULL;
        struct sockaddr_in addr;
        struct pf_divert *pd;
 
+       inp = NULL;
        divstat.divs_ipackets++;
 
        if (m->m_len < sizeof(struct ip) &&
            (m = m_pullup(m, sizeof(struct ip))) == NULL) {
                divstat.divs_errors++;
-               return;
+               return (0);
        }
 
        pd = pf_find_divert(m);
        if (pd == NULL) {
                divstat.divs_errors++;
                m_freem(m);
-               return;
+               return (0);
+       }
+
+       CIRCLEQ_FOREACH(inp_np, &divbtable.inpt_queue, inp_queue) {
+               if (inp_np->inp_lport != pd->port)
+                       continue;
+               inp = inp_np;
+               if (dir == PF_IN && !(inp->divert_flags & DIVERT_IN))
+                       return (-1);
+               if (dir == PF_OUT && !(inp->divert_flags & DIVERT_OUT))
+                       return (-1);
+               break;
        }
 
        bzero(&addr, sizeof(addr));
@@ -177,25 +191,27 @@ divert_packet(struct mbuf *m, int dir)
        if (dir == PF_OUT)
                in_proto_cksum_out(m, NULL);
 
-       CIRCLEQ_FOREACH(inp, &divbtable.inpt_queue, inp_queue) {
-               if (inp->inp_lport != pd->port)
-                       continue;
-
+       /*
+        * XXX move the if up directly below the CIRCLEQ_FOREACH?
+        * we don't need to calculate checksums etc if we are not going
+        * to divert the packet
+        */
+       if (inp != NULL) {
                sa = inp->inp_socket;
                if (sbappendaddr(&sa->so_rcv, (struct sockaddr *)&addr, 
                    m, NULL) == 0) {
                        divstat.divs_fullsock++;
                        m_freem(m);
-                       return;
+                       return (0);
                } else
                        sorwakeup(inp->inp_socket);
-               break;
        }
 
        if (sa == NULL) {
                divstat.divs_noport++;
                m_freem(m);
        }
+       return (0);
 }
 
 /*ARGSUSED*/
@@ -205,11 +221,24 @@ divert_usrreq(struct socket *so, int req
 {
        struct inpcb *inp = sotoinpcb(so);
        int error = 0;
-       int s;
+       int arg, s;
 
        if (req == PRU_CONTROL) {
-               return (in_control(so, (u_long)m, (caddr_t)addr,
-                   (struct ifnet *)control));
+               switch ((u_long)m) {
+               case SIOCSETDVRTFL:
+                       /*
+                        * XXX this is fubar, but maybe that's just the
+                        * way ioctl works?!?
+                        */
+                       arg = (int)*(int *)addr;
+                       if ( arg != (arg & (DIVERT_IN | DIVERT_OUT)))
+                               return EINVAL;
+                       inp->divert_flags = arg;
+                       return 0;
+               default:
+                       return (in_control(so, (u_long)m, (caddr_t)addr,
+                           (struct ifnet *)control));
+               }
        }
        if (inp == NULL && req != PRU_ATTACH) {
                error = EINVAL;
@@ -236,6 +265,8 @@ divert_usrreq(struct socket *so, int req
                if (error)
                        break;
                ((struct inpcb *) so->so_pcb)->inp_flags |= INP_HDRINCL;
+               ((struct inpcb *) so->so_pcb)->divert_flags =
+                   DIVERT_IN | DIVERT_OUT;
                break;
 
        case PRU_DETACH:
Index: sys/netinet/ip_divert.h
===================================================================
RCS file: /opt/OpenBSD-CVS/src/sys/netinet/ip_divert.h,v
retrieving revision 1.3
diff -u -p -r1.3 ip_divert.h
--- sys/netinet/ip_divert.h     4 Oct 2009 16:08:37 -0000       1.3
+++ sys/netinet/ip_divert.h     20 Jul 2012 13:36:39 -0000
@@ -55,7 +55,7 @@ extern struct divstat         divstat;
 
 void    divert_init(void);
 void    divert_input(struct mbuf *, ...);
-void    divert_packet(struct mbuf *, int);
+int     divert_packet(struct mbuf *, int);
 int     divert_output(struct mbuf *, ...);
 int     divert_sysctl(int *, u_int, void *, size_t *, void *, size_t);
 int     divert_usrreq(struct socket *,
Index: sys/netinet6/ip6_divert.c
===================================================================
RCS file: /opt/OpenBSD-CVS/src/sys/netinet6/ip6_divert.c,v
retrieving revision 1.5
diff -u -p -r1.5 ip6_divert.c
--- sys/netinet6/ip6_divert.c   3 Jul 2010 04:44:51 -0000       1.5
+++ sys/netinet6/ip6_divert.c   20 Jul 2012 13:36:39 -0000
@@ -20,6 +20,7 @@
 #include <sys/systm.h>
 #include <sys/mbuf.h>
 #include <sys/protosw.h>
+#include <sys/sockio.h>
 #include <sys/socket.h>
 #include <sys/socketvar.h>
 #include <sys/proc.h>
@@ -135,27 +136,40 @@ divert6_output(struct mbuf *m, ...)
        return (error);
 }
 
-void
+/* XXX return enum? */
+int
 divert6_packet(struct mbuf *m, int dir)
 {
-       struct inpcb *inp;
+       struct inpcb *inp, *inp_np;
        struct socket *sa = NULL;
        struct sockaddr_in6 addr;
        struct pf_divert *pd;
 
+       inp = NULL;
        div6stat.divs_ipackets++;
        
        if (m->m_len < sizeof(struct ip6_hdr) &&
            (m = m_pullup(m, sizeof(struct ip6_hdr))) == NULL) {
                div6stat.divs_errors++;
-               return;
+               return (0);
        }
 
        pd = pf_find_divert(m);
        if (pd == NULL) {
                div6stat.divs_errors++;
                m_freem(m);
-               return;
+               return (0);
+       }
+
+       CIRCLEQ_FOREACH(inp_np, &divb6table.inpt_queue, inp_queue) {
+               if (inp_np->inp_lport != pd->port)
+                       continue;
+               inp = inp_np;
+               if (dir == PF_IN && !(inp->divert_flags & DIVERT_IN))
+                       return (-1);
+               if (dir == PF_OUT && !(inp->divert_flags & DIVERT_OUT))
+                       return (-1);
+               break;
        }
 
        bzero(&addr, sizeof(addr));
@@ -176,25 +190,28 @@ divert6_packet(struct mbuf *m, int dir)
                }
        }
 
-       CIRCLEQ_FOREACH(inp, &divb6table.inpt_queue, inp_queue) {
-               if (inp->inp_lport != pd->port)
-                       continue;
-
+       /*
+        * XXX move the if up directly below the CIRCLEQ_FOREACH?
+        * we don't need to calculate checksums etc if we are not going
+        * to divert the packet. btw why is the checksum calculation
+        * not present for v6?
+        */
+       if (inp != NULL) {
                sa = inp->inp_socket;
                if (sbappendaddr(&sa->so_rcv, (struct sockaddr *)&addr, 
                    m, NULL) == 0) {
                        div6stat.divs_fullsock++;
                        m_freem(m);
-                       return;
+                       return (0);
                } else
                        sorwakeup(inp->inp_socket);
-               break;
        }
 
        if (sa == NULL) {
                div6stat.divs_noport++;
                m_freem(m);
        }
+       return (0);
 }
 
 /*ARGSUSED*/
@@ -204,11 +221,24 @@ divert6_usrreq(struct socket *so, int re
 {
        struct inpcb *inp = sotoinpcb(so);
        int error = 0;
-       int s;
+       int arg, s;
 
        if (req == PRU_CONTROL) {
-               return (in6_control(so, (u_long)m, (caddr_t)addr,
-                   (struct ifnet *)control, p));
+               switch ((u_long)m) {
+               case SIOCSETDVRTFL:
+                       /*
+                        * XXX this is fubar, but maybe that's just the
+                        * way ioctl works?!?
+                        */
+                       arg = (int)*(int *)addr;
+                       if ( arg != (arg & (DIVERT_IN | DIVERT_OUT)))
+                               return EINVAL;
+                       inp->divert_flags = arg;
+                       return 0;
+               default:
+                       return (in6_control(so, (u_long)m, (caddr_t)addr,
+                           (struct ifnet *)control, p));
+               }
        }
        if (inp == NULL && req != PRU_ATTACH) {
                error = EINVAL;
@@ -235,6 +265,8 @@ divert6_usrreq(struct socket *so, int re
                if (error)
                        break;
                ((struct inpcb *) so->so_pcb)->inp_flags |= INP_HDRINCL;
+               ((struct inpcb *) so->so_pcb)->divert_flags =
+                   DIVERT_IN | DIVERT_OUT;
                break;
 
        case PRU_DETACH:
Index: sys/netinet6/ip6_divert.h
===================================================================
RCS file: /opt/OpenBSD-CVS/src/sys/netinet6/ip6_divert.h,v
retrieving revision 1.1
diff -u -p -r1.1 ip6_divert.h
--- sys/netinet6/ip6_divert.h   5 Nov 2009 20:50:14 -0000       1.1
+++ sys/netinet6/ip6_divert.h   20 Jul 2012 13:36:39 -0000
@@ -55,7 +55,7 @@ extern struct div6stat                div6stat;
 
 void    divert6_init(void);
 int     divert6_input(struct mbuf **, int *, int);
-void    divert6_packet(struct mbuf *, int);
+int     divert6_packet(struct mbuf *, int);
 int     divert6_output(struct mbuf *, ...);
 int     divert6_sysctl(int *, u_int, void *, size_t *, void *, size_t);
 int     divert6_usrreq(struct socket *,
Index: sys/sys/sockio.h
===================================================================
RCS file: /opt/OpenBSD-CVS/src/sys/sys/sockio.h,v
retrieving revision 1.49
diff -u -p -r1.49 sockio.h
--- sys/sys/sockio.h    26 Nov 2011 23:38:18 -0000      1.49
+++ sys/sys/sockio.h    20 Jul 2012 13:36:39 -0000
@@ -181,6 +181,13 @@
 #define SIOCSETKALIVE  _IOW('i', 163, struct ifkalivereq)
 #define SIOCGETKALIVE  _IOWR('i', 164, struct ifkalivereq)
 
+/*
+ * XXX is 'd' the right group? how does this work anyway?
+ * 'i' and 'r' are special in sys/kern/sys_socket.c, everything else
+ * should work
+ */
+#define SIOCSETDVRTFL  _IOW('d', 165, int)     /* set divert flags */
+
 #define        SIOCSVH         _IOWR('i', 245, struct ifreq)   /* set carp 
param */
 #define        SIOCGVH         _IOWR('i', 246, struct ifreq)   /* get carp 
param */

Reply via email to