From: Alexander Bluhm Subject: move udp control parsing to ip_setpktopts To: tech@openbsd.org Date: Tue, 9 Jan 2024 23:09:33 +0100 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))