From nobody Wed Aug 07 13:44:22 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 4WfBHB408mz5S8tf; Wed, 07 Aug 2024 13:44:22 +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 4WfBHB3hYxz4hWs; Wed, 7 Aug 2024 13:44:22 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1723038262; 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=6Z9MTMZri7Q+DnNx3pYDjWvPXMwX8CF1ox+hwomupZE=; b=tQ6To2afXueebXjecvGJtisV5RPUcwEF6xuZwThd5bhZIeoS0E/nphvFpgsDcg0QNerFbd cOYg5pAnuj33Nc7jqP0AvWr+rDlITg5VL3Et14/lYJ9jwQo9gtv7kdcDdC/Dn7Ltt8vUKO pfFEjNSKdmdqMZG9oFfLtbIfjaAzo+yQQQG0+EE5XGZ6SU76pmUSQLQUYzEIDWX7CbvIPC NvEKDB4zad+eMo/tjCGYRVierT/NUid6ItyX5wVg1WXGqIRBYSOquHiMOXNXOlKVTe0tCj XyBly16SHxFYY9XLVToZxyLfWhxOmerpa8VGtmQ6lSgkEsBVBbJL6Q/IKrOoBQ== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1723038262; a=rsa-sha256; cv=none; b=lEOnSbwgIIItA9MI8ruyge66CJ65DtJOu4LAbfqSgbzu7wcxhk0mrOXmgCHq/nm7G9MTRE 1cZS9k5gqZb64YtsYsH+q7nMNXNtFN5a1SPCVYbBb0x0GUEUF9QI4T8zG3ASeA8X3Su5Ec NEEJCAewgOFGLPR0UbNnSIOyGyQ7LLyHSan9OpQi5F8I880BQe0m6tjqx8PShFDT0aYvDW BPUB+wx98hFLpt0uUOkwnl2Rf2X5IxrjOIZmiKjkBerV7OxF3ztgoEez0vHeSeO40Hgi7X IYD2d+4+K846gGWv+30zaC1ziP8/vWElP+nBW+4wXn0VNq3Uh0sQ8Y9dTrjVRw== 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=1723038262; 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=6Z9MTMZri7Q+DnNx3pYDjWvPXMwX8CF1ox+hwomupZE=; b=HKJTwFILVm2TrF0ZbPLU9PG4Zh08Fi7E6TOPkBlK35dF0Z4YSzgKhMuIockR6u+JHWQ/xj j4s1aQtBXPbTcsR1FSvKqvZKcUOs7WokcggBN+d1b23ZAZXI1yZqWyqza4dyo1N4PBXWoR 3RwMyTF5W5i/NLvJ7p3OhonGIEumqRoOPqAG4RLuH2NVkwgcE8PRn+wVMcUdwcIbn2yGWz yexCSbyl/fgS5DL3IZH6bTqzYbyQa24Di3iAKrTrw71vuEFaxgX3ZQ8zDQayhbrUSxdwWt LGG0SkDJVfEWF35VLYCChr7AzzD5BZBMEY/UZ83NWPVr9D9f1+pXkM1b91omKg== 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 4WfBHB3HnjzsmL; Wed, 7 Aug 2024 13:44:22 +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 477DiMtw033413; Wed, 7 Aug 2024 13:44:22 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.18.1/8.18.1/Submit) id 477DiMad033410; Wed, 7 Aug 2024 13:44:22 GMT (envelope-from git) Date: Wed, 7 Aug 2024 13:44:22 GMT Message-Id: <202408071344.477DiMad033410@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: 370e196bae46 - releng/14.1 - 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/14.1 X-Git-Reftype: branch X-Git-Commit: 370e196bae469f776bb95b9733fe6a6481e189f6 Auto-Submitted: auto-generated The branch releng/14.1 has been updated by markj: URL: https://cgit.FreeBSD.org/src/commit/?id=370e196bae469f776bb95b9733fe6a6481e189f6 commit 370e196bae469f776bb95b9733fe6a6481e189f6 Author: Kristof Provost AuthorDate: 2024-07-09 13:59:33 +0000 Commit: Mark Johnston CommitDate: 2024-08-07 13:31:39 +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) (cherry picked from commit 2f6b4611b5b847aee1ff8d5017a0f8a657f4101d) --- 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 5fc234c5aca9..3212a1443f63 100644 --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -291,6 +291,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 *, @@ -337,6 +339,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 *); @@ -389,6 +395,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; \ @@ -1734,6 +1742,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) { @@ -4397,8 +4541,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]; @@ -4432,33 +4576,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: @@ -4552,7 +4700,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); @@ -4561,11 +4708,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); @@ -4573,7 +4721,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); @@ -6402,15 +6549,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) { @@ -6420,49 +6625,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; @@ -6485,14 +6684,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, @@ -6857,13 +7056,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] != @@ -6873,21 +7074,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); @@ -6909,13 +7112,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] != @@ -6925,19 +7140,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];