Date: Sat, 11 Nov 2000 18:38:57 +0100 From: Thomas Moestl <tmoestl@gmx.net> To: freebsd-hackers@freebsd.org Subject: [PATCH] per-packet UDP source address selection for sendmsg Message-ID: <20001111183857.A9781@forge.local>
next in thread | raw e-mail | index | archive | help
--gBBFr7Ir9EOA20Yy Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Hi, recently, I have written a kernel patch that makes sendmsg(2) honor IP_RECVDSTADDR ancillary data on UDP sockets, so that the source address can be set on a per-packet basis. This is very useful for servers that need to use UDP and want to answer with the same source address a query went to. This is not always the case with vanilla sendto(2) in certain routing layouts. As this is my first kernel patch, could someone please give it a quick look before I submit it, so that I can be sure that it contains no stupid glitches? Unfortunately, I had no chance to test this on -current (I'm just building a -current box). Thanks, Thomas --gBBFr7Ir9EOA20Yy Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="recvdstaddr-current.diff" *** netinet/udp_usrreq.c.old Thu Nov 9 19:05:13 2000 --- netinet/udp_usrreq.c Thu Nov 9 19:23:54 2000 *************** *** 642,680 **** { register struct udpiphdr *ui; register int len = m->m_pkthdr.len; struct in_addr laddr; struct sockaddr_in *sin; ! int s = 0, error = 0; ! ! if (control) ! m_freem(control); /* XXX */ ! if (len + sizeof(struct udpiphdr) > IP_MAXPACKET) { error = EMSGSIZE; goto release; } if (addr) { sin = (struct sockaddr_in *)addr; prison_remote_ip(p, 0, &sin->sin_addr.s_addr); ! laddr = inp->inp_laddr; if (inp->inp_faddr.s_addr != INADDR_ANY) { error = EISCONN; ! goto release; } /* * Must block input while temporarily connected. */ ! s = splnet(); error = in_pcbconnect(inp, addr, p); ! if (error) { ! splx(s); ! goto release; ! } } else { if (inp->inp_faddr.s_addr == INADDR_ANY) { error = ENOTCONN; ! goto release; } } /* --- 642,741 ---- { register struct udpiphdr *ui; register int len = m->m_pkthdr.len; + register struct cmsghdr *cm = 0; struct in_addr laddr; struct sockaddr_in *sin; ! struct sockaddr_in src; ! int s = 0, error = 0, bound = 0, addrset = 0, fmbuf = 0; ! if (len + sizeof(struct udpiphdr) > IP_MAXPACKET) { error = EMSGSIZE; + if (control) + m_freem(control); goto release; } + if (control) { + /* + * XXX: Currently, we assume all the optional information is stored + * in a single mbuf. + */ + if (control->m_next) + error = EINVAL; + else { + for (; control->m_len; control->m_data += ALIGN(cm->cmsg_len), + control->m_len -= ALIGN(cm->cmsg_len)) { + cm = mtod(control, struct cmsghdr *); + if (cm->cmsg_len == 0 || cm->cmsg_len > control->m_len) { + error = EINVAL; + break; + } + if (cm->cmsg_level != IPPROTO_IP) + continue; + + switch(cm->cmsg_type) { + case IP_RECVDSTADDR: + if (cm->cmsg_len != CMSG_LEN(sizeof(struct in_addr))) { + error = EINVAL; + break; + } + laddr = inp->inp_laddr; + bzero(&src, sizeof(src)); + src.sin_family = AF_INET; + src.sin_port = inp->inp_lport; + src.sin_addr = *(struct in_addr *)CMSG_DATA(cm); + bound = 1; + s = splnet(); + if (inp->inp_laddr.s_addr == INADDR_ANY && inp->inp_lport == 0) { + /* This will check the address */ + error = in_pcbbind(inp, (struct sockaddr *)&src, p); + } else { + if (prison_ip(p, 0, &src.sin_addr.s_addr)) { + error = EINVAL; + break; + } + if (ifa_ifwithaddr((struct sockaddr *)&src) == 0) { + error = EADDRNOTAVAIL; + break; + } + inp->inp_laddr = src.sin_addr; + in_pcbrehash(inp); + } + break; + default: + error = ENOPROTOOPT; + } + if (error) + break; + } + } + m_freem(control); /* XXX */ + if (error) + goto unbind; + } + if (addr) { sin = (struct sockaddr_in *)addr; prison_remote_ip(p, 0, &sin->sin_addr.s_addr); ! if (!bound) ! laddr = inp->inp_laddr; if (inp->inp_faddr.s_addr != INADDR_ANY) { error = EISCONN; ! goto unbind; } /* * Must block input while temporarily connected. */ ! addrset=1; ! if (!bound) ! s = splnet(); error = in_pcbconnect(inp, addr, p); ! if (error) ! goto unbind; } else { if (inp->inp_faddr.s_addr == INADDR_ANY) { error = ENOTCONN; ! goto unbind; } } /* *************** *** 684,692 **** M_PREPEND(m, sizeof(struct udpiphdr), M_DONTWAIT); if (m == 0) { error = ENOBUFS; ! if (addr) ! splx(s); ! goto release; } /* --- 745,752 ---- M_PREPEND(m, sizeof(struct udpiphdr), M_DONTWAIT); if (m == 0) { error = ENOBUFS; ! /* XXX we did _not_ disconnect here before. Was that correct? Then back out! */ ! goto disconnect; } /* *************** *** 721,739 **** #ifdef IPSEC ipsec_setsocket(m, inp->inp_socket); #endif /*IPSEC*/ error = ip_output(m, inp->inp_options, &inp->inp_route, (inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST)), inp->inp_moptions); ! if (addr) { in_pcbdisconnect(inp); inp->inp_laddr = laddr; /* XXX rehash? */ - splx(s); } ! return (error); ! release: ! m_freem(m); return (error); } --- 781,807 ---- #ifdef IPSEC ipsec_setsocket(m, inp->inp_socket); #endif /*IPSEC*/ + fmbuf = 1; error = ip_output(m, inp->inp_options, &inp->inp_route, (inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST)), inp->inp_moptions); ! disconnect: ! if (addrset) { in_pcbdisconnect(inp); inp->inp_laddr = laddr; /* XXX rehash? */ } ! unbind: ! if (bound) { ! /* restore old state */ ! inp->inp_laddr = laddr; ! in_pcbrehash(inp); ! } ! if (addrset || bound) ! splx(s); release: ! if (!fmbuf) ! m_freem(m); return (error); } --gBBFr7Ir9EOA20Yy-- To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-hackers" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20001111183857.A9781>