kern/188511: divert-reply implementation for pf
PiBa-NL
PiBa.NL.dev at gmail.com
Sat Apr 12 13:30:01 UTC 2014
>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 at 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:
More information about the freebsd-bugs
mailing list