Date: Sat, 12 Apr 2014 13:20:08 GMT From: PiBa-NL <PiBa.NL.dev@gmail.com> To: freebsd-gnats-submit@FreeBSD.org Subject: kern/188511: divert-reply implementation for pf Message-ID: <201404121320.s3CDK85m059408@cgiserv.freebsd.org> Resent-Message-ID: <201404121330.s3CDU03V062485@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
>Number: 188511 >Category: kern >Synopsis: divert-reply implementation for pf >Confidential: no >Severity: non-critical >Priority: low >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: change-request >Submitter-Id: current-users >Arrival-Date: Sat Apr 12 13:30:00 UTC 2014 >Closed-Date: >Last-Modified: >Originator: PiBa-NL >Release: 10.0 >Organization: none >Environment: FreeBSD FreeBSD10 10.0-RELEASE FreeBSD 10.0-RELEASE #0 r260789: Thu Jan 16 22:34:59 UTC 2014 root@snap.freebsd.org:/usr/obj/usr/src/sys/GENERIC amd64 >Description: It is currently not possible to bind to a nonlocal ip and succesfully connect a TCP socket. This patch will include a new option for pf 'divert-reply'. This should work with the haproxy-devel port 'source 0.0.0.0 usesrc clientip' option. But for easy testing i also include a python program that contacts a webserver from a non-local ip using the IP_BINDANY or IPV6_BINDANY socket option. A single firewall rule needs to be made that matches outbound traffic, and has the divert-reply option. Like this: "pass out quick on em0 inet proto tcp from any to 192.168.0.40 port 80 keep state divert-reply" I hope this is OK and can be included in next release, if not please let me know if and what to adjust. p.s. I took pretty much all the code from here: http://lists.freebsd.org/pipermail/freebsd-net/2009-June/022166.html Adapted it to FreeBSD v10 , and removed the parts i was unable to test.. UDP and bridge support. >How-To-Repeat: The python program below uses/demonstrates the function, make sure the machine is in the return-path of the webserver traffic a.k.a. it is the default route. ------------------- import socket HOST = '192.168.0.40' # The remote host PORT = 80 # The same port as used by the server SOURCE = '192.168.108.20' #s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #s.setsockopt(socket.IPPROTO_IP, 24,1) # IP_BINDANY=24 flag to allow binding to nonlocal sockets. HOST = 'fd00:1::40' # The remote host PORT = 80 # The same port as used by the server SOURCE = 'fd00:108::abcd' s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) s.setsockopt(41, 64,1) # IPV6_BINDANY=64 flag to allow binding to nonlocal sockets. s.bind((SOURCE,0)) # port 0 so pick a random client port.. s.connect((HOST, PORT)) s.sendall('GET / HTTP/1.0\r\nhost: test\r\n\r\n') x = 0 while True: data = s.recv(1024) x = x + len(data) if not data: break print repr(data) s.close() print 'Received', repr(x), "bytes" ------------------- >Fix: The attached patch. Patch attached with submission follows: Index: sbin/pfctl/parse.y =================================================================== --- sbin/pfctl/parse.y (revision 261814) +++ sbin/pfctl/parse.y (working copy) @@ -2389,12 +2389,7 @@ } } | DIVERTREPLY { -#ifdef __FreeBSD__ - yyerror("divert-reply has no meaning in FreeBSD pf(4)"); - YYERROR; -#else filter_opts.divert.port = 1; /* some random value */ -#endif } ; Index: sys/netpfil/pf/pf.c =================================================================== --- sys/netpfil/pf/pf.c (revision 261814) +++ sys/netpfil/pf/pf.c (working copy) @@ -271,6 +271,7 @@ struct pf_addr *); static int pf_check_proto_cksum(struct mbuf *, int, int, u_int8_t, sa_family_t); +static struct pf_divert *pf_get_divert(struct mbuf *); static void pf_print_state_parts(struct pf_state *, struct pf_state_key *, struct pf_state_key *); static int pf_addr_wrap_neq(struct pf_addr_wrap *, @@ -5619,7 +5620,23 @@ return (0); } +struct pf_divert * +pf_get_divert(struct mbuf *m) +{ + struct m_tag *mtag; + if ((mtag = m_tag_find(m, PACKET_TAG_PF_DIVERT, NULL)) == NULL) { + mtag = m_tag_get(PACKET_TAG_PF_DIVERT, sizeof(struct pf_divert), + M_NOWAIT); + if (mtag == NULL) + return (NULL); + bzero(mtag + 1, sizeof(struct pf_divert)); + m_tag_prepend(m, mtag); + } + + return ((struct pf_divert *)(mtag + 1)); +} + #ifdef INET int pf_test(int dir, struct ifnet *ifp, struct mbuf **m0, struct inpcb *inp) @@ -5904,6 +5921,15 @@ } } + if (action == PF_PASS && r->divert.port && dir == PF_IN /*&& r->direction == PF_OUT*/ ) { + struct pf_divert *divert; + if ((divert = pf_get_divert(m))) { + m->m_flags |= M_FASTFWD_OURS; + divert->port = r->divert.port; + divert->addr.ipv4 = r->divert.addr.v4; + } + } + if (log) { struct pf_rule *lr; @@ -6275,9 +6301,14 @@ IN6_IS_ADDR_LOOPBACK(&pd.dst->v6)) m->m_flags |= M_SKIP_FIREWALL; - /* XXX: Anybody working on it?! */ - if (r->divert.port) - printf("pf: divert(9) is not supported for IPv6\n"); + if (action == PF_PASS && r->divert.port && dir == PF_IN /*&& r->direction == PF_OUT*/) { + struct pf_divert *divert; + if ((divert = pf_get_divert(m))) { + m->m_flags |= M_FASTFWD_OURS; + divert->port = r->divert.port; + divert->addr.ipv6 = r->divert.addr.v6; + } + } if (log) { struct pf_rule *lr; Index: sys/sys/mbuf.h =================================================================== --- sys/sys/mbuf.h (revision 261814) +++ sys/sys/mbuf.h (working copy) @@ -1023,6 +1023,7 @@ #define PACKET_TAG_DUMMYNET 15 /* dummynet info */ #define PACKET_TAG_DIVERT 17 /* divert info */ #define PACKET_TAG_IPFORWARD 18 /* ipforward info */ +#define PACKET_TAG_PF_DIVERT PACKET_TAG_IPFORWARD #define PACKET_TAG_MACLABEL (19 | MTAG_PERSISTENT) /* MAC label */ #define PACKET_TAG_PF (21 | MTAG_PERSISTENT) /* PF/ALTQ information */ #define PACKET_TAG_RTSOCKFAM 25 /* rtsock sa family */ >Release-Note: >Audit-Trail: >Unformatted:
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201404121320.s3CDK85m059408>