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 */