From e8d816753b9af4d465375cdb9e00ae6f5f109a36 Mon Sep 17 00:00:00 2001 From: sashan Date: Wed, 10 May 2023 22:42:51 +0000 Subject: [PATCH] nat-to may fail to insert state due to conflict on chosen source port number. This is typically indicated by 'wire key attach failed on...' message when pf(4) debugging is enabled. The problem is caused by glitch in pf_get_sport() which fails to discover conflict in advance. In order to fix it we must also calculate toeplitz hash in pf_get_sport() to initialize look up key properly. the bug has been kindly reported by joosepm _von_ gmail _dot_ com OK dlg@ --- sys/net/pf_lb.c | 14 +++++++++++++- sys/net/pfvar_priv.h | 5 ++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/sys/net/pf_lb.c b/sys/net/pf_lb.c index a3d8d6333cd..7bd6dfd607b 100644 --- a/sys/net/pf_lb.c +++ b/sys/net/pf_lb.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pf_lb.c,v 1.73 2023/01/04 10:31:55 dlg Exp $ */ +/* $OpenBSD: pf_lb.c,v 1.74 2023/05/10 22:42:51 sashan Exp $ */ /* * Copyright (c) 2001 Daniel Hartmeier @@ -196,18 +196,24 @@ pf_get_sport(struct pf_pdesc *pd, struct pf_rule *r, /* XXX bug: icmp states dont use the id on both * XXX sides (traceroute -I through nat) */ key.port[sidx] = pd->nsport; + key.hash = pf_pkt_hash(key.af, key.proto, &key.addr[0], + &key.addr[1], key.port[0], key.port[1]); if (pf_find_state_all(&key, dir, NULL) == NULL) { *nport = pd->nsport; return (0); } } else if (low == 0 && high == 0) { key.port[sidx] = pd->nsport; + key.hash = pf_pkt_hash(key.af, key.proto, &key.addr[0], + &key.addr[1], key.port[0], key.port[1]); if (pf_find_state_all(&key, dir, NULL) == NULL) { *nport = pd->nsport; return (0); } } else if (low == high) { key.port[sidx] = htons(low); + key.hash = pf_pkt_hash(key.af, key.proto, &key.addr[0], + &key.addr[1], key.port[0], key.port[1]); if (pf_find_state_all(&key, dir, NULL) == NULL) { *nport = htons(low); return (0); @@ -225,6 +231,9 @@ pf_get_sport(struct pf_pdesc *pd, struct pf_rule *r, /* low <= cut <= high */ for (tmp = cut; tmp <= high && tmp <= 0xffff; ++tmp) { key.port[sidx] = htons(tmp); + key.hash = pf_pkt_hash(key.af, key.proto, + &key.addr[0], &key.addr[1], key.port[0], + key.port[1]); if (pf_find_state_all(&key, dir, NULL) == NULL && !in_baddynamic(tmp, pd->proto)) { *nport = htons(tmp); @@ -234,6 +243,9 @@ pf_get_sport(struct pf_pdesc *pd, struct pf_rule *r, tmp = cut; for (tmp -= 1; tmp >= low && tmp <= 0xffff; --tmp) { key.port[sidx] = htons(tmp); + key.hash = pf_pkt_hash(key.af, key.proto, + &key.addr[0], &key.addr[1], key.port[0], + key.port[1]); if (pf_find_state_all(&key, dir, NULL) == NULL && !in_baddynamic(tmp, pd->proto)) { *nport = htons(tmp); diff --git a/sys/net/pfvar_priv.h b/sys/net/pfvar_priv.h index 13319709da1..e9e80f6196f 100644 --- a/sys/net/pfvar_priv.h +++ b/sys/net/pfvar_priv.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pfvar_priv.h,v 1.32 2023/05/08 23:52:36 dlg Exp $ */ +/* $OpenBSD: pfvar_priv.h,v 1.33 2023/05/10 22:42:51 sashan Exp $ */ /* * Copyright (c) 2001 Daniel Hartmeier @@ -405,6 +405,9 @@ void pf_state_peer_hton(const struct pf_state_peer *, struct pfsync_state_peer *); void pf_state_peer_ntoh(const struct pfsync_state_peer *, struct pf_state_peer *); +u_int16_t pf_pkt_hash(sa_family_t, uint8_t, + const struct pf_addr *, const struct pf_addr *, + uint16_t, uint16_t); #endif /* _KERNEL */ -- 2.20.1