Skip site navigation (1)Skip section navigation (2)
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>