From owner-svn-src-head@FreeBSD.ORG Mon Apr 7 01:53:06 2014 Return-Path: Delivered-To: svn-src-head@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id 1C5D68DC; Mon, 7 Apr 2014 01:53:06 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 0804283D; Mon, 7 Apr 2014 01:53:06 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.8/8.14.8) with ESMTP id s371r5T0064927; Mon, 7 Apr 2014 01:53:05 GMT (envelope-from kevlo@svn.freebsd.org) Received: (from kevlo@localhost) by svn.freebsd.org (8.14.8/8.14.8/Submit) id s371r3Gn064909; Mon, 7 Apr 2014 01:53:03 GMT (envelope-from kevlo@svn.freebsd.org) Message-Id: <201404070153.s371r3Gn064909@svn.freebsd.org> From: Kevin Lo Date: Mon, 7 Apr 2014 01:53:03 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r264212 - in head: lib/libc/net sys/netinet sys/netinet6 sys/sys X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.17 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 07 Apr 2014 01:53:06 -0000 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 #include +#include #ifdef IPSEC #include @@ -98,8 +100,9 @@ __FBSDID("$FreeBSD$"); #include /* - * 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 #include #include +#include #include #include @@ -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 ***