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

*** 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);
  }
  

Reply via email to