From nobody Wed Aug 07 13:44:53 2024 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4WfBHp1wbWz5S99w; Wed, 07 Aug 2024 13:44:54 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R11" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4WfBHn6JwKz4jq4; Wed, 7 Aug 2024 13:44:53 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1723038293; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=12jHmcDb9XXIdgXgVVh/JqG5CUU0rkOd+C7CeNb9qAQ=; b=fW3KWwETrErIlgyeqbkdh3nJ/USJks50j5L5hKIxQcE1WbLSj+vDSgHJYEeS8n7WAQQJ7h R+NAs5dtnSQt2H8HKEmd4HEudKUFAxxB7JCYmMZidOZ+jpLAqXd5FUu1QazKXRPQ5VpLuo huN9v3G0NF5cc+1lMozLt9hy5n5eQAJD6Q/tWXgZ9yirJIUTn7mN1UrZCOYbqc+s7K4fbK lHQOcYMG4RHfgMe6be2Y5M/3L5SRB80n+/DYJSOn1wPeahmzrM3HfxjgE3ppLXCpSrGOrD W8l4PNcqEQVUIqjfplrmqR8uJGUIcQ7u6+kAOgfnEmEgSd56SYkVHqpkqg1y7w== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1723038293; a=rsa-sha256; cv=none; b=ie/WCJNcbml1EHJWRGWOsyTsIDZIyMAIAsw9VTmP3OoSX3GcxHgZe6BwPZjCozi/+1WjNn ERrjF6wB9jGUe4nVRlshIee9zZY8BZtW8NEoP7loSvUcrs97RxHF1MqyuKCWy04gx1n87B mWdmYvsZf/LkfnXQkUEOY6gSL5or7iRNMcxj9/apjCjd7a+uXQLEUjsRFCZJYlO6+Wj3Ve vcAnnPKI1ak7uGMFqJKuvB7HduWhvDh7dkmtcZdYzD5xkNxdY860O8mLPmmPxy0S33GIt7 8ondJK6/VXSYXpNMWr+haBAcwObonpDrH2zg+ecP3J3zrM4GqEkSr3C1/JJejQ== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1723038293; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=12jHmcDb9XXIdgXgVVh/JqG5CUU0rkOd+C7CeNb9qAQ=; b=ifh0wdrCQUi3BTOkB/GB4wRw8GjnaVfVZa9LhhGLg4m9TCgfXo3SvishBawr+o9ifYCP+I XX4TOUVNQA3hTJn0tYqo7FSRSmKRLHixA4V89bVMYFwtXx/4D84dlMXBI0am+W8P/+wGZl /tgillRWa6MjS55+DnI+qPFFank8ebXYovf9ZqJskfKDK/+HXa/dKBRd65VhmKd/QFvUxR UyRFgKt4435LC9Xr1z5ryDkGWrg4dTeWMBX1HVrWxIwYTq4SBrsTgtJP4jNE7SzHT8nN3W oyGFpVvvfaB6HHucwWyi/JdUuvYp29TKOs/3608Veu3LJz5igRyDBItyPIc47g== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4WfBHn5wW9zsVB; Wed, 7 Aug 2024 13:44:53 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.18.1/8.18.1) with ESMTP id 477DirYT034701; Wed, 7 Aug 2024 13:44:53 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.18.1/8.18.1/Submit) id 477Dir9T034698; Wed, 7 Aug 2024 13:44:53 GMT (envelope-from git) Date: Wed, 7 Aug 2024 13:44:53 GMT Message-Id: <202408071344.477Dir9T034698@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Mark Johnston Subject: git: e576536cbae1 - releng/13.3 - pf: stricter state checking for ICMP and ICMPv6 packets List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: markj X-Git-Repository: src X-Git-Refname: refs/heads/releng/13.3 X-Git-Reftype: branch X-Git-Commit: e576536cbae157692116df6639e7c7cc643accad Auto-Submitted: auto-generated The branch releng/13.3 has been updated by markj: URL: https://cgit.FreeBSD.org/src/commit/?id=e576536cbae157692116df6639e7c7cc643accad commit e576536cbae157692116df6639e7c7cc643accad Author: Kristof Provost AuthorDate: 2024-07-09 13:59:33 +0000 Commit: Mark Johnston CommitDate: 2024-08-07 13:37:13 +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 Approved by: so Security: FreeBSD-SA-24:05.pf Security: CVE-2024-6640 MFC after: 1 day Obtained From: OpenBSD, mcbride 70bf7555ef4c Sponsored by: Rubicon Communications, LLC ("Netgate") (cherry picked from commit 534ee17e61ee094ec175703bc50e88ff6587703e) --- sys/netpfil/pf/pf.c | 380 ++++++++++++++++++++++++++++++++++++++----------- sys/netpfil/pf/pf_lb.c | 19 ++- 2 files changed, 316 insertions(+), 83 deletions(-) diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c index e03fcbbe37a9..1bdc695f698c 100644 --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -276,6 +276,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 *, @@ -316,6 +318,10 @@ static int pf_test_state_tcp(struct pf_kstate **, int, static int pf_test_state_udp(struct pf_kstate **, int, 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 **, int, struct pfi_kkif *, struct mbuf *, int, void *, struct pf_pdesc *, u_short *); @@ -369,6 +375,7 @@ extern int pf_end_threads; 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, _dir) \ do { \ @@ -1689,6 +1696,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) { @@ -3851,8 +3994,8 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm, int direction, int tag = -1, rtableid = -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]; @@ -3886,33 +4029,37 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm, int direction, 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: @@ -4001,7 +4148,6 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm, int direction, } #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); @@ -4010,11 +4156,12 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm, int direction, 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); @@ -4022,7 +4169,6 @@ pf_test_rule(struct pf_krule **rm, struct pf_kstate **sm, int direction, #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); @@ -5812,15 +5958,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, direction, *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, int direction, 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) { @@ -5830,49 +6034,43 @@ pf_test_state_icmp(struct pf_kstate **state, int direction, 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 (direction == 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, direction, *state, pd); - (*state)->expire = time_uptime; (*state)->timeout = PFTM_ICMP_ERROR_REPLY; @@ -5895,14 +6093,14 @@ pf_test_state_icmp(struct pf_kstate **state, int direction, 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, @@ -6267,13 +6465,15 @@ pf_test_state_icmp(struct pf_kstate **state, int direction, 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, direction, *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] != @@ -6283,21 +6483,23 @@ pf_test_state_icmp(struct pf_kstate **state, int direction, 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); @@ -6319,13 +6521,25 @@ pf_test_state_icmp(struct pf_kstate **state, int direction, 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, direction, *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] != @@ -6335,19 +6549,21 @@ pf_test_state_icmp(struct pf_kstate **state, int direction, 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 30013ab0d8d0..46d3ea8f508d 100644 --- a/sys/netpfil/pf/pf_lb.c +++ b/sys/netpfil/pf/pf_lb.c @@ -222,6 +222,23 @@ pf_get_sport(sa_family_t af, u_int8_t proto, struct pf_krule *r, if (pf_map_addr(af, r, saddr, naddr, &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; @@ -606,7 +623,7 @@ pf_get_translation(struct pf_pdesc *pd, struct mbuf *m, int off, int direction, switch (r->action) { case PF_NAT: if (pd->proto == IPPROTO_ICMP) { - low = 1; + low = 1; high = 65535; } else { low = r->rpool.proxy_port[0];