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
[-- Attachment #1 --]
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
[-- Attachment #2 --]
*** 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);
}
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20001111183857.A9781>
