From b6de2904f05cfb3334f36bee61052441c5809874 Mon Sep 17 00:00:00 2001
From: Ruben van Staveren <ruben@Verweg.com>
Date: Fri, 20 Jun 2014 14:11:25 +0200
Subject: [PATCH 1/9] FreeBSD way of doing transparent proxy: work in progress

---
 common.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/common.c b/common.c
index a297176..7af9ee1 100644
--- a/common.c
+++ b/common.c
@@ -120,8 +120,13 @@ int bind_peer(int fd, int fd_from)
      * got here */
     res = getpeername(fd_from, from.ai_addr, &from.ai_addrlen);
     CHECK_RES_RETURN(res, "getpeername");
-    res = setsockopt(fd, IPPROTO_IP, IP_TRANSPARENT, &trans, sizeof(trans));
-    CHECK_RES_DIE(res, "setsockopt");
+    if (from.ai_addr->sa_family==AF_INET) { /* IPv4 */
+        res = setsockopt(fd, IPPROTO_IP, IP_BINDANY, &trans, sizeof(trans));
+	CHECK_RES_RETURN(res, "setsockopt IP_BINDANY");
+    } else { /* IPv6 */
+        res = setsockopt(fd, IPPROTO_IPV6, IPV6_BINDANY, &trans, sizeof(trans));
+	CHECK_RES_RETURN(res, "setsockopt IPV6_BINDANY");
+    }
     res = bind(fd, from.ai_addr, from.ai_addrlen);
     CHECK_RES_RETURN(res, "bind");
 
@@ -143,6 +148,8 @@ int connect_addr(struct connection *cnx, int fd_from)
             fprintf(stderr, "connecting to %s family %d len %d\n", 
                     sprintaddr(buf, sizeof(buf), a),
                     a->ai_addr->sa_family, a->ai_addrlen);
