diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h --- a/sys/net/pfvar.h +++ b/sys/net/pfvar.h @@ -2518,7 +2518,6 @@ 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 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 @@ -3605,8 +3605,8 @@ } } -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 @@ 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 @@ 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 @@ 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 @@ *m0 = NULL; break; case PF_AFRT: - if (pf_translate_af(&pd)) { + if (pf_translate_af(&pd, r)) { *m0 = pd.m; action = PF_DROP; break; diff --git a/tests/sys/netpfil/pf/nat64.py b/tests/sys/netpfil/pf/nat64.py --- a/tests/sys/netpfil/pf/nat64.py +++ b/tests/sys/netpfil/pf/nat64.py @@ -329,6 +329,42 @@ 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):