pf: handle TTL expired during nat64
If the TTL (or hop limit) expires during nat64 translation we may
need to send the error message in the original address family (i.e.
pre-translation).
We'd usually handle this in pf_route()/pf_route6(), but at that point we
have already translated the packet, making it difficult to include it in
the generated ICMP message.
Check for this case in pf_translate_af() and send icmp errors directly
from it.
PR: 291527
MFC after: 2 weeks
Sponsored by: Rubicon Communications, LLC ("Netgate")
Differential Revision: https://reviews.freebsd.org/D54166
This commit is contained in:
@@ -2518,7 +2518,6 @@ int pf_socket_lookup(struct pf_pdesc *);
|
||||
struct pf_state_key *pf_alloc_state_key(int);
|
||||
int pf_translate(struct pf_pdesc *, struct pf_addr *, u_int16_t,
|
||||
struct pf_addr *, u_int16_t, u_int16_t, int);
|
||||
int pf_translate_af(struct pf_pdesc *);
|
||||
bool pf_init_threshold(struct pf_kthreshold *, uint32_t, uint32_t);
|
||||
uint16_t pf_tagname2tag(const char *);
|
||||
#ifdef ALTQ
|
||||
|
||||
+20
-5
@@ -3605,8 +3605,8 @@ pf_change_icmp(struct pf_addr *ia, u_int16_t *ip, struct pf_addr *oa,
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
pf_translate_af(struct pf_pdesc *pd)
|
||||
static int
|
||||
pf_translate_af(struct pf_pdesc *pd, struct pf_krule *r)
|
||||
{
|
||||
#if defined(INET) && defined(INET6)
|
||||
struct mbuf *mp;
|
||||
@@ -3617,6 +3617,21 @@ pf_translate_af(struct pf_pdesc *pd)
|
||||
struct pf_fragment_tag *ftag;
|
||||
int hlen;
|
||||
|
||||
if (pd->ttl == 1) {
|
||||
/* We'd generate an ICMP error. Do so now rather than after af translation. */
|
||||
if (pd->af == AF_INET) {
|
||||
pf_send_icmp(pd->m, ICMP_TIMXCEED,
|
||||
ICMP_TIMXCEED_INTRANS, 0, pd->af, r,
|
||||
pd->act.rtableid);
|
||||
} else {
|
||||
pf_send_icmp(pd->m, ICMP6_TIME_EXCEEDED,
|
||||
ICMP6_TIME_EXCEED_TRANSIT, 0, pd->af, r,
|
||||
pd->act.rtableid);
|
||||
}
|
||||
|
||||
return (-1);
|
||||
}
|
||||
|
||||
hlen = pd->naf == AF_INET ? sizeof(*ip4) : sizeof(*ip6);
|
||||
|
||||
/* trim the old header */
|
||||
@@ -9186,7 +9201,7 @@ pf_route(struct pf_krule *r, struct ifnet *oifp,
|
||||
|
||||
if (pd->dir == PF_IN) {
|
||||
if (ip->ip_ttl <= IPTTLDEC) {
|
||||
if (r->rt != PF_DUPTO)
|
||||
if (r->rt != PF_DUPTO && pd->naf == pd->af)
|
||||
pf_send_icmp(m0, ICMP_TIMXCEED,
|
||||
ICMP_TIMXCEED_INTRANS, 0, pd->af, r,
|
||||
pd->act.rtableid);
|
||||
@@ -9511,7 +9526,7 @@ pf_route6(struct pf_krule *r, struct ifnet *oifp,
|
||||
|
||||
if (pd->dir == PF_IN) {
|
||||
if (ip6->ip6_hlim <= IPV6_HLIMDEC) {
|
||||
if (r->rt != PF_DUPTO)
|
||||
if (r->rt != PF_DUPTO && pd->naf == pd->af)
|
||||
pf_send_icmp(m0, ICMP6_TIME_EXCEEDED,
|
||||
ICMP6_TIME_EXCEED_TRANSIT, 0, pd->af, r,
|
||||
pd->act.rtableid);
|
||||
@@ -11354,7 +11369,7 @@ pf_test(sa_family_t af, int dir, int pflags, struct ifnet *ifp, struct mbuf **m0
|
||||
*m0 = NULL;
|
||||
break;
|
||||
case PF_AFRT:
|
||||
if (pf_translate_af(&pd)) {
|
||||
if (pf_translate_af(&pd, r)) {
|
||||
*m0 = pd.m;
|
||||
action = PF_DROP;
|
||||
break;
|
||||
|
||||
@@ -329,6 +329,42 @@ def test_noip6(self):
|
||||
for r in packets:
|
||||
r.show()
|
||||
|
||||
@pytest.mark.require_user("root")
|
||||
@pytest.mark.require_progs(["scapy"])
|
||||
def test_ttl_one(self):
|
||||
"""
|
||||
PR 291527: invalid ICMP error generated by nat64 router
|
||||
"""
|
||||
ifname = self.vnet.iface_alias_map["if1"].name
|
||||
gw_mac = self.vnet.iface_alias_map["if1"].epairb.ether
|
||||
ToolsHelper.print_output("/sbin/route -6 add default 2001:db8::1")
|
||||
|
||||
import scapy.all as sp
|
||||
|
||||
pkt = sp.Ether(dst=gw_mac) \
|
||||
/ sp.IPv6(dst="64:ff9b::198.51.100.2", hlim=1) \
|
||||
/ sp.SCTP(sport=1111, dport=2222) \
|
||||
/ sp.SCTPChunkInit(init_tag=1, n_in_streams=1, n_out_streams=1, \
|
||||
a_rwnd=1500, params=[])
|
||||
s = DelayedSend(pkt, sendif=ifname)
|
||||
|
||||
found = False
|
||||
packets = sp.sniff(iface=ifname, timeout=5)
|
||||
for r in packets:
|
||||
print("Reply packet:")
|
||||
r.show()
|
||||
|
||||
ip6 = r.getlayer(sp.IPv6)
|
||||
icmp6 = r.getlayer(sp.ICMPv6TimeExceeded)
|
||||
if not ip6 or not icmp6:
|
||||
continue
|
||||
assert ip6.src == "2001:db8::1"
|
||||
assert ip6.dst == "2001:db8::2"
|
||||
assert icmp6.type == 3 # Time exceeded
|
||||
assert icmp6.code == 0 # hop limit exceeded in transit
|
||||
found = True
|
||||
assert found
|
||||
|
||||
@pytest.mark.require_user("root")
|
||||
@pytest.mark.require_progs(["scapy"])
|
||||
def test_ttl_zero(self):
|
||||
|
||||
Reference in New Issue
Block a user