+
+	/* XXX Needs to match ai_family from fd_from when being transparent! */
         fd = socket(a->ai_family, SOCK_STREAM, 0);
         if (fd == -1) {
             log_message(LOG_ERR, "forward to %s failed:socket: %s\n",

From dedb3672d785210ae80b44eab36ea65dd46461e8 Mon Sep 17 00:00:00 2001
From: Ruben van Staveren <ruben@Verweg.com>
Date: Tue, 22 Jul 2014 19:36:29 +0200
Subject: [PATCH 2/9] Have USELIBWRAP redefineable

---
 Makefile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Makefile b/Makefile
index 800af29..2e9de22 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@
 
 VERSION=$(shell ./genver.sh -r)
 USELIBCONFIG=1	# Use libconfig? (necessary to use configuration files)
-USELIBWRAP=	# Use libwrap?
+USELIBWRAP?=	# Use libwrap?
 USELIBCAP=	# Use libcap?
 COV_TEST= 	# Perform test coverage?
 PREFIX=/usr/local

From 7d23a5523670075f6077d94efc4e1a1d9a8ebedc Mon Sep 17 00:00:00 2001
From: Ruben van Staveren <ruben@Verweg.com>
Date: Tue, 22 Jul 2014 19:36:40 +0200
Subject: [PATCH 3/9] When transparent, make sure both connections use the same
 address family

---
 common.c | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/common.c b/common.c
index 7af9ee1..139481e 100644
--- a/common.c
+++ b/common.c
@@ -139,11 +139,22 @@ int bind_peer(int fd, int fd_from)
  * of new file descriptor. */
 int connect_addr(struct connection *cnx, int fd_from)
 {
-    struct addrinfo *a;
+    struct addrinfo *a, from;
+    struct sockaddr_storage ss;
     char buf[NI_MAXHOST];
     int fd, res;
 
+    memset(&from, 0, sizeof(from));
+    from.ai_addr = (struct sockaddr*)&ss;
+    from.ai_addrlen = sizeof(ss);
+
+    res = getpeername(fd_from, from.ai_addr, &from.ai_addrlen);
+    CHECK_RES_RETURN(res, "getpeername");
+
     for (a = cnx->proto->saddr; a; a = a->ai_next) {
+        /* When transparent, make sure both connections use the same address family */
+        if (transparent && a->ai_family != from.ai_addr->sa_family)
+            continue;
         if (verbose) 
             fprintf(stderr, "connecting to %s family %d len %d\n", 
                     sprintaddr(buf, sizeof(buf), a),

From e246536be2a704b130902d956c8b55544552ec3c Mon Sep 17 00:00:00 2001
From: Ruben van Staveren <ruben@Verweg.com>
Date: Fri, 20 Jun 2014 14:11:25 +0200
Subject: [PATCH 4/9] FreeBSD way of doing transparent proxy: work in progress

---
 common.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/common.c b/common.c
index a297176..7af9ee1 100644
--- a/common.c
+++ b/common.c
@@ -120,8 +120,13 @@ int bind_peer(int fd, int fd_from)
      * got here */
     res = getpeername(fd_from, from.ai_addr, &from.ai_addrlen);
     CHECK_RES_RETURN(res, "getpeername");
-    res = setsockopt(fd, IPPROTO_IP, IP_TRANSPARENT, &trans, sizeof(trans));
-    CHECK_RES_DIE(res, "setsockopt");
+    if (from.ai_addr->sa_family==AF_INET) { /* IPv4 */
+        res = setsockopt(fd, IPPROTO_IP, IP_BINDANY, &trans, sizeof(trans));
+	CHECK_RES_RETURN(res, "setsockopt IP_BINDANY");
+    } else { /* IPv6 */
+        res = setsockopt(fd, IPPROTO_IPV6, IPV6_BINDANY, &trans, sizeof(trans));
+	CHECK_RES_RETURN(res, "setsockopt IPV6_BINDANY");
+    }
     res = bind(fd, from.ai_addr, from.ai_addrlen);
     CHECK_RES_RETURN(res, "bind");
 
@@ -143,6 +148,8 @@ int connect_addr(struct connection *cnx, int fd_from)
             fprintf(stderr, "connecting to %s family %d len %d\n", 
                     sprintaddr(buf, sizeof(buf), a),
                     a->ai_addr->sa_family, a->ai_addrlen);
+
+	/* XXX Needs to match ai_family from fd_from when being transparent! */
         fd = socket(a->ai_family, SOCK_STREAM, 0);
         if (fd == -1) {
             log_message(LOG_ERR, "forward to %s failed:socket: %s\n",

From 42425a837368d964c0856e4b7cede057870451f8 Mon Sep 17 00:00:00 2001
From: Ruben van Staveren <ruben@Verweg.com>
Date: Tue, 22 Jul 2014 19:36:29 +0200
Subject: [PATCH 5/9] Have USELIBWRAP redefineable

---
 Makefile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Makefile b/Makefile
index 800af29..2e9de22 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@
 
 VERSION=$(shell ./genver.sh -r)
 USELIBCONFIG=1	# Use libconfig? (necessary to use configuration files)
-USELIBWRAP=	# Use libwrap?
+USELIBWRAP?=	# Use libwrap?
 USELIBCAP=	# Use libcap?
 COV_TEST= 	# Perform test coverage?
 PREFIX=/usr/local

From e2fc09148251bfbe0705380d9125a46beaa118e4 Mon Sep 17 00:00:00 2001
From: Ruben van Staveren <ruben@Verweg.com>
Date: Tue, 22 Jul 2014 19:36:40 +0200
Subject: [PATCH 6/9] When transparent, make sure both connections use the same
 address family

---
 common.c | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/common.c b/common.c
index 7af9ee1..139481e 100644
--- a/common.c
+++ b/common.c
@@ -139,11 +139,22 @@ int bind_peer(int fd, int fd_from)
  * of new file descriptor. */
 int connect_addr(struct connection *cnx, int fd_from)
 {
-    struct addrinfo *a;
+    struct addrinfo *a, from;
+    struct sockaddr_storage ss;
     char buf[NI_MAXHOST];
     int fd, res;
 
+    memset(&from, 0, sizeof(from));
+    from.ai_addr = (struct sockaddr*)&ss;
+    from.ai_addrlen = sizeof(ss);
+
+    res = getpeername(fd_from, from.ai_addr, &from.ai_addrlen);
+    CHECK_RES_RETURN(res, "getpeername");
+
     for (a = cnx->proto->saddr; a; a = a->ai_next) {
+        /* When transparent, make sure both connections use the same address family */
+        if (transparent && a->ai_family != from.ai_addr->sa_family)
+            continue;
         if (verbose) 
             fprintf(stderr, "connecting to %s family %d len %d\n", 
                     sprintaddr(buf, sizeof(buf), a),

From 36cf99697b40702b760b2c8c7995212bc4f5caa4 Mon Sep 17 00:00:00 2001
From: Ruben van Staveren <ruben@Verweg.com>
Date: Tue, 22 Jul 2014 20:30:52 +0200
Subject: [PATCH 7/9] Add instruction for FreeBSD

---
 README.md | 40 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 39 insertions(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 2b7f44d..37f3220 100644
--- a/README.md
+++ b/README.md
@@ -217,13 +217,15 @@ transparent proxying, just don't use it (or use the libcap method).
 Transparent proxy support
 -------------------------
 
-On Linux (only?) you can use the `--transparent` option to
+On Linux and FreeBSD you can use the `--transparent` option to
 request transparent proying. This means services behind `sslh`
 (Apache, `sshd` and so on) will see the external IP and ports
 as if the external world connected directly to them. This
 simplifies IP-based access control (or makes it possible at
 all).
 
+Linux:
+
 `sslh` needs extended rights to perform this: you'll need to
 give it `CAP_NET_ADMIN` capabilities (see appropriate chapter)
 or run it as root (but don't do that).
@@ -241,6 +243,42 @@ this scheme -- let me know if you manage that:
 	# ip rule add fwmark 0x1 lookup 100
 	# ip route add local 0.0.0.0/0 dev lo table 100
 
+FreeBSD:
+
+Given you have no firewall defined yet, you can use the following configuration
+to have ipfw properly redirect traffic back to sslh
+
+/etc/rc.conf
+firewall_enable="YES"
+firewall_type="open"
+firewall_logif="YES"
+firewall_coscripts="/etc/ipfw/sslh.rules"
+
+
+/etc/ipfw/sslh.rules
+#! /bin/sh
+
+# ssl
+ipfw add 20000 fwd 192.0.2.1,443 log tcp from 192.0.2.1 8443 to any out
+ipfw add 20010 fwd 2001:db8::1,443 log tcp from 2001:db8::1 8443 to any out
+
+# ssh
+ipfw add 20100 fwd 192.0.2.1,443 log tcp from 192.0.2.1 8022 to any out
+ipfw add 20110 fwd 2001:db8::1,443 log tcp from 2001:db8::1 8022 to any out
+
+# xmpp
+ipfw add 20200 fwd 192.0.2.1,443 log tcp from 192.0.2.1 5222 to any out
+ipfw add 20210 fwd 2001:db8::1,443 log tcp from 2001:db8::1 5222 to any out
+
+# openvpn (running on other internal system)
+ipfw add 20300 fwd 192.0.2.1,443 log tcp from 198.51.100.7 1194 to any out
+ipfw add 20310 fwd 2001:db8::1,443 log tcp from 2001:db8:1::7 1194 to any out
+
+
+
+
+
+
 This will only work if `sslh` does not use any loopback
 addresses (no `127.0.0.1` or `localhost`), you'll need to use
 explicit IP addresses (or names):

From 0d8e2438de261523952af08263f4c0c2a763a4b9 Mon Sep 17 00:00:00 2001
From: Ruben van Staveren <ruben@Verweg.com>
Date: Tue, 22 Jul 2014 21:43:03 +0200
Subject: [PATCH 8/9] Correct markdown

---
 README.md | 39 +++++++++++++++++++--------------------
 1 file changed, 19 insertions(+), 20 deletions(-)

diff --git a/README.md b/README.md
index 37f3220..9ea4216 100644
--- a/README.md
+++ b/README.md
@@ -248,35 +248,34 @@ FreeBSD:
 Given you have no firewall defined yet, you can use the following configuration
 to have ipfw properly redirect traffic back to sslh
 
-/etc/rc.conf
-firewall_enable="YES"
-firewall_type="open"
-firewall_logif="YES"
-firewall_coscripts="/etc/ipfw/sslh.rules"
+	/etc/rc.conf
+	firewall_enable="YES"
+	firewall_type="open"
+	firewall_logif="YES"
+	firewall_coscripts="/etc/ipfw/sslh.rules"
 
 
 /etc/ipfw/sslh.rules
-#! /bin/sh
 
-# ssl
-ipfw add 20000 fwd 192.0.2.1,443 log tcp from 192.0.2.1 8443 to any out
-ipfw add 20010 fwd 2001:db8::1,443 log tcp from 2001:db8::1 8443 to any out
+	#! /bin/sh
 
-# ssh
-ipfw add 20100 fwd 192.0.2.1,443 log tcp from 192.0.2.1 8022 to any out
-ipfw add 20110 fwd 2001:db8::1,443 log tcp from 2001:db8::1 8022 to any out
-
-# xmpp
-ipfw add 20200 fwd 192.0.2.1,443 log tcp from 192.0.2.1 5222 to any out
-ipfw add 20210 fwd 2001:db8::1,443 log tcp from 2001:db8::1 5222 to any out
-
-# openvpn (running on other internal system)
-ipfw add 20300 fwd 192.0.2.1,443 log tcp from 198.51.100.7 1194 to any out
-ipfw add 20310 fwd 2001:db8::1,443 log tcp from 2001:db8:1::7 1194 to any out
+	# ssl
+	ipfw add 20000 fwd 192.0.2.1,443 log tcp from 192.0.2.1 8443 to any out
+	ipfw add 20010 fwd 2001:db8::1,443 log tcp from 2001:db8::1 8443 to any out
 
+	# ssh
+	ipfw add 20100 fwd 192.0.2.1,443 log tcp from 192.0.2.1 8022 to any out
+	ipfw add 20110 fwd 2001:db8::1,443 log tcp from 2001:db8::1 8022 to any out
 
+	# xmpp
+	ipfw add 20200 fwd 192.0.2.1,443 log tcp from 192.0.2.1 5222 to any out
+	ipfw add 20210 fwd 2001:db8::1,443 log tcp from 2001:db8::1 5222 to any out
 
+	# openvpn (running on other internal system)
+	ipfw add 20300 fwd 192.0.2.1,443 log tcp from 198.51.100.7 1194 to any out
+	ipfw add 20310 fwd 2001:db8::1,443 log tcp from 2001:db8:1::7 1194 to any out
 
+General notes:
 
 
 This will only work if `sslh` does not use any loopback

From ece6e28e453e8361b583570d95594fefb40f7647 Mon Sep 17 00:00:00 2001
From: Ruben van Staveren <ruben@Verweg.com>
Date: Thu, 24 Jul 2014 17:29:53 +0200
Subject: [PATCH 9/9] #ifdef IP_BINDANY/IPV6_BINDANY cases

---
 common.c | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/common.c b/common.c
index 139481e..43285bf 100644
--- a/common.c
+++ b/common.c
@@ -120,13 +120,20 @@ int bind_peer(int fd, int fd_from)
      * got here */
     res = getpeername(fd_from, from.ai_addr, &from.ai_addrlen);
     CHECK_RES_RETURN(res, "getpeername");
+#ifndef IP_BINDANY /* use IP_TRANSPARENT */
+    res = setsockopt(fd, IPPROTO_IP, IP_TRANSPARENT, &trans, sizeof(trans));
+    CHECK_RES_DIE(res, "setsockopt");
+#else
     if (from.ai_addr->sa_family==AF_INET) { /* IPv4 */
         res = setsockopt(fd, IPPROTO_IP, IP_BINDANY, &trans, sizeof(trans));
-	CHECK_RES_RETURN(res, "setsockopt IP_BINDANY");
+        CHECK_RES_RETURN(res, "setsockopt IP_BINDANY");
+#ifdef IPV6_BINDANY
     } else { /* IPv6 */
         res = setsockopt(fd, IPPROTO_IPV6, IPV6_BINDANY, &trans, sizeof(trans));
-	CHECK_RES_RETURN(res, "setsockopt IPV6_BINDANY");
+        CHECK_RES_RETURN(res, "setsockopt IPV6_BINDANY");
+#endif /* IPV6_BINDANY */
     }
+#endif /* IP_TRANSPARENT / IP_BINDANY */
     res = bind(fd, from.ai_addr, from.ai_addrlen);
     CHECK_RES_RETURN(res, "bind");
 
@@ -160,7 +167,7 @@ int connect_addr(struct connection *cnx, int fd_from)
                     sprintaddr(buf, sizeof(buf), a),
                     a->ai_addr->sa_family, a->ai_addrlen);
 
-	/* XXX Needs to match ai_family from fd_from when being transparent! */
+        /* XXX Needs to match ai_family from fd_from when being transparent! */
         fd = socket(a->ai_family, SOCK_STREAM, 0);
         if (fd == -1) {
             log_message(LOG_ERR, "forward to %s failed:socket: %s\n",