git: 4378aee9f82f - releng/13.0 - Fix fragmented UDP packets handling since rev.360967.
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 11 Jan 2022 18:15:02 UTC
The branch releng/13.0 has been updated by emaste: URL: https://cgit.FreeBSD.org/src/commit/?id=4378aee9f82fa568f3efb34afa17b48927c06864 commit 4378aee9f82fa568f3efb34afa17b48927c06864 Author: Maxim Sobolev <sobomax@FreeBSD.org> AuthorDate: 2022-01-10 00:19:08 +0000 Commit: Ed Maste <emaste@FreeBSD.org> CommitDate: 2022-01-10 14:51:54 +0000 Fix fragmented UDP packets handling since rev.360967. Consider IP_MF flag when checking length of the UDP packet to match the declared value. Sponsored by: Sippy Software, Inc. Differential Revision: https://reviews.freebsd.org/D32363 MFC after: 2 weeks (cherry picked from commit 461e6f23db3b9794e6af88b381b066a2c0463d1c) (cherry picked from commit 73c5a2566dbb3ae57970b203d4de6fcf6088701c) Approved by: so Sponsored by: The FreeBSD Foundation [rework for 13.0] Errata: FreeBSD-EN-22:06.libalias --- sys/netinet/libalias/alias.c | 332 +++++++++++++++++++++++-------------------- 1 file changed, 174 insertions(+), 158 deletions(-) diff --git a/sys/netinet/libalias/alias.c b/sys/netinet/libalias/alias.c index 900731fcbec6..4b8e8bbbd284 100644 --- a/sys/netinet/libalias/alias.c +++ b/sys/netinet/libalias/alias.c @@ -724,21 +724,37 @@ ProtoAliasOut(struct libalias *la, struct ip *pip, return (PKT_ALIAS_IGNORED); } +#define MF_ISSET(_pip) (ntohs((_pip)->ip_off) & IP_MF) +#define FRAG_NO_HDR(_pip) (ntohs((_pip)->ip_off) & IP_OFFMASK) + +static struct udphdr * +ValidateUdpLength(struct ip *pip) +{ + struct udphdr *ud; + size_t dlen; + +#ifdef _KERNEL + KASSERT(!FRAG_NO_HDR(pip), ("header-less fragment isn't expected here")); +#endif + dlen = ntohs(pip->ip_len) - (pip->ip_hl << 2); + if (dlen < sizeof(struct udphdr)) + return (NULL); + ud = (struct udphdr *)ip_next(pip); + if (!MF_ISSET(pip) && dlen < ntohs(ud->uh_ulen)) + return (NULL); + return (ud); +} + static int UdpAliasIn(struct libalias *la, struct ip *pip) { struct udphdr *ud; struct alias_link *lnk; - size_t dlen; LIBALIAS_LOCK_ASSERT(la); - dlen = ntohs(pip->ip_len) - (pip->ip_hl << 2); - if (dlen < sizeof(struct udphdr)) - return (PKT_ALIAS_IGNORED); - - ud = (struct udphdr *)ip_next(pip); - if (dlen < ntohs(ud->uh_ulen)) + ud = ValidateUdpLength(pip); + if (ud == NULL) return (PKT_ALIAS_IGNORED); lnk = FindUdpTcpIn(la, pip->ip_src, pip->ip_dst, @@ -753,8 +769,8 @@ UdpAliasIn(struct libalias *la, struct ip *pip) int accumulate; int error; struct alias_data ad = { - .lnk = lnk, - .oaddr = &original_address, + .lnk = lnk, + .oaddr = &original_address, .aaddr = &alias_address, .aport = &alias_port, .sport = &ud->uh_sport, @@ -769,46 +785,48 @@ UdpAliasIn(struct libalias *la, struct ip *pip) ud->uh_dport = GetOriginalPort(lnk); proxy_port = GetProxyPort(lnk); - /* Walk out chain. */ + /* Walk out chain. */ error = find_handler(IN, UDP, la, pip, &ad); /* If we cannot figure out the packet, ignore it. */ if (error < 0) return (PKT_ALIAS_IGNORED); -/* If UDP checksum is not zero, then adjust since destination port */ -/* is being unaliased and destination address is being altered. */ + /* If UDP checksum is not zero, then adjust since + * destination port is being unaliased and + * destination address is being altered. */ if (ud->uh_sum != 0) { accumulate = alias_port; accumulate -= ud->uh_dport; accumulate += twowords(&alias_address); accumulate -= twowords(&original_address); -/* If this is a proxy packet, modify checksum because of source change.*/ - if (proxy_port != 0) { - accumulate += ud->uh_sport; - accumulate -= proxy_port; - } + /* If this is a proxy packet, modify checksum + * because of source change.*/ + if (proxy_port != 0) { + accumulate += ud->uh_sport; + accumulate -= proxy_port; + } - if (proxy_address.s_addr != 0) { + if (proxy_address.s_addr != 0) { accumulate += twowords(&pip->ip_src); accumulate -= twowords(&proxy_address); - } + } ADJUST_CHECKSUM(accumulate, ud->uh_sum); } -/* XXX: Could the two if's below be concatenated to one ? */ -/* Restore source port and/or address in case of proxying*/ - if (proxy_port != 0) - ud->uh_sport = proxy_port; + /* XXX: Could the two if's below be concatenated to one ? */ + /* Restore source port and/or address in case of proxying*/ + if (proxy_port != 0) + ud->uh_sport = proxy_port; - if (proxy_address.s_addr != 0) { - DifferentialChecksum(&pip->ip_sum, - &proxy_address, &pip->ip_src, 2); - pip->ip_src = proxy_address; - } + if (proxy_address.s_addr != 0) { + DifferentialChecksum(&pip->ip_sum, + &proxy_address, &pip->ip_src, 2); + pip->ip_src = proxy_address; + } -/* Restore original IP address */ + /* Restore original IP address */ DifferentialChecksum(&pip->ip_sum, &original_address, &pip->ip_dst, 2); pip->ip_dst = original_address; @@ -829,47 +847,41 @@ UdpAliasOut(struct libalias *la, struct ip *pip, int maxpacketsize, int create) u_short proxy_server_port; int proxy_type; int error; - size_t dlen; LIBALIAS_LOCK_ASSERT(la); -/* Return if proxy-only mode is enabled and not proxyrule found.*/ - dlen = ntohs(pip->ip_len) - (pip->ip_hl << 2); - if (dlen < sizeof(struct udphdr)) + ud = ValidateUdpLength(pip); + if (ud == NULL) return (PKT_ALIAS_IGNORED); - ud = (struct udphdr *)ip_next(pip); - if (dlen < ntohs(ud->uh_ulen)) - return (PKT_ALIAS_IGNORED); - - proxy_type = ProxyCheck(la, &proxy_server_address, - &proxy_server_port, pip->ip_src, pip->ip_dst, - ud->uh_dport, pip->ip_p); + /* Return if proxy-only mode is enabled and not proxyrule found.*/ + proxy_type = ProxyCheck(la, &proxy_server_address, &proxy_server_port, + pip->ip_src, pip->ip_dst, ud->uh_dport, pip->ip_p); if (proxy_type == 0 && (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY)) return (PKT_ALIAS_OK); -/* If this is a transparent proxy, save original destination, - * then alter the destination and adjust checksums */ + /* If this is a transparent proxy, save original destination, + * then alter the destination and adjust checksums */ dest_port = ud->uh_dport; dest_address = pip->ip_dst; if (proxy_type != 0) { - int accumulate; + int accumulate; accumulate = twowords(&pip->ip_dst); accumulate -= twowords(&proxy_server_address); - ADJUST_CHECKSUM(accumulate, pip->ip_sum); + ADJUST_CHECKSUM(accumulate, pip->ip_sum); if (ud->uh_sum != 0) { accumulate = twowords(&pip->ip_dst); accumulate -= twowords(&proxy_server_address); - accumulate += ud->uh_dport; - accumulate -= proxy_server_port; - ADJUST_CHECKSUM(accumulate, ud->uh_sum); + accumulate += ud->uh_dport; + accumulate -= proxy_server_port; + ADJUST_CHECKSUM(accumulate, ud->uh_sum); } - pip->ip_dst = proxy_server_address; - ud->uh_dport = proxy_server_port; + pip->ip_dst = proxy_server_address; + ud->uh_dport = proxy_server_port; } lnk = FindUdpTcpOut(la, pip->ip_src, pip->ip_dst, ud->uh_sport, ud->uh_dport, @@ -878,7 +890,7 @@ UdpAliasOut(struct libalias *la, struct ip *pip, int maxpacketsize, int create) u_short alias_port; struct in_addr alias_address; struct alias_data ad = { - .lnk = lnk, + .lnk = lnk, .oaddr = NULL, .aaddr = &alias_address, .aport = &alias_port, @@ -887,24 +899,24 @@ UdpAliasOut(struct libalias *la, struct ip *pip, int maxpacketsize, int create) .maxpktsize = 0 }; -/* Save original destination address, if this is a proxy packet. - * Also modify packet to include destination encoding. This may - * change the size of IP header. */ + /* Save original destination address, if this is a proxy packet. + * Also modify packet to include destination encoding. This may + * change the size of IP header. */ if (proxy_type != 0) { - SetProxyPort(lnk, dest_port); - SetProxyAddress(lnk, dest_address); - ProxyModify(la, lnk, pip, maxpacketsize, proxy_type); - ud = (struct udphdr *)ip_next(pip); - } + SetProxyPort(lnk, dest_port); + SetProxyAddress(lnk, dest_address); + ProxyModify(la, lnk, pip, maxpacketsize, proxy_type); + ud = (struct udphdr *)ip_next(pip); + } alias_address = GetAliasAddress(lnk); alias_port = GetAliasPort(lnk); - /* Walk out chain. */ + /* Walk out chain. */ error = find_handler(OUT, UDP, la, pip, &ad); -/* If UDP checksum is not zero, adjust since source port is */ -/* being aliased and source address is being altered */ + /* If UDP checksum is not zero, adjust since source port is */ + /* being aliased and source address is being altered */ if (ud->uh_sum != 0) { int accumulate; @@ -914,10 +926,10 @@ UdpAliasOut(struct libalias *la, struct ip *pip, int maxpacketsize, int create) accumulate -= twowords(&alias_address); ADJUST_CHECKSUM(accumulate, ud->uh_sum); } -/* Put alias port in UDP header */ + /* Put alias port in UDP header */ ud->uh_sport = alias_port; -/* Change source address */ + /* Change source address */ DifferentialChecksum(&pip->ip_sum, &alias_address, &pip->ip_src, 2); pip->ip_src = alias_address; @@ -1340,68 +1352,69 @@ LibAliasInLocked(struct libalias *la, struct ip *pip, int maxpacketsize) /* Defense against mangled packets */ if (ntohs(pip->ip_len) > maxpacketsize || (pip->ip_hl << 2) > maxpacketsize) { - iresult = PKT_ALIAS_IGNORED; + iresult = PKT_ALIAS_IGNORED; + goto getout; + } + + if (FRAG_NO_HDR(pip)) { + iresult = FragmentIn(la, pip->ip_src, pip, pip->ip_id, + &pip->ip_sum); goto getout; } iresult = PKT_ALIAS_IGNORED; - if ((ntohs(pip->ip_off) & IP_OFFMASK) == 0) { - switch (pip->ip_p) { - case IPPROTO_ICMP: - iresult = IcmpAliasIn(la, pip); - break; - case IPPROTO_UDP: - iresult = UdpAliasIn(la, pip); - break; - case IPPROTO_TCP: - iresult = TcpAliasIn(la, pip); - break; + switch (pip->ip_p) { + case IPPROTO_ICMP: + iresult = IcmpAliasIn(la, pip); + break; + case IPPROTO_UDP: + iresult = UdpAliasIn(la, pip); + break; + case IPPROTO_TCP: + iresult = TcpAliasIn(la, pip); + break; #ifdef _KERNEL - case IPPROTO_SCTP: - iresult = SctpAlias(la, pip, SN_TO_LOCAL); - break; + case IPPROTO_SCTP: + iresult = SctpAlias(la, pip, SN_TO_LOCAL); + break; #endif - case IPPROTO_GRE: { - int error; - struct alias_data ad = { - .lnk = NULL, - .oaddr = NULL, - .aaddr = NULL, - .aport = NULL, - .sport = NULL, - .dport = NULL, - .maxpktsize = 0 - }; - - /* Walk out chain. */ - error = find_handler(IN, IP, la, pip, &ad); - if (error == 0) - iresult = PKT_ALIAS_OK; - else - iresult = ProtoAliasIn(la, pip->ip_src, - pip, pip->ip_p, &pip->ip_sum); - } - break; - default: - iresult = ProtoAliasIn(la, pip->ip_src, pip, - pip->ip_p, &pip->ip_sum); - break; - } + case IPPROTO_GRE: { + int error; + struct alias_data ad = { + .lnk = NULL, + .oaddr = NULL, + .aaddr = NULL, + .aport = NULL, + .sport = NULL, + .dport = NULL, + .maxpktsize = 0 + }; - if (ntohs(pip->ip_off) & IP_MF) { - struct alias_link *lnk; + /* Walk out chain. */ + error = find_handler(IN, IP, la, pip, &ad); + if (error == 0) + iresult = PKT_ALIAS_OK; + else + iresult = ProtoAliasIn(la, pip->ip_src, + pip, pip->ip_p, &pip->ip_sum); + break; + } + default: + iresult = ProtoAliasIn(la, pip->ip_src, pip, + pip->ip_p, &pip->ip_sum); + break; + } - lnk = FindFragmentIn1(la, pip->ip_src, alias_addr, pip->ip_id); - if (lnk != NULL) { - iresult = PKT_ALIAS_FOUND_HEADER_FRAGMENT; - SetFragmentAddr(lnk, pip->ip_dst); - } else { - iresult = PKT_ALIAS_ERROR; - } + if (MF_ISSET(pip)) { + struct alias_link *lnk; + + lnk = FindFragmentIn1(la, pip->ip_src, alias_addr, pip->ip_id); + if (lnk != NULL) { + iresult = PKT_ALIAS_FOUND_HEADER_FRAGMENT; + SetFragmentAddr(lnk, pip->ip_dst); + } else { + iresult = PKT_ALIAS_ERROR; } - } else { - iresult = FragmentIn(la, pip->ip_src, pip, pip->ip_id, - &pip->ip_sum); } getout: @@ -1449,10 +1462,10 @@ LibAliasOutTry(struct libalias *la, void *ptr, int maxpacketsize, int create) } static int -LibAliasOutLocked(struct libalias *la, struct ip *pip, /* valid IP packet */ - int maxpacketsize, /* How much the packet data may grow (FTP - * and IRC inline changes) */ - int create /* Create new entries ? */ +LibAliasOutLocked(struct libalias *la, + struct ip *pip, /* valid IP packet */ + int maxpacketsize, /* How much the packet data may grow (FTP and IRC inline changes) */ + int create /* Create new entries ? */ ) { int iresult; @@ -1498,52 +1511,55 @@ LibAliasOutLocked(struct libalias *la, struct ip *pip, /* valid IP packet */ } else if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY) { SetDefaultAliasAddress(la, pip->ip_src); } + + if (FRAG_NO_HDR(pip)) { + iresult = FragmentOut(la, pip, &pip->ip_sum); + goto getout_restore; + } + iresult = PKT_ALIAS_IGNORED; - if ((ntohs(pip->ip_off) & IP_OFFMASK) == 0) { - switch (pip->ip_p) { - case IPPROTO_ICMP: - iresult = IcmpAliasOut(la, pip, create); - break; - case IPPROTO_UDP: - iresult = UdpAliasOut(la, pip, maxpacketsize, create); - break; - case IPPROTO_TCP: - iresult = TcpAliasOut(la, pip, maxpacketsize, create); - break; + switch (pip->ip_p) { + case IPPROTO_ICMP: + iresult = IcmpAliasOut(la, pip, create); + break; + case IPPROTO_UDP: + iresult = UdpAliasOut(la, pip, maxpacketsize, create); + break; + case IPPROTO_TCP: + iresult = TcpAliasOut(la, pip, maxpacketsize, create); + break; #ifdef _KERNEL - case IPPROTO_SCTP: - iresult = SctpAlias(la, pip, SN_TO_GLOBAL); - break; + case IPPROTO_SCTP: + iresult = SctpAlias(la, pip, SN_TO_GLOBAL); + break; #endif - case IPPROTO_GRE: { - int error; - struct alias_data ad = { - .lnk = NULL, - .oaddr = NULL, - .aaddr = NULL, - .aport = NULL, - .sport = NULL, - .dport = NULL, - .maxpktsize = 0 - }; - /* Walk out chain. */ - error = find_handler(OUT, IP, la, pip, &ad); - if (error == 0) - iresult = PKT_ALIAS_OK; - else - iresult = ProtoAliasOut(la, pip, - pip->ip_dst, pip->ip_p, &pip->ip_sum, create); - } - break; - default: + case IPPROTO_GRE: { + int error; + struct alias_data ad = { + .lnk = NULL, + .oaddr = NULL, + .aaddr = NULL, + .aport = NULL, + .sport = NULL, + .dport = NULL, + .maxpktsize = 0 + }; + /* Walk out chain. */ + error = find_handler(OUT, IP, la, pip, &ad); + if (error == 0) + iresult = PKT_ALIAS_OK; + else iresult = ProtoAliasOut(la, pip, pip->ip_dst, pip->ip_p, &pip->ip_sum, create); - break; + break; } - } else { - iresult = FragmentOut(la, pip, &pip->ip_sum); + default: + iresult = ProtoAliasOut(la, pip, + pip->ip_dst, pip->ip_p, &pip->ip_sum, create); + break; } +getout_restore: SetDefaultAliasAddress(la, addr_save); getout: return (iresult);