git: 461e6f23db3b - main - Fix fragmented UDP packets handling since rev.360967.

From: Maxim Sobolev <sobomax_at_FreeBSD.org>
Date: Fri, 15 Oct 2021 23:50:26 UTC
The branch main has been updated by sobomax:

URL: https://cgit.FreeBSD.org/src/commit/?id=461e6f23db3b9794e6af88b381b066a2c0463d1c

commit 461e6f23db3b9794e6af88b381b066a2c0463d1c
Author:     Maxim Sobolev <sobomax@FreeBSD.org>
AuthorDate: 2021-10-07 20:41:40 +0000
Commit:     Maxim Sobolev <sobomax@FreeBSD.org>
CommitDate: 2021-10-15 23:48:12 +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
---
 sys/netinet/libalias/alias.c | 225 +++++++++++++++++++++++--------------------
 1 file changed, 120 insertions(+), 105 deletions(-)

diff --git a/sys/netinet/libalias/alias.c b/sys/netinet/libalias/alias.c
index 39e9b060623d..37f5bd5a1db1 100644
--- a/sys/netinet/libalias/alias.c
+++ b/sys/netinet/libalias/alias.c
@@ -721,21 +721,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,
@@ -828,19 +844,14 @@ 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))
-		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);
 
+	/* 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))
@@ -1339,64 +1350,65 @@ LibAliasInLocked(struct libalias *la, struct ip *pip, int maxpacketsize)
 		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:
@@ -1492,52 +1504,55 @@ LibAliasOutLocked(struct libalias *la,
 	} 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);