Page MenuHomeFreeBSD

D44488.id136146.diff
No OneTemporary

D44488.id136146.diff

diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -126,6 +126,8 @@
SDT_PROBE_DEFINE5(pf, ip, state, lookup, "struct pfi_kkif *",
"struct pf_state_key_cmp *", "int", "struct pf_pdesc *",
"struct pf_kstate *");
+SDT_PROBE_DEFINE2(pf, ip, state, conflict, "struct pf_state_key *",
+ "struct pf_state_key *");
SDT_PROBE_DEFINE2(pf, ip, , bound_iface, "struct pf_kstate *",
"struct pfi_kkif *");
SDT_PROBE_DEFINE4(pf, sctp, multihome, test, "struct pfi_kkif *",
@@ -4862,6 +4864,12 @@
u_int16_t mss = V_tcp_mssdflt;
u_short reason, sn_reason;
struct pf_krule_item *ri;
+ struct pf_keyhash *skh = NULL;
+ struct pf_state_key *cur = NULL;
+ struct pf_state_key *wk = NULL;
+ struct pf_state_key *stk = NULL;
+ int fixed_srcport = 0;
+
/* check maximums */
if (r->max_states &&
@@ -5026,13 +5034,49 @@
__func__, nr, sk, nk));
/* Swap sk/nk for PF_OUT. */
- if (pf_state_insert(BOUND_IFACE(s, kif), kif,
- (pd->dir == PF_IN) ? sk : nk,
- (pd->dir == PF_IN) ? nk : sk, s)) {
+ wk = (pd->dir == PF_IN) ? sk : nk;
+ stk = (pd->dir == PF_IN) ? nk : sk;
+
+tryagain:
+
+ /* check if we can insert stack key */
+ skh = &V_pf_keyhash[pf_hashkey(stk)];
+
+ PF_HASHROW_LOCK(skh);
+
+ LIST_FOREACH(cur, &skh->keys, entry)
+ if (bcmp(cur, stk, sizeof(struct pf_state_key_cmp)) == 0)
+ break;
+ PF_HASHROW_UNLOCK(skh);
+
+ if (cur != NULL) {
+ /* A mapping for this src port already exists */
+ SDT_PROBE2(pf, ip, state, conflict, stk, cur);
+
+ /* try to find an avialable sk src port */
+ if (stk->port[0] == IPPORT_MAX) {
+ REASON_SET(&reason, PFRES_STATEINS);
+ goto drop;
+ }
+ stk->port[0]++;
+ fixed_srcport = stk->port[0];
+
+ goto tryagain;
+ }
+
+ if (pf_state_insert(BOUND_IFACE(s, kif), kif, wk, stk, s)) {
REASON_SET(&reason, PFRES_STATEINS);
goto drop;
- } else
+ } else {
+ if (fixed_srcport) {
+ pf_change_ap(m, pd->src, &th->th_sport,
+ pd->ip_sum, &th->th_sum, &stk->addr[pd->sidx],
+ stk->port[pd->sidx], 0, pd->af);
+
+ pd->sport = &th->th_sport;
+ }
*sm = s;
+ }
if (tag > 0)
s->tag = tag;
diff --git a/tests/sys/netpfil/pf/Makefile b/tests/sys/netpfil/pf/Makefile
--- a/tests/sys/netpfil/pf/Makefile
+++ b/tests/sys/netpfil/pf/Makefile
@@ -67,6 +67,7 @@
pfsync_defer.py \
pft_ether.py \
pft_read_ipfix.py \
+ rdr_srcport.c \
utils.subr
${PACKAGE}FILESMODE_CVE-2019-5597.py= 0555
diff --git a/tests/sys/netpfil/pf/rdr.sh b/tests/sys/netpfil/pf/rdr.sh
--- a/tests/sys/netpfil/pf/rdr.sh
+++ b/tests/sys/netpfil/pf/rdr.sh
@@ -121,7 +121,96 @@
pft_cleanup
}
+
+atf_test_case "srcport" "cleanup"
+srcport_head()
+{
+ atf_set descr 'TCP rdr srcport modulation'
+ atf_set require.user root
+ atf_set require.progs python3
+}
+
+#
+# Test that rdr works for multiple TCP with same srcip and srcport.
+#
+# a:5454 <-----> b[0]:7777 <-----> c:8000
+# a:5454 <-----> b[1]:7777 <-----> c:8000
+# a:5454 <-----> b[2]:7777 <-----> c:8000
+#
+# Test configures three jails (a, b and c) and connects them together with b as
+# a router between a and c.
+#
+# TCP traffic to b on port 7777 is redirected to c on port 8000
+#
+# Multiple connections from a with the same srcport need to remapped to succeed
+#
+srcport_body()
+{
+ pft_init
+
+ j="rdr:srcport"
+ epair_one=$(vnet_mkepair)
+ epair_two=$(vnet_mkepair)
+
+ echo $epair_one
+ echo $epair_two
+
+ vnet_mkjail ${j}a ${epair_one}b
+ vnet_mkjail ${j}b ${epair_one}a ${epair_two}a
+ vnet_mkjail ${j}c ${epair_two}b
+
+ # configure addresses for b
+ jexec ${j}b ifconfig lo0 up
+ jexec ${j}b ifconfig ${epair_one}a inet 198.51.100.1/24 up
+ jexec ${j}b ifconfig ${epair_one}a inet alias 198.51.100.2/24
+ jexec ${j}b ifconfig ${epair_one}a inet alias 198.51.100.3/24
+ jexec ${j}b ifconfig ${epair_two}a inet 203.0.113.1/24 up
+
+ # configure addresses for a
+ jexec ${j}a ifconfig lo0 up
+ jexec ${j}a ifconfig ${epair_one}b inet 198.51.100.50/24 up
+
+ # configure addresses for c
+ jexec ${j}c ifconfig lo0 up
+ jexec ${j}c ifconfig ${epair_two}b inet 203.0.113.50/24 up
+
+ # enable forwarding in the b jail
+ jexec ${j}b sysctl net.inet.ip.forwarding=1
+
+ jexec ${j}b pfctl -e
+
+ pft_set_rules ${j}b \
+ "nat on ${epair_one}a inet from 203.0.113.0/24 to any -> ${epair_one}a port 1024:65535 static-port" \
+ "rdr on ${epair_one}a proto tcp from any to ${epair_one}a port 7777 -> 203.0.113.50 port 8000"
+
+ # add routes so c can reply to a
+ jexec ${j}c route add 198.51.100.0/24 203.0.113.1
+
+ # Check that c can reach a over the router
+ atf_check -s exit:0 -o ignore \
+ jexec ${j}a ping -c 1 198.51.100.50
+
+ # start a web server and give it a second to start
+ jexec ${j}c python3 -m http.server &
+ sleep 1
+
+ # Use SO_REUSEPORT to make multiple connections with the same srcport
+ tmp=`pwd`
+ jexec ${j}a clang -o ${tmp}/rdr_srcport $(atf_get_srcdir)/rdr_srcport.c
+
+ # http from a to b with a redirect -> a ---> b
+ atf_check -s exit:0 -o ignore \
+ jexec ${j}a ${tmp}/rdr_srcport 198.51.100.1 198.51.100.2 198.51.100.3
+
+}
+
+srcport()
+{
+ pft_cleanup
+}
+
atf_init_test_cases()
{
atf_add_test_case "tcp_v6"
+ atf_add_test_case "srcport"
}
diff --git a/tests/sys/netpfil/pf/rdr_srcport.c b/tests/sys/netpfil/pf/rdr_srcport.c
new file mode 100644
--- /dev/null
+++ b/tests/sys/netpfil/pf/rdr_srcport.c
@@ -0,0 +1,70 @@
+#include <sys/socket.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+static int
+newsock(void)
+{
+ struct sockaddr_in sin;
+ int on, s;
+
+ s = socket(PF_INET, SOCK_STREAM, 0);
+ assert(s >= 0);
+
+ on = 1;
+ assert(setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)) == 0);
+
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(5454);
+ sin.sin_addr.s_addr = INADDR_ANY;
+ memset(&sin.sin_zero, 0, sizeof(sin.sin_zero));
+
+ assert(bind(s, (struct sockaddr *)&sin, sizeof(sin)) == 0);
+ return (s);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int *sock;
+ int *err;
+ int remotes;
+ int i;
+
+ struct sockaddr_in dest;
+ char outbuf[] = "GET / HTTP/1.1\nConnection: Keep-alive\n\n";
+
+ sock = calloc(argc, sizeof(sock));
+ err = calloc(argc, sizeof(sock));
+
+ remotes = argc - 1;
+
+ for (i = 0; i < remotes; i++) {
+ sock[i] = newsock();
+ printf("Connecting to %s:7777\n", argv[i + 1]);
+ dest.sin_family = AF_INET;
+ dest.sin_port = htons(7777);
+ dest.sin_addr.s_addr = inet_addr(argv[i + 1]);
+ memset(&dest.sin_zero, 0, sizeof(dest.sin_zero));
+ err[i] = connect(sock[i], (struct sockaddr *)&dest, sizeof(dest));
+ printf("Socket[%d] ret=%d errno=%d\n", i, err[i], errno);
+ }
+
+ for (i = 0; i < remotes; i++) {
+ assert(err[i] == 0);
+ assert(write(sock[i], outbuf, sizeof(outbuf) - 1) == sizeof(outbuf) - 1);
+ }
+
+ printf("Holding the connection open...\n");
+ sleep(1);
+ printf("Exiting\n");
+
+}

File Metadata

Mime Type
text/plain
Expires
Wed, Mar 25, 11:52 PM (12 h, 39 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
30352965
Default Alt Text
D44488.id136146.diff (6 KB)

Event Timeline