Author: kevlo
Date: Mon Apr  7 01:53:03 2014
New Revision: 264212
URL: http://svnweb.freebsd.org/changeset/base/264212

Log:
  Add support for UDP-Lite protocol (RFC 3828) to IPv4 and IPv6 stacks.
  Tested with vlc and a test suite [1].
  
  [1] http://www.erg.abdn.ac.uk/~gerrit/udp-lite/files/udplite_linux.tar.gz
  
  Reviewed by:  jhb, glebius, adrian

Added:
  head/sys/netinet/udplite.h   (contents, props changed)
Modified:
  head/lib/libc/net/getaddrinfo.c
  head/sys/netinet/in.c
  head/sys/netinet/in.h
  head/sys/netinet/in_pcb.c
  head/sys/netinet/in_proto.c
  head/sys/netinet/udp_usrreq.c
  head/sys/netinet/udp_var.h
  head/sys/netinet6/in6_ifattach.c
  head/sys/netinet6/in6_proto.c
  head/sys/netinet6/udp6_usrreq.c
  head/sys/netinet6/udp6_var.h
  head/sys/sys/param.h

Modified: head/lib/libc/net/getaddrinfo.c
==============================================================================
--- head/lib/libc/net/getaddrinfo.c     Mon Apr  7 01:52:35 2014        
(r264211)
+++ head/lib/libc/net/getaddrinfo.c     Mon Apr  7 01:53:03 2014        
(r264212)
@@ -170,12 +170,14 @@ static const struct explore explore[] = 
        { PF_INET6, SOCK_STREAM, IPPROTO_TCP, 0x07 },
        { PF_INET6, SOCK_STREAM, IPPROTO_SCTP, 0x03 },
        { PF_INET6, SOCK_SEQPACKET, IPPROTO_SCTP, 0x07 },
+       { PF_INET6, SOCK_DGRAM, IPPROTO_UDPLITE, 0x03 },
        { PF_INET6, SOCK_RAW, ANY, 0x05 },
 #endif
        { PF_INET, SOCK_DGRAM, IPPROTO_UDP, 0x07 },
        { PF_INET, SOCK_STREAM, IPPROTO_TCP, 0x07 },
        { PF_INET, SOCK_STREAM, IPPROTO_SCTP, 0x03 },
        { PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP, 0x07 },
+       { PF_INET, SOCK_DGRAM, IPPROTO_UDPLITE, 0x03 },
        { PF_INET, SOCK_RAW, ANY, 0x05 },
        { -1, 0, 0, 0 },
 };
@@ -1477,6 +1479,9 @@ get_port(struct addrinfo *ai, const char
                case IPPROTO_SCTP:
                        proto = "sctp";
                        break;
+               case IPPROTO_UDPLITE:
+                       proto = "udplite";
+                       break;
                default:
                        proto = NULL;
                        break;

Modified: head/sys/netinet/in.c
==============================================================================
--- head/sys/netinet/in.c       Mon Apr  7 01:52:35 2014        (r264211)
+++ head/sys/netinet/in.c       Mon Apr  7 01:53:03 2014        (r264212)
@@ -859,6 +859,7 @@ in_ifdetach(struct ifnet *ifp)
 
        in_pcbpurgeif0(&V_ripcbinfo, ifp);
        in_pcbpurgeif0(&V_udbinfo, ifp);
+       in_pcbpurgeif0(&V_ulitecbinfo, ifp);
        in_purgemaddrs(ifp);
 }
 

Modified: head/sys/netinet/in.h
==============================================================================
--- head/sys/netinet/in.h       Mon Apr  7 01:52:35 2014        (r264211)
+++ head/sys/netinet/in.h       Mon Apr  7 01:53:03 2014        (r264212)
@@ -237,6 +237,7 @@ __END_DECLS
 #define        IPPROTO_IPCOMP          108             /* payload compression 
(IPComp) */
 #define        IPPROTO_SCTP            132             /* SCTP */
 #define        IPPROTO_MH              135             /* IPv6 Mobility Header 
*/
+#define        IPPROTO_UDPLITE         136             /* UDP-Lite */
 #define        IPPROTO_HIP             139             /* IP6 Host Identity 
Protocol */
 #define        IPPROTO_SHIM6           140             /* IP6 Shim6 Protocol */
 /* 101-254: Partly Unassigned */

Modified: head/sys/netinet/in_pcb.c
==============================================================================
--- head/sys/netinet/in_pcb.c   Mon Apr  7 01:52:35 2014        (r264211)
+++ head/sys/netinet/in_pcb.c   Mon Apr  7 01:53:03 2014        (r264212)
@@ -389,13 +389,14 @@ in_pcb_lport(struct inpcb *inp, struct i
                lastport = &pcbinfo->ipi_lastport;
        }
        /*
-        * For UDP, use random port allocation as long as the user
+        * For UDP(-Lite), use random port allocation as long as the user
         * allows it.  For TCP (and as of yet unknown) connections,
         * use random port allocation only if the user allows it AND
         * ipport_tick() allows it.
         */
        if (V_ipport_randomized &&
-               (!V_ipport_stoprandom || pcbinfo == &V_udbinfo))
+               (!V_ipport_stoprandom || pcbinfo == &V_udbinfo ||
+               pcbinfo == &V_ulitecbinfo))
                dorandom = 1;
        else
                dorandom = 0;
