>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     [email protected]:/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:
_______________________________________________
[email protected] mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-bugs
To unsubscribe, send any mail to "[email protected]"

Reply via email to