Index | Thread | Search

From:
Alexander Bluhm <alexander.bluhm@gmx.net>
Subject:
move udp control parsing to ip_setpktopts
To:
tech@openbsd.org
Date:
Tue, 9 Jan 2024 23:09:33 +0100

Download raw body.

Thread
  • Alexander Bluhm:

    move udp control parsing to ip_setpktopts

Hi,

I would like to move the parsing code for control messages from
udp_output() to new function ip_setpktopts().  IPv6 has a similar
ip6_setpktopts().  NetBSD also does that.  This has the advantage
that raw IP can also use the features.  NetBSD also supports modern
interface IP_PKTINFO which I may implement later.

Note that parsing of the cmsg is stricter now.  Invalid lengths
generate EINVAL, unknown type ENOPROTOOPT.  As NetBSD has the same
checks, I don't expect fallout.

ok?

bluhm

Index: netinet/ip_output.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/ip_output.c,v
diff -u -p -r1.392 ip_output.c
--- netinet/ip_output.c	1 Dec 2023 15:30:47 -0000	1.392
+++ netinet/ip_output.c	9 Jan 2024 20:04:31 -0000
@@ -85,7 +85,7 @@ static u_int16_t in_cksum_phdr(u_int32_t
 void in_delayed_cksum(struct mbuf *);
 
 int ip_output_ipsec_lookup(struct mbuf *m, int hlen, const u_char seclevel[],
-    struct tdb **, int ipsecflowinfo);
+    struct tdb **, u_int32_t ipsecflowinfo);
 void ip_output_ipsec_pmtu_update(struct tdb *, struct route *, struct in_addr,
     int, int);
 int ip_output_ipsec_send(struct tdb *, struct mbuf *, struct route *, int);
@@ -510,7 +510,7 @@ bad:
 #ifdef IPSEC
 int
 ip_output_ipsec_lookup(struct mbuf *m, int hlen, const u_char seclevel[],
-    struct tdb **tdbout, int ipsecflowinfo)
+    struct tdb **tdbout, u_int32_t ipsecflowinfo)
 {
 	struct m_tag *mtag;
 	struct tdb_ident *tdbi;
@@ -1755,6 +1755,72 @@ ip_freemoptions(struct ip_moptions *imo)
 		    imo->imo_max_memberships * sizeof(struct in_multi *));
 		free(imo, M_IPMOPTS, sizeof(*imo));
 	}
