git: 2f6b4611b5b8 - stable/14 - pf: stricter state checking for ICMP and ICMPv6 packets
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Wed, 31 Jul 2024 07:41:08 UTC
The branch stable/14 has been updated by kp: URL: https://cgit.FreeBSD.org/src/commit/?id=2f6b4611b5b847aee1ff8d5017a0f8a657f4101d commit 2f6b4611b5b847aee1ff8d5017a0f8a657f4101d Author: Kristof Provost <kp@FreeBSD.org> AuthorDate: 2024-07-09 13:59:33 +0000 Commit: Kristof Provost <kp@FreeBSD.org> CommitDate: 2024-07-31 07:39:53 +0000 pf: stricter state checking for ICMP and ICMPv6 packets Include the ICMP type in one port of the state key, using the type to determine which side should be the id, and which should be the type. Also: - Handle ICMP6 messages which are typically sent to multicast addresses but recieve unicast replies, by doing fallthrough lookups against the correct multicast address. - Clear up some mistaken assumptions in the PF code: - Not all ICMP packets have an icmp_id, so simulate one based on other data if we can, otherwise set it to 0. - Don't modify the icmp id field in NAT unless it's echo - Use the full range of possible id's when NATing icmp6 echoy ok henning marco testing matthieu todd MFC after: 1 day Obtained From: OpenBSD, mcbride <mcbride@openbsd.org> 70bf7555ef4c Sponsored by: Rubicon Communications, LLC ("Netgate") (cherry picked from commit 534ee17e61ee094ec175703bc50e88ff6587703e) --- sys/netpfil/pf/pf.c | 381 ++++++++++++++++++++++++++++++++++++++----------- sys/netpfil/pf/pf_lb.c | 19 ++- 2 files changed, 317 insertions(+), 83 deletions(-) diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c index 1a4ef22ce5de..b3c4d47cabc2 100644 --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -295,6 +295,8 @@ static void pf_change_ap(struct mbuf *, struct pf_addr *, u_int16_t *, u_int16_t, u_int8_t, sa_family_t); static int pf_modulate_sack(struct mbuf *, int, struct pf_pdesc *, struct tcphdr *, struct pf_state_peer *); +int pf_icmp_mapping(struct pf_pdesc *, u_int8_t, int *, + int *, u_int16_t *, u_int16_t *); static void pf_change_icmp(struct pf_addr *, u_int16_t *, struct pf_addr *, struct pf_addr *, u_int16_t, u_int16_t *, u_int16_t *, u_int16_t *, @@ -341,6 +343,10 @@ static int pf_test_state_tcp(struct pf_kstate **, static int pf_test_state_udp(struct pf_kstate **, struct pfi_kkif *, struct mbuf *, int, void *, struct pf_pdesc *); +int pf_icmp_state_lookup(struct pf_state_key_cmp *, + struct pf_pdesc *, struct pf_kstate **, struct mbuf *, + int, struct pfi_kkif *, u_int16_t, u_int16_t, + int, int *, int); static int pf_test_state_icmp(struct pf_kstate **, struct pfi_kkif *, struct mbuf *, int, void *, struct pf_pdesc *, u_short *); @@ -393,6 +399,8 @@ extern struct proc *pf_purge_proc; VNET_DEFINE(struct pf_limit, pf_limits[PF_LIMIT_MAX]); +enum { PF_ICMP_MULTI_NONE, PF_ICMP_MULTI_SOLICITED, PF_ICMP_MULTI_LINK }; + #define PACKET_UNDO_NAT(_m, _pd, _off, _s) \ do { \ struct pf_state_key *nk; \ @@ -1745,6 +1753,142 @@ pf_isforlocal(struct mbuf *m, int af) return (false); } +int +pf_icmp_mapping(struct pf_pdesc *pd, u_int8_t type, + int *icmp_dir, int *multi, u_int16_t *icmpid, u_int16_t *icmptype) +{ + /* + * ICMP types marked with PF_OUT are typically responses to + * PF_IN, and will match states in the opposite direction. + * PF_IN ICMP types need to match a state with that type. + */ + *icmp_dir = PF_OUT; + *multi = PF_ICMP_MULTI_LINK; + /* Queries (and responses) */ + switch (type) { + case ICMP_ECHO: + *icmp_dir = PF_IN; + case ICMP_ECHOREPLY: + *icmptype = ICMP_ECHO; + *icmpid = pd->hdr.icmp.icmp_id; + break; + + case ICMP_TSTAMP: + *icmp_dir = PF_IN; + case ICMP_TSTAMPREPLY: + *icmptype = ICMP_TSTAMP; + *icmpid = 0; /* Time is not a secret. */ + break; + + case ICMP_IREQ: + *icmp_dir = PF_IN; + case ICMP_IREQREPLY: + *icmptype = ICMP_IREQ; + *icmpid = 0; /* Nothing sane to match on! */ + break; + + case ICMP_MASKREQ: + *icmp_dir = PF_IN; + case ICMP_MASKREPLY: + *icmptype = ICMP_MASKREQ; + *icmpid = 0; /* Nothing sane to match on! */ + break; + + case ICMP_IPV6_WHEREAREYOU: + *icmp_dir = PF_IN; + case ICMP_IPV6_IAMHERE: + *icmptype = ICMP_IPV6_WHEREAREYOU; + *icmpid = 0; /* Nothing sane to match on! */ + break; + + case ICMP_MOBILE_REGREQUEST: + *icmp_dir = PF_IN; + case ICMP_MOBILE_REGREPLY: + *icmptype = ICMP_MOBILE_REGREQUEST; + *icmpid = 0; /* Nothing sane to match on! */ + break; + + case ICMP_ROUTERSOLICIT: + *icmp_dir = PF_IN; + case ICMP_ROUTERADVERT: + *icmptype = ICMP_ROUTERSOLICIT; + *icmpid = 0; /* Nothing sane to match on! */ + break; + +#ifdef INET6 + case ICMP6_ECHO_REQUEST: + *icmp_dir = PF_IN; + case ICMP6_ECHO_REPLY: + *icmptype = ICMP6_ECHO_REQUEST; + *icmpid = pd->hdr.icmp6.icmp6_id; + break; + + case MLD_LISTENER_QUERY: + *icmp_dir = PF_IN; + case MLD_LISTENER_REPORT: { + *icmptype = MLD_LISTENER_QUERY; + *icmpid = 0; + break; + } + + /* ICMP6_FQDN and ICMP6_NI query/reply are the same type as ICMP6_WRU */ + case ICMP6_WRUREQUEST: + *icmp_dir = PF_IN; + case ICMP6_WRUREPLY: + *icmptype = ICMP6_WRUREQUEST; + *icmpid = 0; /* Nothing sane to match on! */ + break; + + case MLD_MTRACE: + *icmp_dir = PF_IN; + case MLD_MTRACE_RESP: + *icmptype = MLD_MTRACE; + *icmpid = 0; /* Nothing sane to match on! */ + break; + + case ND_NEIGHBOR_SOLICIT: + *icmp_dir = PF_IN; + case ND_NEIGHBOR_ADVERT: { + *icmptype = ND_NEIGHBOR_SOLICIT; + *multi = PF_ICMP_MULTI_SOLICITED; + *icmpid = 0; + break; + } + +#endif /* INET6 */ + /* These ICMP types map to other connections */ + case ICMP_UNREACH: + case ICMP_SOURCEQUENCH: + case ICMP_REDIRECT: + case ICMP_TIMXCEED: + case ICMP_PARAMPROB: +#ifdef INET6 + /* + * ICMP6_TIME_EXCEEDED is the same type as ICMP_UNREACH + * ND_REDIRECT can't be in this list because the triggering packet + * header is optional. + */ + case ICMP6_PACKET_TOO_BIG: +#endif /* INET6 */ + /* These will not be used, but set them anyways */ + *icmp_dir = PF_IN; + *icmptype = htons(type); + *icmpid = 0; + return (1); /* These types are matched to other state */ + /* + * All remaining ICMP types get their own states, + * and will only match in one direction. + */ + default: + *icmp_dir = PF_IN; + *icmptype = type; + *icmpid = 0; + break; + } + HTONS(*icmptype); + return (0); +} + void pf_intr(void *v) { @@ -4408,8 +4552,8 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm, struct pfi_kkif *kif, int tag = -1; int asd = 0; int match = 0; - int state_icmp = 0; - u_int16_t sport = 0, dport = 0; + int state_icmp = 0, icmp_dir, multi; + u_int16_t sport = 0, dport = 0, virtual_type, virtual_id; u_int16_t bproto_sum = 0, bip_sum = 0; u_int8_t icmptype = 0, icmpcode = 0; struct pf_kanchor_stackframe anchor_stack[PF_ANCHOR_STACKSIZE]; @@ -4443,33 +4587,37 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm, struct pfi_kkif *kif, case IPPROTO_ICMP: if (pd->af != AF_INET) break; - sport = dport = pd->hdr.icmp.icmp_id; hdrlen = sizeof(pd->hdr.icmp); icmptype = pd->hdr.icmp.icmp_type; icmpcode = pd->hdr.icmp.icmp_code; - - if (icmptype == ICMP_UNREACH || - icmptype == ICMP_SOURCEQUENCH || - icmptype == ICMP_REDIRECT || - icmptype == ICMP_TIMXCEED || - icmptype == ICMP_PARAMPROB) - state_icmp++; + state_icmp = pf_icmp_mapping(pd, icmptype, + &icmp_dir, &multi, &virtual_id, &virtual_type); + if (icmp_dir == PF_IN) { + sport = virtual_id; + dport = virtual_type; + } else { + sport = virtual_type; + dport = virtual_id; + } break; #endif /* INET */ #ifdef INET6 case IPPROTO_ICMPV6: if (af != AF_INET6) break; - sport = dport = pd->hdr.icmp6.icmp6_id; hdrlen = sizeof(pd->hdr.icmp6); icmptype = pd->hdr.icmp6.icmp6_type; icmpcode = pd->hdr.icmp6.icmp6_code; + state_icmp = pf_icmp_mapping(pd, icmptype, + &icmp_dir, &multi, &virtual_id, &virtual_type); + if (icmp_dir == PF_IN) { + sport = virtual_id; + dport = virtual_type; + } else { + sport = virtual_type; + dport = virtual_id; + } - if (icmptype == ICMP6_DST_UNREACH || - icmptype == ICMP6_PACKET_TOO_BIG || - icmptype == ICMP6_TIME_EXCEEDED || - icmptype == ICMP6_PARAM_PROB) - state_icmp++; break; #endif /* INET6 */ default: @@ -4563,7 +4711,6 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm, struct pfi_kkif *kif, } #ifdef INET case IPPROTO_ICMP: - nk->port[0] = nk->port[1]; if (PF_ANEQ(saddr, &nk->addr[pd->sidx], AF_INET)) pf_change_a(&saddr->v4.s_addr, pd->ip_sum, nk->addr[pd->sidx].v4.s_addr, 0); @@ -4572,11 +4719,12 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm, struct pfi_kkif *kif, pf_change_a(&daddr->v4.s_addr, pd->ip_sum, nk->addr[pd->didx].v4.s_addr, 0); - if (nk->port[1] != pd->hdr.icmp.icmp_id) { + if (virtual_type == ICMP_ECHO && + nk->port[pd->sidx] != pd->hdr.icmp.icmp_id) { pd->hdr.icmp.icmp_cksum = pf_cksum_fixup( pd->hdr.icmp.icmp_cksum, sport, - nk->port[1], 0); - pd->hdr.icmp.icmp_id = nk->port[1]; + nk->port[pd->sidx], 0); + pd->hdr.icmp.icmp_id = nk->port[pd->sidx]; pd->sport = &pd->hdr.icmp.icmp_id; } m_copyback(m, off, ICMP_MINLEN, (caddr_t)&pd->hdr.icmp); @@ -4584,7 +4732,6 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm, struct pfi_kkif *kif, #endif /* INET */ #ifdef INET6 case IPPROTO_ICMPV6: - nk->port[0] = nk->port[1]; if (PF_ANEQ(saddr, &nk->addr[pd->sidx], AF_INET6)) pf_change_a6(saddr, &pd->hdr.icmp6.icmp6_cksum, &nk->addr[pd->sidx], 0); @@ -6417,15 +6564,73 @@ pf_multihome_scan_asconf(struct mbuf *m, int start, int len, return (pf_multihome_scan(m, start, len, pd, kif, SCTP_ADD_IP_ADDRESS)); } +int +pf_icmp_state_lookup(struct pf_state_key_cmp *key, struct pf_pdesc *pd, + struct pf_kstate **state, struct mbuf *m, int direction, struct pfi_kkif *kif, + u_int16_t icmpid, u_int16_t type, int icmp_dir, int *iidx, int multi) +{ + key->af = pd->af; + key->proto = pd->proto; + if (icmp_dir == PF_IN) { + *iidx = pd->sidx; + key->port[pd->sidx] = icmpid; + key->port[pd->didx] = type; + } else { + *iidx = pd->didx; + key->port[pd->sidx] = type; + key->port[pd->didx] = icmpid; + } + if (pd->af == AF_INET6 && multi != PF_ICMP_MULTI_NONE) { + switch (multi) { + case PF_ICMP_MULTI_SOLICITED: + key->addr[pd->sidx].addr32[0] = IPV6_ADDR_INT32_MLL; + key->addr[pd->sidx].addr32[1] = 0; + key->addr[pd->sidx].addr32[2] = IPV6_ADDR_INT32_ONE; + key->addr[pd->sidx].addr32[3] = pd->src->addr32[3]; + key->addr[pd->sidx].addr8[12] = 0xff; + break; + case PF_ICMP_MULTI_LINK: + key->addr[pd->sidx].addr32[0] = IPV6_ADDR_INT32_MLL; + key->addr[pd->sidx].addr32[1] = 0; + key->addr[pd->sidx].addr32[2] = 0; + key->addr[pd->sidx].addr32[3] = IPV6_ADDR_INT32_ONE; + break; + } + } else + PF_ACPY(&key->addr[pd->sidx], pd->src, key->af); + PF_ACPY(&key->addr[pd->didx], pd->dst, key->af); + + STATE_LOOKUP(kif, key, *state, pd); + + /* Is this ICMP message flowing in right direction? */ + if ((*state)->rule.ptr->type && + (((*state)->direction == direction) ? + PF_IN : PF_OUT) != icmp_dir) { + if (V_pf_status.debug >= PF_DEBUG_MISC) { + printf("pf: icmp type %d in wrong direction (%d): ", + icmp_dir, pd->dir); + pf_print_state(*state); + printf("\n"); + } + return (PF_DROP); + } + return (-1); +} + static int pf_test_state_icmp(struct pf_kstate **state, struct pfi_kkif *kif, struct mbuf *m, int off, void *h, struct pf_pdesc *pd, u_short *reason) { struct pf_addr *saddr = pd->src, *daddr = pd->dst; - u_int16_t icmpid = 0, *icmpsum; + u_int16_t *icmpsum, virtual_id, virtual_type; u_int8_t icmptype, icmpcode; - int state_icmp = 0; + int icmp_dir, iidx, ret, multi; struct pf_state_key_cmp key; +#ifdef INET + u_int16_t icmpid; +#endif + + MPASS(*state == NULL); bzero(&key, sizeof(key)); switch (pd->proto) { @@ -6435,49 +6640,43 @@ pf_test_state_icmp(struct pf_kstate **state, struct pfi_kkif *kif, icmpcode = pd->hdr.icmp.icmp_code; icmpid = pd->hdr.icmp.icmp_id; icmpsum = &pd->hdr.icmp.icmp_cksum; - - if (icmptype == ICMP_UNREACH || - icmptype == ICMP_SOURCEQUENCH || - icmptype == ICMP_REDIRECT || - icmptype == ICMP_TIMXCEED || - icmptype == ICMP_PARAMPROB) - state_icmp++; break; #endif /* INET */ #ifdef INET6 case IPPROTO_ICMPV6: icmptype = pd->hdr.icmp6.icmp6_type; icmpcode = pd->hdr.icmp6.icmp6_code; +#ifdef INET icmpid = pd->hdr.icmp6.icmp6_id; +#endif icmpsum = &pd->hdr.icmp6.icmp6_cksum; - - if (icmptype == ICMP6_DST_UNREACH || - icmptype == ICMP6_PACKET_TOO_BIG || - icmptype == ICMP6_TIME_EXCEEDED || - icmptype == ICMP6_PARAM_PROB) - state_icmp++; break; #endif /* INET6 */ } - if (!state_icmp) { + if (pf_icmp_mapping(pd, icmptype, &icmp_dir, &multi, + &virtual_id, &virtual_type) == 0) { /* * ICMP query/reply message not related to a TCP/UDP packet. * Search for an ICMP state. */ - key.af = pd->af; - key.proto = pd->proto; - key.port[0] = key.port[1] = icmpid; - if (pd->dir == PF_IN) { /* wire side, straight */ - PF_ACPY(&key.addr[0], pd->src, key.af); - PF_ACPY(&key.addr[1], pd->dst, key.af); - } else { /* stack side, reverse */ - PF_ACPY(&key.addr[1], pd->src, key.af); - PF_ACPY(&key.addr[0], pd->dst, key.af); + ret = pf_icmp_state_lookup(&key, pd, state, m, pd->dir, + kif, virtual_id, virtual_type, icmp_dir, &iidx, + PF_ICMP_MULTI_NONE); + if (ret >= 0) { + if (ret == PF_DROP && pd->af == AF_INET6 && + icmp_dir == PF_OUT) { + if (*state != NULL) + PF_STATE_UNLOCK((*state)); + ret = pf_icmp_state_lookup(&key, pd, state, m, + pd->dir, kif, virtual_id, virtual_type, + icmp_dir, &iidx, multi); + if (ret >= 0) + return (ret); + } else + return (ret); } - STATE_LOOKUP(kif, &key, *state, pd); - (*state)->expire = time_uptime; (*state)->timeout = PFTM_ICMP_ERROR_REPLY; @@ -6500,14 +6699,14 @@ pf_test_state_icmp(struct pf_kstate **state, struct pfi_kkif *kif, pd->ip_sum, nk->addr[pd->didx].v4.s_addr, 0); - if (nk->port[0] != + if (nk->port[iidx] != pd->hdr.icmp.icmp_id) { pd->hdr.icmp.icmp_cksum = pf_cksum_fixup( pd->hdr.icmp.icmp_cksum, icmpid, - nk->port[pd->sidx], 0); + nk->port[iidx], 0); pd->hdr.icmp.icmp_id = - nk->port[pd->sidx]; + nk->port[iidx]; } m_copyback(m, off, ICMP_MINLEN, @@ -6872,13 +7071,15 @@ pf_test_state_icmp(struct pf_kstate **state, struct pfi_kkif *kif, return (PF_DROP); } - key.af = pd2.af; - key.proto = IPPROTO_ICMP; - PF_ACPY(&key.addr[pd2.sidx], pd2.src, key.af); - PF_ACPY(&key.addr[pd2.didx], pd2.dst, key.af); - key.port[0] = key.port[1] = iih.icmp_id; + icmpid = iih.icmp_id; + pf_icmp_mapping(&pd2, iih.icmp_type, + &icmp_dir, &multi, &virtual_id, &virtual_type); - STATE_LOOKUP(kif, &key, *state, pd); + ret = pf_icmp_state_lookup(&key, &pd2, state, m, + pd->dir, kif, virtual_id, virtual_type, + icmp_dir, &iidx, PF_ICMP_MULTI_NONE); + if (ret >= 0) + return (ret); /* translate source/destination address, if necessary */ if ((*state)->key[PF_SK_WIRE] != @@ -6888,21 +7089,23 @@ pf_test_state_icmp(struct pf_kstate **state, struct pfi_kkif *kif, if (PF_ANEQ(pd2.src, &nk->addr[pd2.sidx], pd2.af) || - nk->port[pd2.sidx] != iih.icmp_id) - pf_change_icmp(pd2.src, &iih.icmp_id, + (virtual_type == ICMP_ECHO && + nk->port[iidx] != iih.icmp_id)) + pf_change_icmp(pd2.src, + (virtual_type == ICMP_ECHO) ? + &iih.icmp_id : NULL, daddr, &nk->addr[pd2.sidx], - nk->port[pd2.sidx], NULL, + (virtual_type == ICMP_ECHO) ? + nk->port[iidx] : 0, NULL, pd2.ip_sum, icmpsum, pd->ip_sum, 0, AF_INET); if (PF_ANEQ(pd2.dst, - &nk->addr[pd2.didx], pd2.af) || - nk->port[pd2.didx] != iih.icmp_id) - pf_change_icmp(pd2.dst, &iih.icmp_id, - saddr, &nk->addr[pd2.didx], - nk->port[pd2.didx], NULL, - pd2.ip_sum, icmpsum, - pd->ip_sum, 0, AF_INET); + &nk->addr[pd2.didx], pd2.af)) + pf_change_icmp(pd2.dst, NULL, NULL, + &nk->addr[pd2.didx], 0, NULL, + pd2.ip_sum, icmpsum, pd->ip_sum, 0, + AF_INET); m_copyback(m, off, ICMP_MINLEN, (caddr_t)&pd->hdr.icmp); m_copyback(m, ipoff2, sizeof(h2), (caddr_t)&h2); @@ -6924,13 +7127,25 @@ pf_test_state_icmp(struct pf_kstate **state, struct pfi_kkif *kif, return (PF_DROP); } - key.af = pd2.af; - key.proto = IPPROTO_ICMPV6; - PF_ACPY(&key.addr[pd2.sidx], pd2.src, key.af); - PF_ACPY(&key.addr[pd2.didx], pd2.dst, key.af); - key.port[0] = key.port[1] = iih.icmp6_id; - - STATE_LOOKUP(kif, &key, *state, pd); + pf_icmp_mapping(&pd2, iih.icmp6_type, + &icmp_dir, &multi, &virtual_id, &virtual_type); + ret = pf_icmp_state_lookup(&key, &pd2, state, m, + pd->dir, kif, virtual_id, virtual_type, + icmp_dir, &iidx, PF_ICMP_MULTI_NONE); + if (ret >= 0) { + if (ret == PF_DROP && pd->af == AF_INET6 && + icmp_dir == PF_OUT) { + if (*state != NULL) + PF_STATE_UNLOCK((*state)); + ret = pf_icmp_state_lookup(&key, pd, + state, m, pd->dir, kif, + virtual_id, virtual_type, + icmp_dir, &iidx, multi); + if (ret >= 0) + return (ret); + } else + return (ret); + } /* translate source/destination address, if necessary */ if ((*state)->key[PF_SK_WIRE] != @@ -6940,19 +7155,21 @@ pf_test_state_icmp(struct pf_kstate **state, struct pfi_kkif *kif, if (PF_ANEQ(pd2.src, &nk->addr[pd2.sidx], pd2.af) || - nk->port[pd2.sidx] != iih.icmp6_id) - pf_change_icmp(pd2.src, &iih.icmp6_id, + ((virtual_type == ICMP6_ECHO_REQUEST) && + nk->port[pd2.sidx] != iih.icmp6_id)) + pf_change_icmp(pd2.src, + (virtual_type == ICMP6_ECHO_REQUEST) + ? &iih.icmp6_id : NULL, daddr, &nk->addr[pd2.sidx], - nk->port[pd2.sidx], NULL, + (virtual_type == ICMP6_ECHO_REQUEST) + ? nk->port[iidx] : 0, NULL, pd2.ip_sum, icmpsum, pd->ip_sum, 0, AF_INET6); if (PF_ANEQ(pd2.dst, - &nk->addr[pd2.didx], pd2.af) || - nk->port[pd2.didx] != iih.icmp6_id) - pf_change_icmp(pd2.dst, &iih.icmp6_id, - saddr, &nk->addr[pd2.didx], - nk->port[pd2.didx], NULL, + &nk->addr[pd2.didx], pd2.af)) + pf_change_icmp(pd2.dst, NULL, NULL, + &nk->addr[pd2.didx], 0, NULL, pd2.ip_sum, icmpsum, pd->ip_sum, 0, AF_INET6); diff --git a/sys/netpfil/pf/pf_lb.c b/sys/netpfil/pf/pf_lb.c index eb3d087e3df6..4fcad7e578a8 100644 --- a/sys/netpfil/pf/pf_lb.c +++ b/sys/netpfil/pf/pf_lb.c @@ -225,6 +225,23 @@ pf_get_sport(sa_family_t af, u_int8_t proto, struct pf_krule *r, if (pf_map_addr(af, r, saddr, naddr, NULL, &init_addr, sn)) return (1); + if (proto == IPPROTO_ICMP) { + if (*nport == htons(ICMP_ECHO)) { + low = 1; + high = 65535; + } else + return (0); /* Don't try to modify non-echo ICMP */ + } +#ifdef INET6 + if (proto == IPPROTO_ICMPV6) { + if (*nport == htons(ICMP6_ECHO_REQUEST)) { + low = 1; + high = 65535; + } else + return (0); /* Don't try to modify non-echo ICMP */ + } +#endif /* INET6 */ + bzero(&key, sizeof(key)); key.af = af; key.proto = proto; @@ -633,7 +650,7 @@ pf_get_translation(struct pf_pdesc *pd, struct mbuf *m, int off, switch (r->action) { case PF_NAT: if (pd->proto == IPPROTO_ICMP) { - low = 1; + low = 1; high = 65535; } else { low = r->rpool.proxy_port[0];