@@ -405,8 +406,8 @@ in_pcb_lport(struct inpcb *inp, struct i
         */
        if (first == last)
                dorandom = 0;
-       /* Make sure to not include UDP packets in the count. */
-       if (pcbinfo != &V_udbinfo)
+       /* Make sure to not include UDP(-Lite) packets in the count. */
+       if (pcbinfo != &V_udbinfo || pcbinfo != &V_ulitecbinfo)
                V_ipport_tcpallocs++;
        /*
         * Instead of having two loops further down counting up or down

Modified: head/sys/netinet/in_proto.c
==============================================================================
--- head/sys/netinet/in_proto.c Mon Apr  7 01:52:35 2014        (r264211)
+++ head/sys/netinet/in_proto.c Mon Apr  7 01:53:03 2014        (r264212)
@@ -184,6 +184,20 @@ struct protosw inetsw[] = {
 },
 #endif /* SCTP */
 {
+       .pr_type =              SOCK_DGRAM,
+       .pr_domain =            &inetdomain,
+       .pr_protocol =          IPPROTO_UDPLITE,
+       .pr_flags =             PR_ATOMIC|PR_ADDR,
+       .pr_input =             udp_input,
+       .pr_ctlinput =          udplite_ctlinput,
+       .pr_ctloutput =         udp_ctloutput,
+       .pr_init =              udplite_init,
+#ifdef VIMAGE
+       .pr_destroy =           udplite_destroy,
+#endif
+       .pr_usrreqs =           &udp_usrreqs
+},
+{
        .pr_type =              SOCK_RAW,
        .pr_domain =            &inetdomain,
        .pr_protocol =          IPPROTO_RAW,

Modified: head/sys/netinet/udp_usrreq.c
==============================================================================
--- head/sys/netinet/udp_usrreq.c       Mon Apr  7 01:52:35 2014        
(r264211)
+++ head/sys/netinet/udp_usrreq.c       Mon Apr  7 01:53:03 2014        
(r264212)
@@ -3,6 +3,7 @@
  *     The Regents of the University of California.
  * Copyright (c) 2008 Robert N. M. Watson
  * Copyright (c) 2010-2011 Juniper Networks, Inc.
+ * Copyright (c) 2014 Kevin Lo
  * All rights reserved.
  *
  * Portions of this software were developed by Robert N. M. Watson under
@@ -87,6 +88,7 @@ __FBSDID("$FreeBSD$");
 #endif
 #include <netinet/udp.h>
 #include <netinet/udp_var.h>
+#include <netinet/udplite.h>
 
 #ifdef IPSEC
 #include <netipsec/ipsec.h>
@@ -98,8 +100,9 @@ __FBSDID("$FreeBSD$");
 #include <security/mac/mac_framework.h>
 
 /*
- * UDP protocol implementation.
+ * UDP and UDP-Lite protocols implementation.
  * Per RFC 768, August, 1980.
+ * Per RFC 3828, July, 2004.
  */
 
 /*
@@ -139,6 +142,8 @@ SYSCTL_ULONG(_net_inet_udp, UDPCTL_RECVS
 
 VNET_DEFINE(struct inpcbhead, udb);            /* from udp_var.h */
 VNET_DEFINE(struct inpcbinfo, udbinfo);
+VNET_DEFINE(struct inpcbhead, ulitecb);
+VNET_DEFINE(struct inpcbinfo, ulitecbinfo);
 static VNET_DEFINE(uma_zone_t, udpcb_zone);
 #define        V_udpcb_zone                    VNET(udpcb_zone)
 
@@ -187,6 +192,16 @@ udp_inpcb_init(void *mem, int size, int 
        return (0);
 }
 
+static int
+udplite_inpcb_init(void *mem, int size, int flags)
+{
+       struct inpcb *inp;
+
+       inp = mem;
+       INP_LOCK_INIT(inp, "inp", "udpliteinp");
+       return (0);
+}
+
 void
 udp_init(void)
 {
@@ -202,6 +217,15 @@ udp_init(void)
            EVENTHANDLER_PRI_ANY);
 }
 
+void
+udplite_init(void)
+{
+
+       in_pcbinfo_init(&V_ulitecbinfo, "udplite", &V_ulitecb, UDBHASHSIZE,
+           UDBHASHSIZE, "udplite_inpcb", udplite_inpcb_init, NULL,
+           UMA_ZONE_NOFREE, IPI_HASHFIELDS_2TUPLE);
+}
+
 /*
  * Kernel module interface for updating udpstat.  The argument is an index
  * into udpstat treated as an array of u_long.  While this encodes the
@@ -243,6 +267,13 @@ udp_destroy(void)
        in_pcbinfo_destroy(&V_udbinfo);
        uma_zdestroy(V_udpcb_zone);
 }
+
+void
+udplite_destroy(void)
+{
+
+       in_pcbinfo_destroy(&V_ulitecbinfo);
+}
 #endif
 
 #ifdef INET
@@ -346,10 +377,14 @@ udp_input(struct mbuf *m, int off)
        struct ifnet *ifp;
        struct inpcb *inp;
        uint16_t len, ip_len;
+       struct inpcbinfo *pcbinfo;
        struct ip save_ip;
        struct sockaddr_in udp_in;
        struct m_tag *fwd_tag;
+       int cscov_partial;
+       uint8_t pr;
 
+       iphlen = off;
        ifp = m->m_pkthdr.rcvif;
        UDPSTAT_INC(udps_ipackets);
 
@@ -375,6 +410,8 @@ udp_input(struct mbuf *m, int off)
                ip = mtod(m, struct ip *);
        }
        uh = (struct udphdr *)((caddr_t)ip + iphlen);
+       pr = ip->ip_p;
+       cscov_partial = (pr == IPPROTO_UDPLITE) ? 1 : 0;
 
        /*
         * Destination port of 0 is illegal, based on RFC768.
@@ -398,12 +435,18 @@ udp_input(struct mbuf *m, int off)
         */
        len = ntohs((u_short)uh->uh_ulen);
        ip_len = ntohs(ip->ip_len) - iphlen;
+       if (pr == IPPROTO_UDPLITE && len == 0) {
+               /* Zero means checksum over the complete packet. */
+               len = ip_len;
+               cscov_partial = 0;
+       }
        if (ip_len != len) {
                if (len > ip_len || len < sizeof(struct udphdr)) {
                        UDPSTAT_INC(udps_badlen);
                        goto badunlocked;
                }
-               m_adj(m, len - ip_len);
+               if (pr == IPPROTO_UDP)
+                       m_adj(m, len - ip_len);
        }
 
        /*
@@ -421,20 +464,22 @@ udp_input(struct mbuf *m, int off)
        if (uh->uh_sum) {
                u_short uh_sum;
 
-               if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID) {
+               if ((m->m_pkthdr.csum_flags & CSUM_DATA_VALID) &&
+                   !cscov_partial) {
                        if (m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR)
                                uh_sum = m->m_pkthdr.csum_data;
                        else
                                uh_sum = in_pseudo(ip->ip_src.s_addr,
                                    ip->ip_dst.s_addr, htonl((u_short)len +
-                                   m->m_pkthdr.csum_data + IPPROTO_UDP));
+                                   m->m_pkthdr.csum_data + pr));
                        uh_sum ^= 0xffff;
                } else {
                        char b[9];
 
                        bcopy(((struct ipovly *)ip)->ih_x1, b, 9);
                        bzero(((struct ipovly *)ip)->ih_x1, 9);
-                       ((struct ipovly *)ip)->ih_len = uh->uh_ulen;
+                       ((struct ipovly *)ip)->ih_len = (pr == IPPROTO_UDP) ?
+                           uh->uh_ulen : htons(ip_len);
                        uh_sum = in_cksum(m, len + sizeof (struct ip));
                        bcopy(b, ((struct ipovly *)ip)->ih_x1, 9);
                }
@@ -446,14 +491,17 @@ udp_input(struct mbuf *m, int off)
        } else
                UDPSTAT_INC(udps_nosum);
 
+       pcbinfo = get_inpcbinfo(pr);
        if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) ||
            in_broadcast(ip->ip_dst, ifp)) {
                struct inpcb *last;
+               struct inpcbhead *pcblist;
                struct ip_moptions *imo;
 
-               INP_INFO_RLOCK(&V_udbinfo);
+               INP_INFO_RLOCK(pcbinfo);
+               pcblist = get_pcblist(pr);
                last = NULL;
-               LIST_FOREACH(inp, &V_udb, inp_list) {
+               LIST_FOREACH(inp, pcblist, inp_list) {
                        if (inp->inp_lport != uh->uh_dport)
                                continue;
 #ifdef INET6
@@ -539,12 +587,12 @@ udp_input(struct mbuf *m, int off)
                        UDPSTAT_INC(udps_noportbcast);
                        if (inp)
                                INP_RUNLOCK(inp);
-                       INP_INFO_RUNLOCK(&V_udbinfo);
+                       INP_INFO_RUNLOCK(pcbinfo);
                        goto badunlocked;
                }
                udp_append(last, ip, m, iphlen, &udp_in);
                INP_RUNLOCK(last);
-               INP_INFO_RUNLOCK(&V_udbinfo);
+               INP_INFO_RUNLOCK(pcbinfo);
                return;
        }
 
@@ -565,7 +613,7 @@ udp_input(struct mbuf *m, int off)
                 * Transparently forwarded. Pretend to be the destination.
                 * Already got one like this?
                 */
-               inp = in_pcblookup_mbuf(&V_udbinfo, ip->ip_src, uh->uh_sport,
+               inp = in_pcblookup_mbuf(pcbinfo, ip->ip_src, uh->uh_sport,
                    ip->ip_dst, uh->uh_dport, INPLOOKUP_RLOCKPCB, ifp, m);
                if (!inp) {
                        /*
@@ -573,7 +621,7 @@ udp_input(struct mbuf *m, int off)
                         * Because we've rewritten the destination address,
                         * any hardware-generated hash is ignored.
                         */
-                       inp = in_pcblookup(&V_udbinfo, ip->ip_src,
+                       inp = in_pcblookup(pcbinfo, ip->ip_src,
                            uh->uh_sport, next_hop->sin_addr,
                            next_hop->sin_port ? htons(next_hop->sin_port) :
                            uh->uh_dport, INPLOOKUP_WILDCARD |
@@ -583,7 +631,7 @@ udp_input(struct mbuf *m, int off)
                m_tag_delete(m, fwd_tag);
                m->m_flags &= ~M_IP_NEXTHOP;
        } else
-               inp = in_pcblookup_mbuf(&V_udbinfo, ip->ip_src, uh->uh_sport,
+               inp = in_pcblookup_mbuf(pcbinfo, ip->ip_src, uh->uh_sport,
                    ip->ip_dst, uh->uh_dport, INPLOOKUP_WILDCARD |
                    INPLOOKUP_RLOCKPCB, ifp, m);
        if (inp == NULL) {
@@ -619,6 +667,16 @@ udp_input(struct mbuf *m, int off)
                m_freem(m);
                return;
        }
+       if (cscov_partial) {
+               struct udpcb *up;
+
+               up = intoudpcb(inp);
+               if (up->u_rxcslen > len) {
+                       INP_RUNLOCK(inp);
+                       m_freem(m);
+                       return;
+               }
+       }
 
        UDP_PROBE(receive, NULL, inp, ip, inp, uh);
        udp_append(inp, ip, m, iphlen, &udp_in);
@@ -653,8 +711,9 @@ udp_notify(struct inpcb *inp, int errno)
 }
 
 #ifdef INET
-void
-udp_ctlinput(int cmd, struct sockaddr *sa, void *vip)
+static void
+udp_common_ctlinput(int cmd, struct sockaddr *sa, void *vip,
+    struct inpcbinfo *pcbinfo)
 {
        struct ip *ip = vip;
        struct udphdr *uh;
@@ -683,7 +742,7 @@ udp_ctlinput(int cmd, struct sockaddr *s
                return;
        if (ip != NULL) {
                uh = (struct udphdr *)((caddr_t)ip + (ip->ip_hl << 2));
-               inp = in_pcblookup(&V_udbinfo, faddr, uh->uh_dport,
+               inp = in_pcblookup(pcbinfo, faddr, uh->uh_dport,
                    ip->ip_src, uh->uh_sport, INPLOOKUP_RLOCKPCB, NULL);
                if (inp != NULL) {
                        INP_RLOCK_ASSERT(inp);
@@ -693,9 +752,22 @@ udp_ctlinput(int cmd, struct sockaddr *s
                        INP_RUNLOCK(inp);
                }
        } else
-               in_pcbnotifyall(&V_udbinfo, faddr, inetctlerrmap[cmd],
+               in_pcbnotifyall(pcbinfo, faddr, inetctlerrmap[cmd],
                    udp_notify);
 }
+void
+udp_ctlinput(int cmd, struct sockaddr *sa, void *vip)
+{
+
+       return (udp_common_ctlinput(cmd, sa, vip, &V_udbinfo));
+}
+
+void
+udplite_ctlinput(int cmd, struct sockaddr *sa, void *vip)
+{
+
+       return (udp_common_ctlinput(cmd, sa, vip, &V_ulitecbinfo));
+}
 #endif /* INET */
 
 static int
@@ -851,16 +923,16 @@ SYSCTL_PROC(_net_inet_udp, OID_AUTO, get
 int
 udp_ctloutput(struct socket *so, struct sockopt *sopt)
 {
-       int error = 0, optval;
        struct inpcb *inp;
-#ifdef IPSEC_NAT_T
        struct udpcb *up;
-#endif
+       int isudplite, error, optval;
 
+       error = 0;
+       isudplite = (so->so_proto->pr_protocol == IPPROTO_UDPLITE) ? 1 : 0;
        inp = sotoinpcb(so);
        KASSERT(inp != NULL, ("%s: inp == NULL", __func__));
        INP_WLOCK(inp);
-       if (sopt->sopt_level != IPPROTO_UDP) {
+       if (sopt->sopt_level != so->so_proto->pr_protocol) {
 #ifdef INET6
                if (INP_CHECK_SOCKAF(so, AF_INET6)) {
                        INP_WUNLOCK(inp);
@@ -918,6 +990,34 @@ udp_ctloutput(struct socket *so, struct 
                        }
                        INP_WUNLOCK(inp);
                        break;
+               case UDPLITE_SEND_CSCOV:
+               case UDPLITE_RECV_CSCOV:
+                       if (!isudplite) {
+                               INP_WUNLOCK(inp);
+                               error = ENOPROTOOPT;
+                               break;
+                       }
+                       INP_WUNLOCK(inp);
+                       error = sooptcopyin(sopt, &optval, sizeof(optval),
+                           sizeof(optval));
+                       if (error != 0)
+                               break;
+                       inp = sotoinpcb(so);
+                       KASSERT(inp != NULL, ("%s: inp == NULL", __func__));
+                       INP_WLOCK(inp);
+                       up = intoudpcb(inp);
+                       KASSERT(up != NULL, ("%s: up == NULL", __func__));
+                       if (optval != 0 && optval < 8) {
+                               INP_WUNLOCK(inp);
+                               error = EINVAL;
+                               break;
+                       }
+                       if (sopt->sopt_name == UDPLITE_SEND_CSCOV)
+                               up->u_txcslen = optval;
+                       else
+                               up->u_rxcslen = optval;
+                       INP_WUNLOCK(inp);
+                       break;
                default:
                        INP_WUNLOCK(inp);
                        error = ENOPROTOOPT;
@@ -935,6 +1035,22 @@ udp_ctloutput(struct socket *so, struct 
                        error = sooptcopyout(sopt, &optval, sizeof optval);
                        break;
 #endif
+               case UDPLITE_SEND_CSCOV:
+               case UDPLITE_RECV_CSCOV:
+                       if (!isudplite) {
+                               INP_WUNLOCK(inp);
+                               error = ENOPROTOOPT;
+                               break;
+                       }
+                       up = intoudpcb(inp);
+                       KASSERT(up != NULL, ("%s: up == NULL", __func__));
+                       if (sopt->sopt_name == UDPLITE_SEND_CSCOV)
+                               optval = up->u_txcslen;
+                       else
+                               optval = up->u_rxcslen;
+                       INP_WUNLOCK(inp);
+                       error = sooptcopyout(sopt, &optval, sizeof(optval));
+                       break;
                default:
                        INP_WUNLOCK(inp);
                        error = ENOPROTOOPT;
@@ -957,12 +1073,16 @@ udp_output(struct inpcb *inp, struct mbu
        int len = m->m_pkthdr.len;
        struct in_addr faddr, laddr;
        struct cmsghdr *cm;
+       struct inpcbinfo *pcbinfo;
        struct sockaddr_in *sin, src;
+       int cscov_partial = 0;
        int error = 0;
        int ipflags;
        u_short fport, lport;
        int unlock_udbinfo;
        u_char tos;
+       uint8_t pr;
+       uint16_t cscov = 0;
 
        /*
         * udp_output() may need to temporarily bind or connect the current
@@ -1057,12 +1177,14 @@ udp_output(struct inpcb *inp, struct mbu
         *
         * XXXRW: Check that hash locking update here is correct.
         */
+       pr = inp->inp_socket->so_proto->pr_protocol;
+       pcbinfo = get_inpcbinfo(pr);
        sin = (struct sockaddr_in *)addr;
        if (sin != NULL &&
            (inp->inp_laddr.s_addr == INADDR_ANY && inp->inp_lport == 0)) {
                INP_RUNLOCK(inp);
                INP_WLOCK(inp);
-               INP_HASH_WLOCK(&V_udbinfo);
+               INP_HASH_WLOCK(pcbinfo);
                unlock_udbinfo = UH_WLOCKED;
        } else if ((sin != NULL && (
            (sin->sin_addr.s_addr == INADDR_ANY) ||
@@ -1070,7 +1192,7 @@ udp_output(struct inpcb *inp, struct mbu
            (inp->inp_laddr.s_addr == INADDR_ANY) ||
            (inp->inp_lport == 0))) ||
            (src.sin_family == AF_INET)) {
-               INP_HASH_RLOCK(&V_udbinfo);
+               INP_HASH_RLOCK(pcbinfo);
                unlock_udbinfo = UH_RLOCKED;
        } else
                unlock_udbinfo = UH_UNLOCKED;
@@ -1083,7 +1205,7 @@ udp_output(struct inpcb *inp, struct mbu
        laddr = inp->inp_laddr;
        lport = inp->inp_lport;
        if (src.sin_family == AF_INET) {
-               INP_HASH_LOCK_ASSERT(&V_udbinfo);
+               INP_HASH_LOCK_ASSERT(pcbinfo);
                if ((lport == 0) ||
                    (laddr.s_addr == INADDR_ANY &&
                     src.sin_addr.s_addr == INADDR_ANY)) {
@@ -1134,7 +1256,7 @@ udp_output(struct inpcb *inp, struct mbu
                    inp->inp_lport == 0 ||
                    sin->sin_addr.s_addr == INADDR_ANY ||
                    sin->sin_addr.s_addr == INADDR_BROADCAST) {
-                       INP_HASH_LOCK_ASSERT(&V_udbinfo);
+                       INP_HASH_LOCK_ASSERT(pcbinfo);
                        error = in_pcbconnect_setup(inp, addr, &laddr.s_addr,
                            &lport, &faddr.s_addr, &fport, NULL,
                            td->td_ucred);
@@ -1149,7 +1271,7 @@ udp_output(struct inpcb *inp, struct mbu
                        if (inp->inp_laddr.s_addr == INADDR_ANY &&
                            inp->inp_lport == 0) {
                                INP_WLOCK_ASSERT(inp);
-                               INP_HASH_WLOCK_ASSERT(&V_udbinfo);
+                               INP_HASH_WLOCK_ASSERT(pcbinfo);
                                /*
                                 * Remember addr if jailed, to prevent
                                 * rebinding.
@@ -1198,13 +1320,30 @@ udp_output(struct inpcb *inp, struct mbu
         */
        ui = mtod(m, struct udpiphdr *);
        bzero(ui->ui_x1, sizeof(ui->ui_x1));    /* XXX still needed? */
-       ui->ui_v = IPVERSION << 4;
-       ui->ui_pr = IPPROTO_UDP;
+       ui->ui_pr = pr;
        ui->ui_src = laddr;
        ui->ui_dst = faddr;
        ui->ui_sport = lport;
        ui->ui_dport = fport;
        ui->ui_ulen = htons((u_short)len + sizeof(struct udphdr));
+       if (pr == IPPROTO_UDPLITE) {
+               struct udpcb *up;
+               uint16_t plen;
+
+               up = intoudpcb(inp);
+               cscov = up->u_txcslen;
+               plen = (u_short)len + sizeof(struct udphdr);
+               if (cscov >= plen)
+                       cscov = 0;
+               ui->ui_len = htons(plen);
+               ui->ui_ulen = htons(cscov);
+               /*
+                * For UDP-Lite, checksum coverage length of zero means
+                * the entire UDPLite packet is covered by the checksum.
+                */
+                cscov_partial = (cscov == 0) ? 0 : 1;
+       } else
+               ui->ui_v = IPVERSION << 4;
 
        /*
         * Set the Don't Fragment bit in the IP header.
@@ -1231,24 +1370,29 @@ udp_output(struct inpcb *inp, struct mbu
        /*
         * Set up checksum and output datagram.
         */
-       if (V_udp_cksum) {
+       ui->ui_sum = 0;
+       if (cscov_partial) {
+               if (inp->inp_flags & INP_ONESBCAST)
+                       faddr.s_addr = INADDR_BROADCAST;
+               if ((ui->ui_sum = in_cksum(m, sizeof(struct ip) + cscov)) == 0)
+                       ui->ui_sum = 0xffff;
+       } else if (V_udp_cksum || !cscov_partial) {
                if (inp->inp_flags & INP_ONESBCAST)
                        faddr.s_addr = INADDR_BROADCAST;
                ui->ui_sum = in_pseudo(ui->ui_src.s_addr, faddr.s_addr,
-                   htons((u_short)len + sizeof(struct udphdr) + IPPROTO_UDP));
+                   htons((u_short)len + sizeof(struct udphdr) + pr));
                m->m_pkthdr.csum_flags = CSUM_UDP;
                m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum);
-       } else
-               ui->ui_sum = 0;
+       }
        ((struct ip *)ui)->ip_len = htons(sizeof(struct udpiphdr) + len);
        ((struct ip *)ui)->ip_ttl = inp->inp_ip_ttl;    /* XXX */
        ((struct ip *)ui)->ip_tos = tos;                /* XXX */
        UDPSTAT_INC(udps_opackets);
 
        if (unlock_udbinfo == UH_WLOCKED)
-               INP_HASH_WUNLOCK(&V_udbinfo);
+               INP_HASH_WUNLOCK(pcbinfo);
        else if (unlock_udbinfo == UH_RLOCKED)
-               INP_HASH_RUNLOCK(&V_udbinfo);
+               INP_HASH_RUNLOCK(pcbinfo);
        UDP_PROBE(send, NULL, inp, &ui->ui_i, inp, &ui->ui_u);
        error = ip_output(m, inp->inp_options, NULL, ipflags,
            inp->inp_moptions, inp);
@@ -1260,10 +1404,10 @@ udp_output(struct inpcb *inp, struct mbu
 
 release:
        if (unlock_udbinfo == UH_WLOCKED) {
-               INP_HASH_WUNLOCK(&V_udbinfo);
+               INP_HASH_WUNLOCK(pcbinfo);
                INP_WUNLOCK(inp);
        } else if (unlock_udbinfo == UH_RLOCKED) {
-               INP_HASH_RUNLOCK(&V_udbinfo);
+               INP_HASH_RUNLOCK(pcbinfo);
                INP_RUNLOCK(inp);
        } else
                INP_RUNLOCK(inp);
@@ -1410,15 +1554,17 @@ static void
 udp_abort(struct socket *so)
 {
        struct inpcb *inp;
+       struct inpcbinfo *pcbinfo;
 
+       pcbinfo = get_inpcbinfo(so->so_proto->pr_protocol);
        inp = sotoinpcb(so);
        KASSERT(inp != NULL, ("udp_abort: inp == NULL"));
        INP_WLOCK(inp);
        if (inp->inp_faddr.s_addr != INADDR_ANY) {
-               INP_HASH_WLOCK(&V_udbinfo);
+               INP_HASH_WLOCK(pcbinfo);
                in_pcbdisconnect(inp);
                inp->inp_laddr.s_addr = INADDR_ANY;
-               INP_HASH_WUNLOCK(&V_udbinfo);
+               INP_HASH_WUNLOCK(pcbinfo);
                soisdisconnected(so);
        }
        INP_WUNLOCK(inp);
@@ -1428,17 +1574,19 @@ static int
 udp_attach(struct socket *so, int proto, struct thread *td)
 {
        struct inpcb *inp;
+       struct inpcbinfo *pcbinfo;
        int error;
 
+       pcbinfo = get_inpcbinfo(so->so_proto->pr_protocol);
        inp = sotoinpcb(so);
        KASSERT(inp == NULL, ("udp_attach: inp != NULL"));
        error = soreserve(so, udp_sendspace, udp_recvspace);
        if (error)
                return (error);
-       INP_INFO_WLOCK(&V_udbinfo);
-       error = in_pcballoc(so, &V_udbinfo);
+       INP_INFO_WLOCK(pcbinfo);
+       error = in_pcballoc(so, pcbinfo);
        if (error) {
-               INP_INFO_WUNLOCK(&V_udbinfo);
+               INP_INFO_WUNLOCK(pcbinfo);
                return (error);
        }
 
@@ -1450,12 +1598,12 @@ udp_attach(struct socket *so, int proto,
        if (error) {
                in_pcbdetach(inp);
                in_pcbfree(inp);
-               INP_INFO_WUNLOCK(&V_udbinfo);
+               INP_INFO_WUNLOCK(pcbinfo);
                return (error);
        }
 
        INP_WUNLOCK(inp);
-       INP_INFO_WUNLOCK(&V_udbinfo);
+       INP_INFO_WUNLOCK(pcbinfo);
        return (0);
 }
 #endif /* INET */
@@ -1486,14 +1634,16 @@ static int
 udp_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
 {
        struct inpcb *inp;
+       struct inpcbinfo *pcbinfo;
        int error;
 
+       pcbinfo = get_inpcbinfo(so->so_proto->pr_protocol);
        inp = sotoinpcb(so);
        KASSERT(inp != NULL, ("udp_bind: inp == NULL"));
        INP_WLOCK(inp);
-       INP_HASH_WLOCK(&V_udbinfo);
+       INP_HASH_WLOCK(pcbinfo);
        error = in_pcbbind(inp, nam, td->td_ucred);
-       INP_HASH_WUNLOCK(&V_udbinfo);
+       INP_HASH_WUNLOCK(pcbinfo);
        INP_WUNLOCK(inp);
        return (error);
 }
@@ -1502,15 +1652,17 @@ static void
 udp_close(struct socket *so)
 {
        struct inpcb *inp;
+       struct inpcbinfo *pcbinfo;
 
+       pcbinfo = get_inpcbinfo(so->so_proto->pr_protocol);
        inp = sotoinpcb(so);
        KASSERT(inp != NULL, ("udp_close: inp == NULL"));
        INP_WLOCK(inp);
        if (inp->inp_faddr.s_addr != INADDR_ANY) {
-               INP_HASH_WLOCK(&V_udbinfo);
+               INP_HASH_WLOCK(pcbinfo);
                in_pcbdisconnect(inp);
                inp->inp_laddr.s_addr = INADDR_ANY;
-               INP_HASH_WUNLOCK(&V_udbinfo);
+               INP_HASH_WUNLOCK(pcbinfo);
                soisdisconnected(so);
        }
        INP_WUNLOCK(inp);
@@ -1520,9 +1672,11 @@ static int
 udp_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
 {
        struct inpcb *inp;
-       int error;
+       struct inpcbinfo *pcbinfo;
        struct sockaddr_in *sin;
+       int error;
 
+       pcbinfo = get_inpcbinfo(so->so_proto->pr_protocol);
        inp = sotoinpcb(so);
        KASSERT(inp != NULL, ("udp_connect: inp == NULL"));
        INP_WLOCK(inp);
@@ -1536,9 +1690,9 @@ udp_connect(struct socket *so, struct so
                INP_WUNLOCK(inp);
                return (error);
        }
-       INP_HASH_WLOCK(&V_udbinfo);
+       INP_HASH_WLOCK(pcbinfo);
        error = in_pcbconnect(inp, nam, td->td_ucred);
-       INP_HASH_WUNLOCK(&V_udbinfo);
+       INP_HASH_WUNLOCK(pcbinfo);
        if (error == 0)
                soisconnected(so);
        INP_WUNLOCK(inp);
@@ -1549,20 +1703,22 @@ static void
 udp_detach(struct socket *so)
 {
        struct inpcb *inp;
+       struct inpcbinfo *pcbinfo;
        struct udpcb *up;
 
+       pcbinfo = get_inpcbinfo(so->so_proto->pr_protocol);
        inp = sotoinpcb(so);
        KASSERT(inp != NULL, ("udp_detach: inp == NULL"));
        KASSERT(inp->inp_faddr.s_addr == INADDR_ANY,
            ("udp_detach: not disconnected"));
-       INP_INFO_WLOCK(&V_udbinfo);
+       INP_INFO_WLOCK(pcbinfo);
        INP_WLOCK(inp);
        up = intoudpcb(inp);
        KASSERT(up != NULL, ("%s: up == NULL", __func__));
        inp->inp_ppcb = NULL;
        in_pcbdetach(inp);
        in_pcbfree(inp);
-       INP_INFO_WUNLOCK(&V_udbinfo);
+       INP_INFO_WUNLOCK(pcbinfo);
        udp_discardcb(up);
 }
 
@@ -1570,7 +1726,9 @@ static int
 udp_disconnect(struct socket *so)
 {
        struct inpcb *inp;
+       struct inpcbinfo *pcbinfo;
 
+       pcbinfo = get_inpcbinfo(so->so_proto->pr_protocol);
        inp = sotoinpcb(so);
        KASSERT(inp != NULL, ("udp_disconnect: inp == NULL"));
        INP_WLOCK(inp);
@@ -1578,10 +1736,10 @@ udp_disconnect(struct socket *so)
                INP_WUNLOCK(inp);
                return (ENOTCONN);
        }
-       INP_HASH_WLOCK(&V_udbinfo);
+       INP_HASH_WLOCK(pcbinfo);
        in_pcbdisconnect(inp);
        inp->inp_laddr.s_addr = INADDR_ANY;
-       INP_HASH_WUNLOCK(&V_udbinfo);
+       INP_HASH_WUNLOCK(pcbinfo);
        SOCK_LOCK(so);
        so->so_state &= ~SS_ISCONNECTED;                /* XXX */
        SOCK_UNLOCK(so);

Modified: head/sys/netinet/udp_var.h
==============================================================================
--- head/sys/netinet/udp_var.h  Mon Apr  7 01:52:35 2014        (r264211)
+++ head/sys/netinet/udp_var.h  Mon Apr  7 01:53:03 2014        (r264212)
@@ -63,6 +63,8 @@ typedef void(*udp_tun_func_t)(struct mbu
 struct udpcb {
        udp_tun_func_t  u_tun_func;     /* UDP kernel tunneling callback. */
        u_int           u_flags;        /* Generic UDP flags. */
+       uint16_t        u_rxcslen;      /* Coverage for incoming datagrams. */
+       uint16_t        u_txcslen;      /* Coverage for outgoing datagrams. */
 };
 
 #define        intoudpcb(ip)   ((struct udpcb *)(ip)->inp_ppcb)
@@ -129,8 +131,12 @@ SYSCTL_DECL(_net_inet_udp);
 extern struct pr_usrreqs       udp_usrreqs;
 VNET_DECLARE(struct inpcbhead, udb);
 VNET_DECLARE(struct inpcbinfo, udbinfo);
+VNET_DECLARE(struct inpcbhead, ulitecb);
+VNET_DECLARE(struct inpcbinfo, ulitecbinfo);
 #define        V_udb                   VNET(udb)
 #define        V_udbinfo               VNET(udbinfo)
+#define        V_ulitecb               VNET(ulitecb)
+#define        V_ulitecbinfo           VNET(ulitecbinfo)
 
 extern u_long                  udp_sendspace;
 extern u_long                  udp_recvspace;
@@ -140,20 +146,37 @@ VNET_DECLARE(int, udp_blackhole);
 #define        V_udp_blackhole         VNET(udp_blackhole)
 extern int                     udp_log_in_vain;
 
+static __inline struct inpcbinfo *
+get_inpcbinfo(uint8_t protocol)
+{
+       return (protocol == IPPROTO_UDP) ? &V_udbinfo : &V_ulitecbinfo;
+}
+
+static __inline struct inpcbhead *
+get_pcblist(uint8_t protocol)
+{
+       return (protocol == IPPROTO_UDP) ? &V_udb : &V_ulitecb;
+}
+
 int             udp_newudpcb(struct inpcb *);
 void            udp_discardcb(struct udpcb *);
 
 void            udp_ctlinput(int, struct sockaddr *, void *);
+void            udplite_ctlinput(int, struct sockaddr *, void *);
 int             udp_ctloutput(struct socket *, struct sockopt *);
 void            udp_init(void);
+void            udplite_init(void);
 #ifdef VIMAGE
 void            udp_destroy(void);
+void            udplite_destroy(void);
 #endif
 void            udp_input(struct mbuf *, int);
+void            udplite_input(struct mbuf *, int);
 struct inpcb   *udp_notify(struct inpcb *inp, int errno);
 int             udp_shutdown(struct socket *so);
 
 int udp_set_kernel_tunneling(struct socket *so, udp_tun_func_t f);
-#endif
 
-#endif
+#endif /* _KERNEL */
+
+#endif /* _NETINET_UDP_VAR_H_ */

Added: head/sys/netinet/udplite.h
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/sys/netinet/udplite.h  Mon Apr  7 01:53:03 2014        (r264212)
@@ -0,0 +1,38 @@
+/*-
+ * Copyright (c) 2014, Kevin Lo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _NETINET_UDPLITE_H_
+#define        _NETINET_UDPLITE_H_
+
+/* 
+ * User-settable options (used with setsockopt).
+ */
+#define        UDPLITE_SEND_CSCOV      2       /* Sender checksum coverage. */
+#define        UDPLITE_RECV_CSCOV      4       /* Receiver checksum coverage. 
*/
+
+#endif /* !_NETINET_UDPLITE_H_ */

Modified: head/sys/netinet6/in6_ifattach.c
==============================================================================
--- head/sys/netinet6/in6_ifattach.c    Mon Apr  7 01:52:35 2014        
(r264211)
+++ head/sys/netinet6/in6_ifattach.c    Mon Apr  7 01:53:03 2014        
(r264212)
@@ -829,6 +829,7 @@ in6_ifdetach(struct ifnet *ifp)
        }
 
        in6_pcbpurgeif0(&V_udbinfo, ifp);
+       in6_pcbpurgeif0(&V_ulitecbinfo, ifp);
        in6_pcbpurgeif0(&V_ripcbinfo, ifp);
        /* leave from all multicast groups joined */
        in6_purgemaddrs(ifp);

Modified: head/sys/netinet6/in6_proto.c
==============================================================================
--- head/sys/netinet6/in6_proto.c       Mon Apr  7 01:52:35 2014        
(r264211)
+++ head/sys/netinet6/in6_proto.c       Mon Apr  7 01:53:03 2014        
(r264212)
@@ -215,6 +215,19 @@ struct ip6protosw inet6sw[] = {
 },
 #endif /* SCTP */
 {
+       .pr_type =              SOCK_DGRAM,
+       .pr_domain =            &inet6domain,
+       .pr_protocol =          IPPROTO_UDPLITE,
+       .pr_flags =             PR_ATOMIC|PR_ADDR,
+       .pr_input =             udp6_input,
+       .pr_ctlinput =          udplite6_ctlinput,
+       .pr_ctloutput =         udp_ctloutput,
+#ifndef INET   /* Do not call initialization twice. */
+       .pr_init =              udplite_init,
+#endif
+       .pr_usrreqs =           &udp6_usrreqs,
+},
+{
        .pr_type =              SOCK_RAW,
        .pr_domain =            &inet6domain,
        .pr_protocol =          IPPROTO_RAW,

Modified: head/sys/netinet6/udp6_usrreq.c
==============================================================================
--- head/sys/netinet6/udp6_usrreq.c     Mon Apr  7 01:52:35 2014        
(r264211)
+++ head/sys/netinet6/udp6_usrreq.c     Mon Apr  7 01:53:03 2014        
(r264212)
@@ -1,6 +1,7 @@
 /*-
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
  * Copyright (c) 2010-2011 Juniper Networks, Inc.
+ * Copyright (c) 2014 Kevin Lo
  * All rights reserved.
  *
  * Portions of this software were developed by Robert N. M. Watson under
@@ -109,6 +110,7 @@ __FBSDID("$FreeBSD$");
 #include <netinet/ip_var.h>
 #include <netinet/udp.h>
 #include <netinet/udp_var.h>
+#include <netinet/udplite.h>
 
 #include <netinet6/ip6protosw.h>
 #include <netinet6/ip6_var.h>
@@ -181,12 +183,15 @@ udp6_input(struct mbuf **mp, int *offp, 
        struct ip6_hdr *ip6;
        struct udphdr *uh;
        struct inpcb *inp;
+       struct inpcbinfo *pcbinfo;
        struct udpcb *up;
        int off = *offp;
+       int cscov_partial;
        int plen, ulen;
        struct sockaddr_in6 fromsa;
        struct m_tag *fwd_tag;
        uint16_t uh_sum;
+       uint8_t nxt;
 
        ifp = m->m_pkthdr.rcvif;
        ip6 = mtod(m, struct ip6_hdr *);
@@ -218,6 +223,13 @@ udp6_input(struct mbuf **mp, int *offp, 
        plen = ntohs(ip6->ip6_plen) - off + sizeof(*ip6);
        ulen = ntohs((u_short)uh->uh_ulen);
 
+       nxt = ip6->ip6_nxt;
+       cscov_partial = (nxt == IPPROTO_UDPLITE) ? 1 : 0;
+       if (nxt == IPPROTO_UDPLITE && ulen == 0) {
+               /* Zero means checksum over the complete packet. */
+               ulen = plen;
+               cscov_partial = 0;
+       }
        if (plen != ulen) {
                UDPSTAT_INC(udps_badlen);
                goto badunlocked;
@@ -227,19 +239,22 @@ udp6_input(struct mbuf **mp, int *offp, 
         * Checksum extended UDP header and data.
         */
        if (uh->uh_sum == 0) {
-               UDPSTAT_INC(udps_nosum);
-               goto badunlocked;
+               if (ulen > plen || ulen < sizeof(struct udphdr)) {
+                       UDPSTAT_INC(udps_nosum);
+                       goto badunlocked;
+               }
        }
 
-       if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID_IPV6) {
+       if ((m->m_pkthdr.csum_flags & CSUM_DATA_VALID_IPV6) &&
+           !cscov_partial) {
                if (m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR)
                        uh_sum = m->m_pkthdr.csum_data;
                else
-                       uh_sum = in6_cksum_pseudo(ip6, ulen,
-                           IPPROTO_UDP, m->m_pkthdr.csum_data);
+                       uh_sum = in6_cksum_pseudo(ip6, ulen, nxt,
+                           m->m_pkthdr.csum_data);
                uh_sum ^= 0xffff;
        } else
-               uh_sum = in6_cksum(m, IPPROTO_UDP, off, ulen);
+               uh_sum = in6_cksum(m, nxt, off, ulen);
 
        if (uh_sum != 0) {
                UDPSTAT_INC(udps_badsum);
@@ -252,11 +267,13 @@ udp6_input(struct mbuf **mp, int *offp, 
        init_sin6(&fromsa, m);
        fromsa.sin6_port = uh->uh_sport;
 
+       pcbinfo = get_inpcbinfo(nxt);
        if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
                struct inpcb *last;

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to