+}
+
+int
+ip_setpktopts(struct mbuf *control, struct sockaddr_in *src,
+    u_int32_t *ipsecflowinfo, const struct inpcb *inp)
+{
+	caddr_t cmsgs;
+	u_int clen;
+	int error;
+
+	if (control == NULL)
+		return (EINVAL);
+
+	/*
+	 * XXX: Currently, we assume all the optional information is stored
+	 * in a single mbuf.
+	 */
+	if (control->m_next)
+		return (EINVAL);
+
+	clen = control->m_len;
+	cmsgs = mtod(control, caddr_t);
+	do {
+		struct cmsghdr *cm;
+
+		if (clen < CMSG_LEN(0))
+			return (EINVAL);
+		cm = (struct cmsghdr *)cmsgs;
+		if (cm->cmsg_len < CMSG_LEN(0) || cm->cmsg_len > clen ||
+		    CMSG_ALIGN(cm->cmsg_len) > clen)
+			return (EINVAL);
+		if (cm->cmsg_level == IPPROTO_IP) {
+			switch (cm->cmsg_type) {
+#ifdef IPSEC
+			case IP_IPSECFLOWINFO:
+				if (cm->cmsg_len !=
+				    CMSG_LEN(sizeof(u_int32_t)))
+					return (EINVAL);
+				if (!ISSET(inp->inp_flags, INP_IPSECFLOWINFO))
+					break;
+				*ipsecflowinfo = *(u_int32_t *)CMSG_DATA(cm);
+				break;
+#endif
+			case IP_SENDSRCADDR:
+				if (cm->cmsg_len !=
+				    CMSG_LEN(sizeof(struct in_addr)))
+					return (EINVAL);
+				src->sin_family = AF_INET;
+				src->sin_len = sizeof(struct sockaddr_in);
+				memcpy(&src->sin_addr, CMSG_DATA(cm),
+				    sizeof(struct in_addr));
+				/* no check on reuse when sin->sin_port == 0 */
+				error = in_pcbaddrisavail(inp, src, 0,
+				    curproc);
+				if (error)
+					return (error);
+				break;
+			default:
+				return (ENOPROTOOPT);
+			}
+		}
+		clen -= CMSG_ALIGN(cm->cmsg_len);
+		cmsgs += CMSG_ALIGN(cm->cmsg_len);
+	} while (clen);
+
+	return (0);
 }
 
 /*
Index: netinet/ip_var.h
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/ip_var.h,v
diff -u -p -r1.110 ip_var.h
--- netinet/ip_var.h	26 Nov 2023 22:08:10 -0000	1.110
+++ netinet/ip_var.h	9 Jan 2024 20:04:31 -0000
@@ -231,6 +231,8 @@ int	 ip_fragment(struct mbuf *, struct m
 void	 ip_freemoptions(struct ip_moptions *);
 int	 ip_getmoptions(int, struct ip_moptions *, struct mbuf *);
 void	 ip_init(void);
+int	 ip_setpktopts(struct mbuf *, struct sockaddr_in *, u_int32_t *,
+	    const struct inpcb *);
 struct mbuf*
 	 ip_insertoptions(struct mbuf *, struct mbuf *, int *);
 int	 ip_mforward(struct mbuf *, struct ifnet *);
Index: netinet/udp_usrreq.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/udp_usrreq.c,v
diff -u -p -r1.312 udp_usrreq.c
--- netinet/udp_usrreq.c	1 Dec 2023 15:30:47 -0000	1.312
+++ netinet/udp_usrreq.c	9 Jan 2024 20:04:31 -0000
@@ -932,57 +932,10 @@ udp_output(struct inpcb *inp, struct mbu
 	}
 
 	memset(&src_sin, 0, sizeof(src_sin));
-
 	if (control) {
-		u_int clen;
-		struct cmsghdr *cm;
-		caddr_t cmsgs;
-
-		/*
-		 * XXX: Currently, we assume all the optional information is
-		 * stored in a single mbuf.
-		 */
-		if (control->m_next) {
-			error = EINVAL;
+		error = ip_setpktopts(control, &src_sin, &ipsecflowinfo, inp);
+		if (error)
 			goto release;
-		}
-
-		clen = control->m_len;
-		cmsgs = mtod(control, caddr_t);
-		do {
-			if (clen < CMSG_LEN(0)) {
-				error = EINVAL;
-				goto release;
-			}
-			cm = (struct cmsghdr *)cmsgs;
-			if (cm->cmsg_len < CMSG_LEN(0) ||
-			    CMSG_ALIGN(cm->cmsg_len) > clen) {
-				error = EINVAL;
-				goto release;
-			}
-#ifdef IPSEC
-			if ((inp->inp_flags & INP_IPSECFLOWINFO) != 0 &&
-			    cm->cmsg_len == CMSG_LEN(sizeof(ipsecflowinfo)) &&
-			    cm->cmsg_level == IPPROTO_IP &&
-			    cm->cmsg_type == IP_IPSECFLOWINFO) {
-				ipsecflowinfo = *(u_int32_t *)CMSG_DATA(cm);
-			} else
-#endif
-			if (cm->cmsg_len == CMSG_LEN(sizeof(struct in_addr)) &&
-			    cm->cmsg_level == IPPROTO_IP &&
-			    cm->cmsg_type == IP_SENDSRCADDR) {
-				memcpy(&src_sin.sin_addr, CMSG_DATA(cm),
-				    sizeof(struct in_addr));
-				src_sin.sin_family = AF_INET;
-				src_sin.sin_len = sizeof(src_sin);
-				/* no check on reuse when sin->sin_port == 0 */
-				if ((error = in_pcbaddrisavail(inp, &src_sin,
-				    0, curproc)))
-					goto release;
-			}
-			clen -= CMSG_ALIGN(cm->cmsg_len);
-			cmsgs += CMSG_ALIGN(cm->cmsg_len);
-		} while (clen);
 	}
 
 	if (addr) {
Index: netinet6/ip6_output.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/ip6_output.c,v
diff -u -p -r1.282 ip6_output.c
--- netinet6/ip6_output.c	1 Dec 2023 15:30:47 -0000	1.282
+++ netinet6/ip6_output.c	9 Jan 2024 20:04:31 -0000
@@ -2218,9 +2218,8 @@ int
 ip6_setpktopts(struct mbuf *control, struct ip6_pktopts *opt,
     struct ip6_pktopts *stickyopt, int priv, int uproto)
 {
-	u_int clen;
-	struct cmsghdr *cm = 0;
 	caddr_t cmsgs;
+	u_int clen;
 	int error;
 
 	if (control == NULL || opt == NULL)
@@ -2228,8 +2227,6 @@ ip6_setpktopts(struct mbuf *control, str
 
 	ip6_initpktopts(opt);
 	if (stickyopt) {
-		int error;
-
 		/*
 		 * If stickyopt is provided, make a local copy of the options
 		 * for this particular packet, then override them by ancillary
@@ -2253,6 +2250,8 @@ ip6_setpktopts(struct mbuf *control, str
 	clen = control->m_len;
 	cmsgs = mtod(control, caddr_t);
 	do {
+		struct cmsghdr *cm;
+
 		if (clen < CMSG_LEN(0))
 			return (EINVAL);
 		cm = (struct cmsghdr *)cmsgs;
@@ -2281,8 +2280,6 @@ int
 ip6_setpktopt(int optname, u_char *buf, int len, struct ip6_pktopts *opt,
     int priv, int sticky, int uproto)
 {
-	int minmtupolicy;
-
 	switch (optname) {
 	case IPV6_PKTINFO:
 	{
@@ -2493,6 +2490,9 @@ ip6_setpktopt(int optname, u_char *buf, 
 	}
 
 	case IPV6_USE_MIN_MTU:
+	{
+		int minmtupolicy;
+
 		if (len != sizeof(int))
 			return (EINVAL);
 		minmtupolicy = *(int *)buf;
@@ -2503,6 +2503,7 @@ ip6_setpktopt(int optname, u_char *buf, 
 		}
 		opt->ip6po_minmtu = minmtupolicy;
 		break;
+	}
 
 	case IPV6_DONTFRAG:
 		if (len != sizeof(int))