Date: Mon, 17 Mar 2014 02:07:41 -0400 From: Joe Nosay <superbisquit@gmail.com> To: Adrian Chadd <adrian@freebsd.org>, FreeBSD Hackers <freebsd-hackers@freebsd.org> Subject: Re: Definition struct and int Message-ID: <CA%2BWntOvLHYAxp5_s%2BCrWnQSy5H=G_p07WMRZn-=77Dx2kecr0g@mail.gmail.com> In-Reply-To: <CA%2BWntOtsBu=1n134r6Te2kVQ4B=o%2BBzmTcKodxHdGEOwSChi9w@mail.gmail.com> References: <CA%2BWntOtT_SfQ4KRLyaWSQ8g-kkq3P7-YkJR7siqUZYAb-WtYTQ@mail.gmail.com> <CAJ-Vmok5RTi8SSm3PMXSYDpWonO3O%2BLetvH8ZaPzb66OD61S1A@mail.gmail.com> <CA%2BWntOufKY=7ikh2rNfXbxSKHW=WWKiW9iNXJOwZ%2Bar5GzXa9A@mail.gmail.com> <CA%2BWntOtsBu=1n134r6Te2kVQ4B=o%2BBzmTcKodxHdGEOwSChi9w@mail.gmail.com>
next in thread | previous in thread | raw e-mail | index | archive | help
[-- Attachment #1 --] On Sun, Mar 16, 2014 at 1:07 AM, Joe Nosay <superbisquit@gmail.com> wrote: > > > > On Fri, Mar 14, 2014 at 5:43 PM, Joe Nosay <superbisquit@gmail.com> wrote: > >> >> >> >> On Thu, Mar 13, 2014 at 9:26 PM, Adrian Chadd <adrian@freebsd.org> wrote: >> >>> Is this -HEAD, or? >>> >>> >>> -a >>> >>> >>> On 13 March 2014 18:13, Joe Nosay <superbisquit@gmail.com> wrote: >>> > Testing of a patch for using UDP Lite on FreeBSD caused no compilation >>> > errors; however, after adding the options of "VIMAGE" and "MROUTING" to >>> > conf/kern?GENERIC, make buildkernel stops with: >>> > /usr/src/sys/netinet/udp_usrreq.c:1701:18: error: too few arguments to >>> > function >>> > call, expected 2, have 1 >>> > udp_discardcb(up); >>> > ~~~~~~~~~~~~~ ^ >>> > /usr/src/sys/netinet/udp_usrreq.c:274:1: note: 'udp_discardcb' >>> declared here >>> > void >>> > ^ >>> > 1 error generated. >>> > *** Error code 1 >>> > >>> > The file in question of >>> > /usr/src/sys/netinet/udp_usrreq.c >>> > >>> > has the value of >>> > udp_discardcb(struct udpcb *up, int isudp) >>> > >>> > that is causing the problem. >>> > >>> > >>> > >>> > I believe that the compiler is looking for a value to int isudp but >>> that >>> > value does not exist. >>> > _______________________________________________ >>> > freebsd-hackers@freebsd.org mailing list >>> > http://lists.freebsd.org/mailman/listinfo/freebsd-hackers >>> > To unsubscribe, send any mail to " >>> freebsd-hackers-unsubscribe@freebsd.org" >>> >> >> >> No, it is 10.0 RELEASE #0 r262601 >> >> Last time, I had entered too many files. Here are the relative files. >> >> >> There was no problem in compiling as listed earlier. I have not studied >> C enough to solve this problem; however, I can see that int isudp happens >> once while the next closest account is int isudplite. >> >> > > I've just upgraded source to head. I have three patches for UDP Lite. The > question is which one(s) should I use. > > The udp-v.diff only has a reference to udp_discardcb up, while patch > udplite and udplite.diff have the struct and int references. > > Could someone possibly point me towards some online documentation that would allow me to learn of a solution to this problem? This would be much better because I would be solving and learning. Included are the three patches mentioned earlier. Yes, I will be looking at them again. Apologies for any noise and if I am coming off the wrong way. [-- Attachment #2 --] Index: sys/netinet/udp_usrreq.c =================================================================== --- sys/netinet/udp_usrreq.c (revision 249785) +++ sys/netinet/udp_usrreq.c (working copy) @@ -949,6 +949,7 @@ udp_output(struct inpcb *inp, struct mbuf *m, stru int len = m->m_pkthdr.len; struct in_addr faddr, laddr; struct cmsghdr *cm; + struct inpcbinfo *pcbinfo; struct sockaddr_in *sin, src; int error = 0; int ipflags; @@ -1049,12 +1050,13 @@ udp_output(struct inpcb *inp, struct mbuf *m, stru * * XXXRW: Check that hash locking update here is correct. */ + pcbinfo = inp->inp_pcbinfo; 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) || @@ -1062,7 +1064,7 @@ udp_output(struct inpcb *inp, struct mbuf *m, stru (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; @@ -1075,7 +1077,7 @@ udp_output(struct inpcb *inp, struct mbuf *m, stru 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)) { @@ -1126,7 +1128,7 @@ udp_output(struct inpcb *inp, struct mbuf *m, stru 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); @@ -1141,7 +1143,7 @@ udp_output(struct inpcb *inp, struct mbuf *m, stru 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. @@ -1237,9 +1239,9 @@ udp_output(struct inpcb *inp, struct mbuf *m, stru 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); error = ip_output(m, inp->inp_options, NULL, ipflags, inp->inp_moptions, inp); if (unlock_udbinfo == UH_WLOCKED) @@ -1250,10 +1252,10 @@ udp_output(struct inpcb *inp, struct mbuf *m, stru 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); @@ -1405,10 +1407,10 @@ udp_abort(struct socket *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(inp->inp_pcbinfo); in_pcbdisconnect(inp); inp->inp_laddr.s_addr = INADDR_ANY; - INP_HASH_WUNLOCK(&V_udbinfo); + INP_HASH_WUNLOCK(inp->inp_pcbinfo); soisdisconnected(so); } INP_WUNLOCK(inp); @@ -1481,9 +1483,9 @@ udp_bind(struct socket *so, struct sockaddr *nam, inp = sotoinpcb(so); KASSERT(inp != NULL, ("udp_bind: inp == NULL")); INP_WLOCK(inp); - INP_HASH_WLOCK(&V_udbinfo); + INP_HASH_WLOCK(inp->inp_pcbinfo); error = in_pcbbind(inp, nam, td->td_ucred); - INP_HASH_WUNLOCK(&V_udbinfo); + INP_HASH_WUNLOCK(inp->inp_pcbinfo); INP_WUNLOCK(inp); return (error); } @@ -1497,10 +1499,10 @@ udp_close(struct socket *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(inp->inp_pcbinfo); in_pcbdisconnect(inp); inp->inp_laddr.s_addr = INADDR_ANY; - INP_HASH_WUNLOCK(&V_udbinfo); + INP_HASH_WUNLOCK(inp->inp_pcbinfo); soisdisconnected(so); } INP_WUNLOCK(inp); @@ -1526,9 +1528,9 @@ udp_connect(struct socket *so, struct sockaddr *na INP_WUNLOCK(inp); return (error); } - INP_HASH_WLOCK(&V_udbinfo); + INP_HASH_WLOCK(inp->inp_pcbinfo); error = in_pcbconnect(inp, nam, td->td_ucred); - INP_HASH_WUNLOCK(&V_udbinfo); + INP_HASH_WUNLOCK(inp->inp_pcbinfo); if (error == 0) soisconnected(so); INP_WUNLOCK(inp); @@ -1545,14 +1547,14 @@ udp_detach(struct socket *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(inp->inp_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(inp->inp_pcbinfo); udp_discardcb(up); } @@ -1568,10 +1570,10 @@ udp_disconnect(struct socket *so) INP_WUNLOCK(inp); return (ENOTCONN); } - INP_HASH_WLOCK(&V_udbinfo); + INP_HASH_WLOCK(inp->inp_pcbinfo); in_pcbdisconnect(inp); inp->inp_laddr.s_addr = INADDR_ANY; - INP_HASH_WUNLOCK(&V_udbinfo); + INP_HASH_WUNLOCK(inp->inp_pcbinfo); SOCK_LOCK(so); so->so_state &= ~SS_ISCONNECTED; /* XXX */ SOCK_UNLOCK(so); Index: sys/netinet6/udp6_usrreq.c =================================================================== --- sys/netinet6/udp6_usrreq.c (revision 249785) +++ sys/netinet6/udp6_usrreq.c (working copy) @@ -826,10 +826,10 @@ udp6_abort(struct socket *so) INP_WLOCK(inp); if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) { - INP_HASH_WLOCK(&V_udbinfo); + INP_HASH_WLOCK(inp->inp_pcbinfo); in6_pcbdisconnect(inp); inp->in6p_laddr = in6addr_any; - INP_HASH_WUNLOCK(&V_udbinfo); + INP_HASH_WUNLOCK(inp->inp_pcbinfo); soisdisconnected(so); } INP_WUNLOCK(inp); @@ -891,7 +891,7 @@ udp6_bind(struct socket *so, struct sockaddr *nam, KASSERT(inp != NULL, ("udp6_bind: inp == NULL")); INP_WLOCK(inp); - INP_HASH_WLOCK(&V_udbinfo); + INP_HASH_WLOCK(inp->inp_pcbinfo); inp->inp_vflag &= ~INP_IPV4; inp->inp_vflag |= INP_IPV6; if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) { @@ -919,7 +919,7 @@ udp6_bind(struct socket *so, struct sockaddr *nam, #ifdef INET out: #endif - INP_HASH_WUNLOCK(&V_udbinfo); + INP_HASH_WUNLOCK(inp->inp_pcbinfo); INP_WUNLOCK(inp); return (error); } @@ -943,10 +943,10 @@ udp6_close(struct socket *so) #endif INP_WLOCK(inp); if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) { - INP_HASH_WLOCK(&V_udbinfo); + INP_HASH_WLOCK(inp->inp_pcbinfo); in6_pcbdisconnect(inp); inp->in6p_laddr = in6addr_any; - INP_HASH_WUNLOCK(&V_udbinfo); + INP_HASH_WUNLOCK(inp->inp_pcbinfo); soisdisconnected(so); } INP_WUNLOCK(inp); @@ -985,10 +985,10 @@ udp6_connect(struct socket *so, struct sockaddr *n error = prison_remote_ip4(td->td_ucred, &sin.sin_addr); if (error != 0) goto out; - INP_HASH_WLOCK(&V_udbinfo); + INP_HASH_WLOCK(inp->inp_pcbinfo); error = in_pcbconnect(inp, (struct sockaddr *)&sin, td->td_ucred); - INP_HASH_WUNLOCK(&V_udbinfo); + INP_HASH_WUNLOCK(inp->inp_pcbinfo); if (error == 0) soisconnected(so); goto out; @@ -1003,9 +1003,9 @@ udp6_connect(struct socket *so, struct sockaddr *n error = prison_remote_ip6(td->td_ucred, &sin6->sin6_addr); if (error != 0) goto out; - INP_HASH_WLOCK(&V_udbinfo); + INP_HASH_WLOCK(inp->inp_pcbinfo); error = in6_pcbconnect(inp, nam, td->td_ucred); - INP_HASH_WUNLOCK(&V_udbinfo); + INP_HASH_WUNLOCK(inp->inp_pcbinfo); if (error == 0) soisconnected(so); out: @@ -1022,13 +1022,13 @@ udp6_detach(struct socket *so) inp = sotoinpcb(so); KASSERT(inp != NULL, ("udp6_detach: inp == NULL")); - INP_INFO_WLOCK(&V_udbinfo); + INP_INFO_WLOCK(inp->inp_pcbinfo); INP_WLOCK(inp); up = intoudpcb(inp); KASSERT(up != NULL, ("%s: up == NULL", __func__)); in_pcbdetach(inp); in_pcbfree(inp); - INP_INFO_WUNLOCK(&V_udbinfo); + INP_INFO_WUNLOCK(inp->inp_pcbinfo); udp_discardcb(up); } @@ -1058,10 +1058,10 @@ udp6_disconnect(struct socket *so) goto out; } - INP_HASH_WLOCK(&V_udbinfo); + INP_HASH_WLOCK(inp->inp_pcbinfo); in6_pcbdisconnect(inp); inp->in6p_laddr = in6addr_any; - INP_HASH_WUNLOCK(&V_udbinfo); + INP_HASH_WUNLOCK(inp->inp_pcbinfo); SOCK_LOCK(so); so->so_state &= ~SS_ISCONNECTED; /* XXX */ SOCK_UNLOCK(so); @@ -1128,9 +1128,9 @@ udp6_send(struct socket *so, int flags, struct mbu #ifdef MAC mac_inpcb_create_mbuf(inp, m); #endif - INP_HASH_WLOCK(&V_udbinfo); + INP_HASH_WLOCK(inp->inp_pcbinfo); error = udp6_output(inp, m, addr, control, td); - INP_HASH_WUNLOCK(&V_udbinfo); + INP_HASH_WUNLOCK(inp->inp_pcbinfo); #ifdef INET #endif INP_WUNLOCK(inp); [-- Attachment #3 --] Index: lib/libc/net/getaddrinfo.c =================================================================== --- lib/libc/net/getaddrinfo.c (revision 248811) +++ lib/libc/net/getaddrinfo.c (working copy) @@ -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 }, }; @@ -1476,6 +1478,9 @@ get_port(struct addrinfo *ai, const char *servname case IPPROTO_SCTP: proto = "sctp"; break; + case IPPROTO_UDPLITE: + proto = "udplite"; + break; default: proto = NULL; break; Index: sys/netinet/in.c =================================================================== --- sys/netinet/in.c (revision 248811) +++ sys/netinet/in.c (working copy) @@ -1205,6 +1205,7 @@ in_ifdetach(struct ifnet *ifp) in_pcbpurgeif0(&V_ripcbinfo, ifp); in_pcbpurgeif0(&V_udbinfo, ifp); + in_pcbpurgeif0(&V_ulitecbinfo, ifp); in_purgemaddrs(ifp); } Index: sys/netinet/in.h =================================================================== --- sys/netinet/in.h (revision 248811) +++ sys/netinet/in.h (working copy) @@ -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 /* UDPLite */ /* 101-254: Partly Unassigned */ #define IPPROTO_PIM 103 /* Protocol Independent Mcast */ #define IPPROTO_CARP 112 /* CARP */ Index: sys/netinet/in_pcb.c =================================================================== --- sys/netinet/in_pcb.c (revision 248811) +++ sys/netinet/in_pcb.c (working copy) @@ -390,13 +390,14 @@ in_pcb_lport(struct inpcb *inp, struct in_addr *la 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; @@ -406,8 +407,8 @@ in_pcb_lport(struct inpcb *inp, struct in_addr *la */ 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 Index: sys/netinet/in_proto.c =================================================================== --- sys/netinet/in_proto.c (revision 248811) +++ sys/netinet/in_proto.c (working copy) @@ -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, @@ -370,6 +384,7 @@ SYSCTL_NODE(_net_inet, IPPROTO_TCP, tcp, CTLFLAG_R #ifdef SCTP SYSCTL_NODE(_net_inet, IPPROTO_SCTP, sctp, CTLFLAG_RW, 0, "SCTP"); #endif +SYSCTL_NODE(_net_inet, IPPROTO_UDPLITE, udplite,CTLFLAG_RW, 0, "UDPLite"); SYSCTL_NODE(_net_inet, IPPROTO_IGMP, igmp, CTLFLAG_RW, 0, "IGMP"); #ifdef IPSEC /* XXX no protocol # to use, pick something "reserved" */ Index: sys/netinet/udp_usrreq.c =================================================================== --- sys/netinet/udp_usrreq.c (revision 248811) +++ sys/netinet/udp_usrreq.c (working copy) @@ -84,6 +84,7 @@ __FBSDID("$FreeBSD$"); #endif #include <netinet/udp.h> #include <netinet/udp_var.h> +#include <netinet/udplite.h> #ifdef IPSEC #include <netipsec/ipsec.h> @@ -131,13 +132,18 @@ u_long udp_recvspace = 40 * (1024 + #endif ); + SYSCTL_ULONG(_net_inet_udp, UDPCTL_RECVSPACE, recvspace, CTLFLAG_RW, &udp_recvspace, 0, "Maximum space for incoming UDP datagrams"); 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) +static VNET_DEFINE(uma_zone_t, udplitecb_zone); +#define V_udpcb_zone VNET(udpcb_zone) +#define V_udplitecb_zone VNET(udplitecb_zone) #ifndef UDBHASHSIZE #define UDBHASHSIZE 128 @@ -167,10 +173,16 @@ static void udp_zone_change(void *tag) { - uma_zone_set_max(V_udbinfo.ipi_zone, maxsockets); - uma_zone_set_max(V_udpcb_zone, maxsockets); + udp_common_zone_change(V_udbinfo, V_udpcb_zone); } +static void +udplite_zone_change(void *tag) +{ + + udp_common_zone_change(V_ulitecbinfo, V_udplitecb_zone); +} + static int udp_inpcb_init(void *mem, int size, int flags) { @@ -181,21 +193,34 @@ udp_inpcb_init(void *mem, int size, int flags) 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) { - - in_pcbinfo_init(&V_udbinfo, "udp", &V_udb, UDBHASHSIZE, UDBHASHSIZE, - "udp_inpcb", udp_inpcb_init, NULL, UMA_ZONE_NOFREE, - IPI_HASHFIELDS_2TUPLE); - V_udpcb_zone = uma_zcreate("udpcb", sizeof(struct udpcb), - NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_NOFREE); - uma_zone_set_max(V_udpcb_zone, maxsockets); - uma_zone_set_warning(V_udpcb_zone, "kern.ipc.maxsockets limit reached"); + udp_common_init(&V_udbinfo, "udp", &V_udb, "udp_inpcb", udp_inpcb_init, + V_udpcb_zone, "udpcb"); EVENTHANDLER_REGISTER(maxsockets_change, udp_zone_change, NULL, EVENTHANDLER_PRI_ANY); } +void +udplite_init(void) +{ + udp_common_init(&V_ulitecbinfo, "udplite", &V_ulitecb, "udplite_inpcb", + udplite_inpcb_init, V_udplitecb_zone, "udplitecb"); + EVENTHANDLER_REGISTER(maxsockets_change, udplite_zone_change, NULL, + EVENTHANDLER_PRI_ANY); +} + /* * Kernel module interface for updating udpstat. The argument is an index * into udpstat treated as an array of u_long. While this encodes the @@ -215,7 +240,10 @@ udp_newudpcb(struct inpcb *inp) { struct udpcb *up; - up = uma_zalloc(V_udpcb_zone, M_NOWAIT | M_ZERO); + if (inp->inp_socket->so_proto->pr_protocol == IPPROTO_UDP) + up = uma_zalloc(V_udpcb_zone, M_NOWAIT | M_ZERO); + else + up = uma_zalloc(V_udplitecb_zone, M_NOWAIT | M_ZERO); if (up == NULL) return (ENOBUFS); inp->inp_ppcb = up; @@ -223,10 +251,12 @@ udp_newudpcb(struct inpcb *inp) } void -udp_discardcb(struct udpcb *up) +udp_discardcb(struct udpcb *up, int isudp) { - - uma_zfree(V_udpcb_zone, up); + if (isudp) + uma_zfree(V_udpcb_zone, up); + else + uma_zfree(V_udplitecb_zone, up); } #ifdef VIMAGE @@ -234,9 +264,15 @@ void udp_destroy(void) { - in_pcbinfo_destroy(&V_udbinfo); - uma_zdestroy(V_udpcb_zone); + udp_common_destroy(&V_udbinfo, V_udpcb_zone); } + +void +udplite_destroy(void) +{ + + udp_common_destroy(&V_ulitecbinfo, V_udplitecb_zone); +} #endif #ifdef INET @@ -339,10 +375,13 @@ udp_input(struct mbuf *m, int off) struct udphdr *uh; struct ifnet *ifp; struct inpcb *inp; + struct inpcbinfo *pcbinfo; uint16_t len, ip_len; struct ip save_ip; struct sockaddr_in udp_in; struct m_tag *fwd_tag; + int cscov_partial; + uint8_t pr; ifp = m->m_pkthdr.rcvif; UDPSTAT_INC(udps_ipackets); @@ -362,14 +401,15 @@ udp_input(struct mbuf *m, int off) */ ip = mtod(m, struct ip *); if (m->m_len < iphlen + sizeof(struct udphdr)) { - if ((m = m_pullup(m, iphlen + sizeof(struct udphdr))) == 0) { + if ((m = m_pullup(m, iphlen + sizeof(struct udphdr))) == NULL) { UDPSTAT_INC(udps_hdrops); return; } 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. */ @@ -392,12 +432,19 @@ 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); } /* @@ -415,20 +462,24 @@ 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; + if (pr == IPPROTO_UDP) + ((struct ipovly *)ip)->ih_len = uh->uh_ulen; + else + ((struct ipovly *)ip)->ih_len = htons(ip_len); uh_sum = in_cksum(m, len + sizeof (struct ip)); bcopy(b, ((struct ipovly *)ip)->ih_x1, 9); } @@ -440,14 +491,18 @@ udp_input(struct mbuf *m, int off) } else UDPSTAT_INC(udps_nosum); + pcbinfo = (pr == IPPROTO_UDP) ? &V_udbinfo : &V_ulitecbinfo; + 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 = (pr == IPPROTO_UDP) ? &V_udb : &V_ulitecb; 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 @@ -533,12 +588,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; } @@ -559,7 +614,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) { /* @@ -567,7 +622,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 | @@ -577,7 +632,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) { @@ -613,6 +668,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_append(inp, ip, m, iphlen, &udp_in); INP_RUNLOCK(inp); return; @@ -644,9 +709,9 @@ udp_notify(struct inpcb *inp, int errno) return (inp); } -#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; @@ -675,7 +740,7 @@ udp_notify(struct inpcb *inp, int errno) 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); @@ -685,9 +750,23 @@ udp_notify(struct inpcb *inp, int errno) INP_RUNLOCK(inp); } } else - in_pcbnotifyall(&V_udbinfo, faddr, inetctlerrmap[cmd], + in_pcbnotifyall(pcbinfo, faddr, inetctlerrmap[cmd], udp_notify); + return; } + +#ifdef INET +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 @@ -843,16 +922,16 @@ SYSCTL_PROC(_net_inet_udp, OID_AUTO, getcred, int udp_ctloutput(struct socket *so, struct sockopt *sopt) { - int error = 0, optval; + int isudplite, error, optval; struct inpcb *inp; -#ifdef IPSEC_NAT_T struct udpcb *up; -#endif + 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); @@ -910,7 +989,33 @@ udp_ctloutput(struct socket *so, struct sockopt *s } INP_WUNLOCK(inp); break; + case UDPLITE_SEND_CSCOV: + case UDPLITE_RECV_CSCOV: + if (!isudplite) + goto bad_setoptname; + INP_WUNLOCK(inp); + error = sooptcopyin(sopt, &optval, sizeof(optval), + sizeof(optval)); + if (error) + 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) { + error = EINVAL; + INP_WUNLOCK(inp); + break; + } + if (sopt->sopt_name == UDPLITE_SEND_CSCOV) + up->u_txcslen = optval; + else + up->u_rxcslen = optval; + INP_WUNLOCK(inp); + break; default: +bad_setoptname: INP_WUNLOCK(inp); error = ENOPROTOOPT; break; @@ -927,7 +1032,21 @@ udp_ctloutput(struct socket *so, struct sockopt *s error = sooptcopyout(sopt, &optval, sizeof optval); break; #endif + case UDPLITE_SEND_CSCOV: + case UDPLITE_RECV_CSCOV: + if (!isudplite) + goto bad_getoptname; + 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: +bad_getoptname: INP_WUNLOCK(inp); error = ENOPROTOOPT; break; @@ -949,12 +1068,16 @@ udp_output(struct inpcb *inp, struct mbuf *m, stru 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 @@ -1049,12 +1172,13 @@ udp_output(struct inpcb *inp, struct mbuf *m, stru * * XXXRW: Check that hash locking update here is correct. */ + pcbinfo = inp->inp_pcbinfo; 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) || @@ -1062,7 +1186,7 @@ udp_output(struct inpcb *inp, struct mbuf *m, stru (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; @@ -1075,7 +1199,7 @@ udp_output(struct inpcb *inp, struct mbuf *m, stru 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)) { @@ -1126,7 +1250,7 @@ udp_output(struct inpcb *inp, struct mbuf *m, stru 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); @@ -1141,7 +1265,7 @@ udp_output(struct inpcb *inp, struct mbuf *m, stru 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. @@ -1188,15 +1312,35 @@ udp_output(struct inpcb *inp, struct mbuf *m, stru * Fill in mbuf with extended UDP header and addresses and length put * into network format. */ + pr = IPPROTO_UDP; ui = mtod(m, struct udpiphdr *); bzero(ui->ui_x1, sizeof(ui->ui_x1)); /* XXX still needed? */ - ui->ui_pr = IPPROTO_UDP; 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 (inp->inp_socket->so_proto->pr_protocol == IPPROTO_UDPLITE) { + struct udpcb *up; + uint16_t plen; + + pr = IPPROTO_UDPLITE; + 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 UDPLite, checksum coverage length of zero means + * the entire UDPLite packet is covered by the checksum. + */ + cscov_partial = (cscov == 0) ? 0 : 1; + } + ui->ui_pr = pr; + /* * Set the Don't Fragment bit in the IP header. */ @@ -1222,24 +1366,29 @@ udp_output(struct inpcb *inp, struct mbuf *m, stru /* * 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); error = ip_output(m, inp->inp_options, NULL, ipflags, inp->inp_moptions, inp); if (unlock_udbinfo == UH_WLOCKED) @@ -1250,10 +1399,10 @@ udp_output(struct inpcb *inp, struct mbuf *m, stru 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); @@ -1405,10 +1554,10 @@ udp_abort(struct socket *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(inp->inp_pcbinfo); in_pcbdisconnect(inp); inp->inp_laddr.s_addr = INADDR_ANY; - INP_HASH_WUNLOCK(&V_udbinfo); + INP_HASH_WUNLOCK(inp->inp_pcbinfo); soisdisconnected(so); } INP_WUNLOCK(inp); @@ -1418,17 +1567,23 @@ static int udp_attach(struct socket *so, int proto, struct thread *td) { struct inpcb *inp; + struct inpcbinfo *pcbinfo; int error; + if (so->so_proto->pr_protocol == IPPROTO_UDP) + pcbinfo = &V_udbinfo; + else + pcbinfo = &V_ulitecbinfo; + 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); } @@ -1440,13 +1595,14 @@ udp_attach(struct socket *so, int proto, struct th 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 */ @@ -1481,9 +1637,9 @@ udp_bind(struct socket *so, struct sockaddr *nam, inp = sotoinpcb(so); KASSERT(inp != NULL, ("udp_bind: inp == NULL")); INP_WLOCK(inp); - INP_HASH_WLOCK(&V_udbinfo); + INP_HASH_WLOCK(inp->inp_pcbinfo); error = in_pcbbind(inp, nam, td->td_ucred); - INP_HASH_WUNLOCK(&V_udbinfo); + INP_HASH_WUNLOCK(inp->inp_pcbinfo); INP_WUNLOCK(inp); return (error); } @@ -1497,10 +1653,10 @@ udp_close(struct socket *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(inp->inp_pcbinfo); in_pcbdisconnect(inp); inp->inp_laddr.s_addr = INADDR_ANY; - INP_HASH_WUNLOCK(&V_udbinfo); + INP_HASH_WUNLOCK(inp->inp_pcbinfo); soisdisconnected(so); } INP_WUNLOCK(inp); @@ -1526,9 +1682,9 @@ udp_connect(struct socket *so, struct sockaddr *na INP_WUNLOCK(inp); return (error); } - INP_HASH_WLOCK(&V_udbinfo); + INP_HASH_WLOCK(inp->inp_pcbinfo); error = in_pcbconnect(inp, nam, td->td_ucred); - INP_HASH_WUNLOCK(&V_udbinfo); + INP_HASH_WUNLOCK(inp->inp_pcbinfo); if (error == 0) soisconnected(so); INP_WUNLOCK(inp); @@ -1540,20 +1696,23 @@ udp_detach(struct socket *so) { struct inpcb *inp; struct udpcb *up; + int isudp; + isudp = (so->so_proto->pr_protocol == IPPROTO_UDP) ? 1 : 0; + 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(inp->inp_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); - udp_discardcb(up); + INP_INFO_WUNLOCK(inp->inp_pcbinfo); + udp_discardcb(up, isudp); } static int @@ -1568,10 +1727,10 @@ udp_disconnect(struct socket *so) INP_WUNLOCK(inp); return (ENOTCONN); } - INP_HASH_WLOCK(&V_udbinfo); + INP_HASH_WLOCK(inp->inp_pcbinfo); in_pcbdisconnect(inp); inp->inp_laddr.s_addr = INADDR_ANY; - INP_HASH_WUNLOCK(&V_udbinfo); + INP_HASH_WUNLOCK(inp->inp_pcbinfo); SOCK_LOCK(so); so->so_state &= ~SS_ISCONNECTED; /* XXX */ SOCK_UNLOCK(so); @@ -1622,4 +1781,5 @@ struct pr_usrreqs udp_usrreqs = { .pru_sosetlabel = in_pcbsosetlabel, .pru_close = udp_close, }; + #endif /* INET */ Index: sys/netinet/udp_var.h =================================================================== --- sys/netinet/udp_var.h (revision 248811) +++ sys/netinet/udp_var.h (working copy) @@ -62,10 +62,12 @@ typedef void(*udp_tun_func_t)(struct mbuf *, int o 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) -#define sotoudpcb(so) (intoudpcb(sotoinpcb(so))) +#define intoudpcb(ip) ((struct udpcb *)(ip)->inp_ppcb) +#define sotoudpcb(so) (intoudpcb(sotoinpcb(so))) /* IPsec: ESP in UDP tunneling: */ #define UF_ESPINUDP_NON_IKE 0x00000001 /* w/ non-IKE marker .. */ @@ -93,6 +95,33 @@ struct udpstat { u_long udps_filtermcast; /* blocked by multicast filter */ }; +/* + * Protocol control blocks management functions. + */ +#define UDP_PCBINFO_INIT(pcbinfo, pcbname, listhead, inpcbzone_name,\ + inpcbzone_init) { \ + in_pcbinfo_init(pcbinfo, pcbname, listhead, UDBHASHSIZE, UDBHASHSIZE,\ + inpcbzone_name, inpcbzone_init, NULL, UMA_ZONE_NOFREE,\ + IPI_HASHFIELDS_2TUPLE); \ +} + +#define UDP_PCBINFO_DESTROY(pcbinfo) { \ + in_pcbinfo_destroy(pcbinfo); \ +} + +/* + * Zone allocation functions. + */ +#define UDP_ZONE_INIT(zone, zone_name) { \ + zone = uma_zcreate(zone_name, sizeof(struct udpcb), NULL, NULL, NULL,\ + NULL, UMA_ALIGN_PTR, UMA_ZONE_NOFREE); \ + uma_zone_set_max(zone, maxsockets); \ + uma_zone_set_warning(zone, "kern.ipc.maxsockets limit reached"); \ +} + +#define UDP_ZONE_DESTROY(zone) uma_zdestroy(zone) + + #ifdef _KERNEL /* * In-kernel consumers can use these accessor macros directly to update @@ -134,8 +163,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; @@ -147,20 +180,41 @@ VNET_DECLARE(int, udp_blackhole); #define V_udp_blackhole VNET(udp_blackhole) extern int udp_log_in_vain; -int udp_newudpcb(struct inpcb *); -void udp_discardcb(struct udpcb *); +int udp_newudpcb(struct inpcb *); +void udp_discardcb(struct udpcb *, int); -void udp_ctlinput(int, struct sockaddr *, void *); -int udp_ctloutput(struct socket *, struct sockopt *); -void udp_init(void); +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 udp_destroy(void); +void udplite_destroy(void); #endif -void udp_input(struct mbuf *, int); +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_shutdown(struct socket *so); int udp_set_kernel_tunneling(struct socket *so, udp_tun_func_t f); -#endif -#endif +#define udp_common_init(pcbinfo, pcbname, listhead, inpcbzone_name,\ + inpcbzone_init, zone, zone_name) { \ + UDP_PCBINFO_INIT(pcbinfo, pcbname, listhead, inpcbzone_name,\ + inpcbzone_init); \ + UDP_ZONE_INIT(zone, zone_name); \ +} + +#define udp_common_destroy(pcbinfo, zone) { \ + UDP_PCBINFO_DESTROY(pcbinfo); \ + UDP_ZONE_DESTROY(zone); \ +} + +#define udp_common_zone_change(_pcbinfo, _zone) { \ + uma_zone_set_max((_pcbinfo).ipi_zone, maxsockets); \ + uma_zone_set_max((_zone), maxsockets); \ +} +#endif /* _KERNEL */ + +#endif /* _NETINET_UDP_VAR_H_ */ Index: sys/netinet/udplite.h =================================================================== --- sys/netinet/udplite.h (revision 0) +++ sys/netinet/udplite.h (working copy) @@ -0,0 +1,38 @@ +/*- + * Copyright (c) 2013, 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_ */ Index: sys/netinet6/in6_ifattach.c =================================================================== --- sys/netinet6/in6_ifattach.c (revision 248811) +++ sys/netinet6/in6_ifattach.c (working copy) @@ -837,6 +837,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); Index: sys/netinet6/in6_proto.c =================================================================== --- sys/netinet6/in6_proto.c (revision 248811) +++ sys/netinet6/in6_proto.c (working copy) @@ -218,6 +218,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, @@ -453,6 +466,7 @@ SYSCTL_NODE(_net_inet6, IPPROTO_TCP, tcp6, CTLFLAG #ifdef SCTP SYSCTL_NODE(_net_inet6, IPPROTO_SCTP, sctp6, CTLFLAG_RW, 0, "SCTP6"); #endif +SYSCTL_NODE(_net_inet6, IPPROTO_UDPLITE,udplite6,CTLFLAG_RW, 0, "UDPLITE6"); #ifdef IPSEC SYSCTL_NODE(_net_inet6, IPPROTO_ESP, ipsec6, CTLFLAG_RW, 0, "IPSEC6"); #endif /* IPSEC */ Index: sys/netinet6/udp6_usrreq.c =================================================================== --- sys/netinet6/udp6_usrreq.c (revision 248811) +++ sys/netinet6/udp6_usrreq.c (working copy) @@ -106,6 +106,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> @@ -178,11 +179,14 @@ udp6_input(struct mbuf **mp, int *offp, int proto) struct ip6_hdr *ip6; struct udphdr *uh; struct inpcb *inp; - struct udpcb *up; + struct inpcbinfo *pcbinfo; + struct udpcb *up = NULL; int off = *offp; + int cscov_partial; int plen, ulen; struct sockaddr_in6 fromsa; struct m_tag *fwd_tag; + uint8_t nxt; uint16_t uh_sum; ifp = m->m_pkthdr.rcvif; @@ -215,9 +219,19 @@ udp6_input(struct mbuf **mp, int *offp, int proto) 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; + if (ulen > plen || ulen < sizeof(struct udphdr)) { + UDPSTAT_INC(udps_badlen); + goto badunlocked; + } } /* @@ -228,15 +242,16 @@ udp6_input(struct mbuf **mp, int *offp, int proto) 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); @@ -249,11 +264,13 @@ udp6_input(struct mbuf **mp, int *offp, int proto) init_sin6(&fromsa, m); fromsa.sin6_port = uh->uh_sport; + pcbinfo = (nxt == IPPROTO_UDP) ? &V_udbinfo : &V_ulitecbinfo; if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { struct inpcb *last; + struct inpcbhead *pcblist; struct ip6_moptions *imo; - INP_INFO_RLOCK(&V_udbinfo); + INP_INFO_RLOCK(pcbinfo); /* * In the event that laddr should be set to the link-local * address (this happens in RIPng), the multicast address @@ -269,8 +286,9 @@ udp6_input(struct mbuf **mp, int *offp, int proto) * here. We need udphdr for IPsec processing so we do that * later. */ + pcblist = (nxt == IPPROTO_UDP) ? &V_udb : &V_ulitecb; last = NULL; - LIST_FOREACH(inp, &V_udb, inp_list) { + LIST_FOREACH(inp, pcblist, inp_list) { if ((inp->inp_vflag & INP_IPV6) == 0) continue; if (inp->inp_lport != uh->uh_dport) @@ -375,7 +393,7 @@ udp6_input(struct mbuf **mp, int *offp, int proto) goto badheadlocked; } INP_RLOCK(last); - INP_INFO_RUNLOCK(&V_udbinfo); + INP_INFO_RUNLOCK(pcbinfo); up = intoudpcb(last); if (up->u_tun_func == NULL) { udp6_append(last, m, off, &fromsa); @@ -405,8 +423,8 @@ udp6_input(struct mbuf **mp, int *offp, int proto) * Transparently forwarded. Pretend to be the destination. * Already got one like this? */ - inp = in6_pcblookup_mbuf(&V_udbinfo, - &ip6->ip6_src, uh->uh_sport, &ip6->ip6_dst, uh->uh_dport, + inp = in6_pcblookup_mbuf(pcbinfo, &ip6->ip6_src, + uh->uh_sport, &ip6->ip6_dst, uh->uh_dport, INPLOOKUP_RLOCKPCB, m->m_pkthdr.rcvif, m); if (!inp) { /* @@ -414,7 +432,7 @@ udp6_input(struct mbuf **mp, int *offp, int proto) * Because we've rewritten the destination address, * any hardware-generated hash is ignored. */ - inp = in6_pcblookup(&V_udbinfo, &ip6->ip6_src, + inp = in6_pcblookup(pcbinfo, &ip6->ip6_src, uh->uh_sport, &next_hop6->sin6_addr, next_hop6->sin6_port ? htons(next_hop6->sin6_port) : uh->uh_dport, INPLOOKUP_WILDCARD | @@ -424,7 +442,7 @@ udp6_input(struct mbuf **mp, int *offp, int proto) m_tag_delete(m, fwd_tag); m->m_flags &= ~M_IP6_NEXTHOP; } else - inp = in6_pcblookup_mbuf(&V_udbinfo, &ip6->ip6_src, + inp = in6_pcblookup_mbuf(pcbinfo, &ip6->ip6_src, uh->uh_sport, &ip6->ip6_dst, uh->uh_dport, INPLOOKUP_WILDCARD | INPLOOKUP_RLOCKPCB, m->m_pkthdr.rcvif, m); @@ -454,7 +472,14 @@ udp6_input(struct mbuf **mp, int *offp, int proto) return (IPPROTO_DONE); } INP_RLOCK_ASSERT(inp); - up = intoudpcb(inp); + if (cscov_partial) { + up = intoudpcb(inp); + if (up->u_rxcslen > ulen) { + INP_RUNLOCK(inp); + m_freem(m); + return (IPPROTO_DONE); + } + } if (up->u_tun_func == NULL) { udp6_append(inp, m, off, &fromsa); } else { @@ -468,15 +493,16 @@ udp6_input(struct mbuf **mp, int *offp, int proto) return (IPPROTO_DONE); badheadlocked: - INP_INFO_RUNLOCK(&V_udbinfo); + INP_INFO_RUNLOCK(pcbinfo); badunlocked: if (m) m_freem(m); return (IPPROTO_DONE); } -void -udp6_ctlinput(int cmd, struct sockaddr *sa, void *d) +static void +udp6_common_ctlinput(int cmd, struct sockaddr *sa, void *d, + struct inpcbinfo *pcbinfo) { struct udphdr uh; struct ip6_hdr *ip6; @@ -532,14 +558,26 @@ badunlocked: bzero(&uh, sizeof(uh)); m_copydata(m, off, sizeof(*uhp), (caddr_t)&uh); - (void) in6_pcbnotify(&V_udbinfo, sa, uh.uh_dport, + (void) in6_pcbnotify(pcbinfo, sa, uh.uh_dport, (struct sockaddr *)ip6cp->ip6c_src, uh.uh_sport, cmd, cmdarg, notify); } else - (void) in6_pcbnotify(&V_udbinfo, sa, 0, + (void) in6_pcbnotify(pcbinfo, sa, 0, (const struct sockaddr *)sa6_src, 0, cmd, cmdarg, notify); } +void +udp6_ctlinput(int cmd, struct sockaddr *sa, void *d) +{ + return (udp6_common_ctlinput(cmd, sa, d, &V_udbinfo)); +} + +void +udplite6_ctlinput(int cmd, struct sockaddr *sa, void *d) +{ + return (udp6_common_ctlinput(cmd, sa, d, &V_ulitecbinfo)); +} + static int udp6_getcred(SYSCTL_HANDLER_ARGS) { @@ -597,9 +635,12 @@ udp6_output(struct inpcb *inp, struct mbuf *m, str struct in6_addr *laddr, *faddr, in6a; struct sockaddr_in6 *sin6 = NULL; struct ifnet *oifp = NULL; + int cscov_partial = 0; int scope_ambiguous = 0; u_short fport; int error = 0; + uint8_t nxt; + uint16_t cscov = 0; struct ip6_pktopts *optp, opt; int af = AF_INET6, hlen = sizeof(struct ip6_hdr); int flags; @@ -756,13 +797,29 @@ udp6_output(struct inpcb *inp, struct mbuf *m, str /* * Stuff checksum and output datagram. */ + nxt = IPPROTO_UDP; udp6 = (struct udphdr *)(mtod(m, caddr_t) + hlen); udp6->uh_sport = inp->inp_lport; /* lport is always set in the PCB */ udp6->uh_dport = fport; - if (plen <= 0xffff) + if (inp->inp_socket->so_proto->pr_protocol == IPPROTO_UDPLITE) { + struct udpcb *up; + + nxt = IPPROTO_UDPLITE; + up = intoudpcb(inp); + cscov = up->u_txcslen; + if (cscov >= plen) + cscov = 0; + udp6->uh_ulen = htons(cscov); + + /* For UDPLite, checksum coverage length of zero means + * the entire UDPLite packet is covered by the checksum. + */ + cscov_partial = (cscov == 0) ? 0 : 1; + } else if (plen <= 0xffff) udp6->uh_ulen = htons((u_short)plen); else udp6->uh_ulen = 0; + udp6->uh_sum = 0; switch (af) { @@ -771,17 +828,22 @@ udp6_output(struct inpcb *inp, struct mbuf *m, str ip6->ip6_flow = inp->inp_flow & IPV6_FLOWINFO_MASK; ip6->ip6_vfc &= ~IPV6_VERSION_MASK; ip6->ip6_vfc |= IPV6_VERSION; -#if 0 /* ip6_plen will be filled in ip6_output. */ - ip6->ip6_plen = htons((u_short)plen); -#endif - ip6->ip6_nxt = IPPROTO_UDP; + if (nxt == IPPROTO_UDPLITE) + ip6->ip6_plen = htons((u_short)plen); + ip6->ip6_nxt = nxt; ip6->ip6_hlim = in6_selecthlim(inp, NULL); ip6->ip6_src = *laddr; ip6->ip6_dst = *faddr; - udp6->uh_sum = in6_cksum_pseudo(ip6, plen, IPPROTO_UDP, 0); - m->m_pkthdr.csum_flags = CSUM_UDP_IPV6; - m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum); + if (cscov_partial) { + if ((udp6->uh_sum = in6_cksum(m, 0, + sizeof(struct ip6_hdr), cscov)) == 0) + udp6->uh_sum = 0xffff; + } else { + udp6->uh_sum = in6_cksum_pseudo(ip6, plen, nxt, 0); + m->m_pkthdr.csum_flags = CSUM_UDP_IPV6; + m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum); + } flags = 0; @@ -826,10 +888,10 @@ udp6_abort(struct socket *so) INP_WLOCK(inp); if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) { - INP_HASH_WLOCK(&V_udbinfo); + INP_HASH_WLOCK(inp->inp_pcbinfo); in6_pcbdisconnect(inp); inp->in6p_laddr = in6addr_any; - INP_HASH_WUNLOCK(&V_udbinfo); + INP_HASH_WUNLOCK(inp->inp_pcbinfo); soisdisconnected(so); } INP_WUNLOCK(inp); @@ -839,8 +901,13 @@ static int udp6_attach(struct socket *so, int proto, struct thread *td) { struct inpcb *inp; + struct inpcbinfo *pcbinfo; int error; + if (so->so_proto->pr_protocol == IPPROTO_UDP) + pcbinfo = &V_udbinfo; + else + pcbinfo = &V_ulitecbinfo; inp = sotoinpcb(so); KASSERT(inp == NULL, ("udp6_attach: inp != NULL")); @@ -849,10 +916,10 @@ udp6_attach(struct socket *so, int proto, struct t 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); } inp = (struct inpcb *)so->so_pcb; @@ -873,11 +940,11 @@ udp6_attach(struct socket *so, int proto, struct t 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); } @@ -891,7 +958,7 @@ udp6_bind(struct socket *so, struct sockaddr *nam, KASSERT(inp != NULL, ("udp6_bind: inp == NULL")); INP_WLOCK(inp); - INP_HASH_WLOCK(&V_udbinfo); + INP_HASH_WLOCK(inp->inp_pcbinfo); inp->inp_vflag &= ~INP_IPV4; inp->inp_vflag |= INP_IPV6; if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) { @@ -919,7 +986,7 @@ udp6_bind(struct socket *so, struct sockaddr *nam, #ifdef INET out: #endif - INP_HASH_WUNLOCK(&V_udbinfo); + INP_HASH_WUNLOCK(inp->inp_pcbinfo); INP_WUNLOCK(inp); return (error); } @@ -943,10 +1010,10 @@ udp6_close(struct socket *so) #endif INP_WLOCK(inp); if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) { - INP_HASH_WLOCK(&V_udbinfo); + INP_HASH_WLOCK(inp->inp_pcbinfo); in6_pcbdisconnect(inp); inp->in6p_laddr = in6addr_any; - INP_HASH_WUNLOCK(&V_udbinfo); + INP_HASH_WUNLOCK(inp->inp_pcbinfo); soisdisconnected(so); } INP_WUNLOCK(inp); @@ -985,10 +1052,10 @@ udp6_connect(struct socket *so, struct sockaddr *n error = prison_remote_ip4(td->td_ucred, &sin.sin_addr); if (error != 0) goto out; - INP_HASH_WLOCK(&V_udbinfo); + INP_HASH_WLOCK(inp->inp_pcbinfo); error = in_pcbconnect(inp, (struct sockaddr *)&sin, td->td_ucred); - INP_HASH_WUNLOCK(&V_udbinfo); + INP_HASH_WUNLOCK(inp->inp_pcbinfo); if (error == 0) soisconnected(so); goto out; @@ -1003,9 +1070,9 @@ udp6_connect(struct socket *so, struct sockaddr *n error = prison_remote_ip6(td->td_ucred, &sin6->sin6_addr); if (error != 0) goto out; - INP_HASH_WLOCK(&V_udbinfo); + INP_HASH_WLOCK(inp->inp_pcbinfo); error = in6_pcbconnect(inp, nam, td->td_ucred); - INP_HASH_WUNLOCK(&V_udbinfo); + INP_HASH_WUNLOCK(inp->inp_pcbinfo); if (error == 0) soisconnected(so); out: @@ -1018,18 +1085,21 @@ udp6_detach(struct socket *so) { struct inpcb *inp; struct udpcb *up; + int isudp; + isudp = (so->so_proto->pr_protocol == IPPROTO_UDP) ? 1 : 0; + inp = sotoinpcb(so); KASSERT(inp != NULL, ("udp6_detach: inp == NULL")); - INP_INFO_WLOCK(&V_udbinfo); + INP_INFO_WLOCK(inp->inp_pcbinfo); INP_WLOCK(inp); up = intoudpcb(inp); KASSERT(up != NULL, ("%s: up == NULL", __func__)); in_pcbdetach(inp); in_pcbfree(inp); - INP_INFO_WUNLOCK(&V_udbinfo); - udp_discardcb(up); + INP_INFO_WUNLOCK(inp->inp_pcbinfo); + udp_discardcb(up, isudp); } static int @@ -1058,10 +1128,10 @@ udp6_disconnect(struct socket *so) goto out; } - INP_HASH_WLOCK(&V_udbinfo); + INP_HASH_WLOCK(inp->inp_pcbinfo); in6_pcbdisconnect(inp); inp->in6p_laddr = in6addr_any; - INP_HASH_WUNLOCK(&V_udbinfo); + INP_HASH_WUNLOCK(inp->inp_pcbinfo); SOCK_LOCK(so); so->so_state &= ~SS_ISCONNECTED; /* XXX */ SOCK_UNLOCK(so); @@ -1128,9 +1198,9 @@ udp6_send(struct socket *so, int flags, struct mbu #ifdef MAC mac_inpcb_create_mbuf(inp, m); #endif - INP_HASH_WLOCK(&V_udbinfo); + INP_HASH_WLOCK(inp->inp_pcbinfo); error = udp6_output(inp, m, addr, control, td); - INP_HASH_WUNLOCK(&V_udbinfo); + INP_HASH_WUNLOCK(inp->inp_pcbinfo); #ifdef INET #endif INP_WUNLOCK(inp); Index: sys/netinet6/udp6_var.h =================================================================== --- sys/netinet6/udp6_var.h (revision 248811) +++ sys/netinet6/udp6_var.h (working copy) @@ -69,6 +69,7 @@ SYSCTL_DECL(_net_inet6_udp6); extern struct pr_usrreqs udp6_usrreqs; void udp6_ctlinput(int, struct sockaddr *, void *); +void udplite6_ctlinput(int, struct sockaddr *, void *); int udp6_input(struct mbuf **, int *, int); #endif [-- Attachment #4 --] Index: lib/libc/net/getaddrinfo.c =================================================================== --- lib/libc/net/getaddrinfo.c (revision 248458) +++ lib/libc/net/getaddrinfo.c (working copy) @@ -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 }, }; @@ -1476,6 +1478,9 @@ get_port(struct addrinfo *ai, const char *servname case IPPROTO_SCTP: proto = "sctp"; break; + case IPPROTO_UDPLITE: + proto = "udplite"; + break; default: proto = NULL; break; Index: sys/netinet/in.c =================================================================== --- sys/netinet/in.c (revision 248458) +++ sys/netinet/in.c (working copy) @@ -1205,6 +1205,7 @@ in_ifdetach(struct ifnet *ifp) in_pcbpurgeif0(&V_ripcbinfo, ifp); in_pcbpurgeif0(&V_udbinfo, ifp); + in_pcbpurgeif0(&V_ulitecbinfo, ifp); in_purgemaddrs(ifp); } Index: sys/netinet/in.h =================================================================== --- sys/netinet/in.h (revision 248458) +++ sys/netinet/in.h (working copy) @@ -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 /* UDPLite */ /* 101-254: Partly Unassigned */ #define IPPROTO_PIM 103 /* Protocol Independent Mcast */ #define IPPROTO_CARP 112 /* CARP */ Index: sys/netinet/in_pcb.c =================================================================== --- sys/netinet/in_pcb.c (revision 248458) +++ sys/netinet/in_pcb.c (working copy) @@ -390,13 +390,14 @@ in_pcb_lport(struct inpcb *inp, struct in_addr *la 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; @@ -406,8 +407,8 @@ in_pcb_lport(struct inpcb *inp, struct in_addr *la */ 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 Index: sys/netinet/in_proto.c =================================================================== --- sys/netinet/in_proto.c (revision 248458) +++ sys/netinet/in_proto.c (working copy) @@ -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, @@ -370,6 +384,7 @@ SYSCTL_NODE(_net_inet, IPPROTO_TCP, tcp, CTLFLAG_R #ifdef SCTP SYSCTL_NODE(_net_inet, IPPROTO_SCTP, sctp, CTLFLAG_RW, 0, "SCTP"); #endif +SYSCTL_NODE(_net_inet, IPPROTO_UDPLITE, udplite,CTLFLAG_RW, 0, "UDPLite"); SYSCTL_NODE(_net_inet, IPPROTO_IGMP, igmp, CTLFLAG_RW, 0, "IGMP"); #ifdef IPSEC /* XXX no protocol # to use, pick something "reserved" */ Index: sys/netinet/udp_usrreq.c =================================================================== --- sys/netinet/udp_usrreq.c (revision 248458) +++ sys/netinet/udp_usrreq.c (working copy) @@ -84,6 +84,7 @@ __FBSDID("$FreeBSD$"); #endif #include <netinet/udp.h> #include <netinet/udp_var.h> +#include <netinet/udplite.h> #ifdef IPSEC #include <netipsec/ipsec.h> @@ -131,13 +132,18 @@ u_long udp_recvspace = 40 * (1024 + #endif ); + SYSCTL_ULONG(_net_inet_udp, UDPCTL_RECVSPACE, recvspace, CTLFLAG_RW, &udp_recvspace, 0, "Maximum space for incoming UDP datagrams"); 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) +static VNET_DEFINE(uma_zone_t, udplitecb_zone); +#define V_udpcb_zone VNET(udpcb_zone) +#define V_udplitecb_zone VNET(udplitecb_zone) #ifndef UDBHASHSIZE #define UDBHASHSIZE 128 @@ -171,6 +177,14 @@ udp_zone_change(void *tag) uma_zone_set_max(V_udpcb_zone, maxsockets); } +static void +udplite_zone_change(void *tag) +{ + + uma_zone_set_max(V_ulitecbinfo.ipi_zone, maxsockets); + uma_zone_set_max(V_udplitecb_zone, maxsockets); +} + static int udp_inpcb_init(void *mem, int size, int flags) { @@ -181,6 +195,16 @@ udp_inpcb_init(void *mem, int size, int flags) 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) { @@ -196,6 +220,22 @@ 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); + V_udplitecb_zone = uma_zcreate("udplitecb", sizeof(struct udpcb), + NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_NOFREE); + uma_zone_set_max(V_udplitecb_zone, maxsockets); + uma_zone_set_warning(V_udplitecb_zone, + "kern.ipc.maxsockets limit reached"); + EVENTHANDLER_REGISTER(maxsockets_change, udplite_zone_change, NULL, + EVENTHANDLER_PRI_ANY); +} + /* * Kernel module interface for updating udpstat. The argument is an index * into udpstat treated as an array of u_long. While this encodes the @@ -215,7 +255,10 @@ udp_newudpcb(struct inpcb *inp) { struct udpcb *up; - up = uma_zalloc(V_udpcb_zone, M_NOWAIT | M_ZERO); + if (inp->inp_socket->so_proto->pr_protocol == IPPROTO_UDP) + up = uma_zalloc(V_udpcb_zone, M_NOWAIT | M_ZERO); + else + up = uma_zalloc(V_udplitecb_zone, M_NOWAIT | M_ZERO); if (up == NULL) return (ENOBUFS); inp->inp_ppcb = up; @@ -223,10 +266,12 @@ udp_newudpcb(struct inpcb *inp) } void -udp_discardcb(struct udpcb *up) +udp_discardcb(struct udpcb *up, int isudp) { - - uma_zfree(V_udpcb_zone, up); + if (isudp) + uma_zfree(V_udpcb_zone, up); + else + uma_zfree(V_udplitecb_zone, up); } #ifdef VIMAGE @@ -237,6 +282,14 @@ udp_destroy(void) in_pcbinfo_destroy(&V_udbinfo); uma_zdestroy(V_udpcb_zone); } + +void +udplite_destroy(void) +{ + + in_pcbinfo_destroy(&V_ulitecbinfo); + uma_zdestroy(V_udplitecb_zone); +} #endif #ifdef INET @@ -339,10 +392,13 @@ udp_input(struct mbuf *m, int off) struct udphdr *uh; struct ifnet *ifp; struct inpcb *inp; + struct inpcbinfo *pcbinfo; uint16_t len, ip_len; struct ip save_ip; struct sockaddr_in udp_in; struct m_tag *fwd_tag; + int cscov_partial; + uint8_t pr; ifp = m->m_pkthdr.rcvif; UDPSTAT_INC(udps_ipackets); @@ -369,7 +425,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. */ @@ -392,12 +449,19 @@ 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); } /* @@ -415,20 +479,24 @@ 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; + if (pr == IPPROTO_UDP) + ((struct ipovly *)ip)->ih_len = uh->uh_ulen; + else + ((struct ipovly *)ip)->ih_len = htons(ip_len); uh_sum = in_cksum(m, len + sizeof (struct ip)); bcopy(b, ((struct ipovly *)ip)->ih_x1, 9); } @@ -440,14 +508,18 @@ udp_input(struct mbuf *m, int off) } else UDPSTAT_INC(udps_nosum); + pcbinfo = (pr == IPPROTO_UDP) ? &V_udbinfo : &V_ulitecbinfo; + 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 = (pr == IPPROTO_UDP) ? &V_udb : &V_ulitecb; 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 @@ -533,12 +605,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; } @@ -559,7 +631,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) { /* @@ -567,7 +639,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 | @@ -577,7 +649,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) { @@ -613,6 +685,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_append(inp, ip, m, iphlen, &udp_in); INP_RUNLOCK(inp); return; @@ -644,9 +726,9 @@ udp_notify(struct inpcb *inp, int errno) return (inp); } -#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; @@ -675,7 +757,7 @@ udp_notify(struct inpcb *inp, int errno) 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); @@ -685,9 +767,23 @@ udp_notify(struct inpcb *inp, int errno) INP_RUNLOCK(inp); } } else - in_pcbnotifyall(&V_udbinfo, faddr, inetctlerrmap[cmd], + in_pcbnotifyall(pcbinfo, faddr, inetctlerrmap[cmd], udp_notify); + return; } + +#ifdef INET +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 @@ -843,16 +939,16 @@ SYSCTL_PROC(_net_inet_udp, OID_AUTO, getcred, int udp_ctloutput(struct socket *so, struct sockopt *sopt) { - int error = 0, optval; + int isudplite, error, optval; struct inpcb *inp; -#ifdef IPSEC_NAT_T struct udpcb *up; -#endif + 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); @@ -910,7 +1006,33 @@ udp_ctloutput(struct socket *so, struct sockopt *s } INP_WUNLOCK(inp); break; + case UDPLITE_SEND_CSCOV: + case UDPLITE_RECV_CSCOV: + if (!isudplite) + goto bad_setoptname; + INP_WUNLOCK(inp); + error = sooptcopyin(sopt, &optval, sizeof(optval), + sizeof(optval)); + if (error) + 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) { + error = EINVAL; + INP_WUNLOCK(inp); + break; + } + if (sopt->sopt_name == UDPLITE_SEND_CSCOV) + up->u_txcslen = optval; + else + up->u_rxcslen = optval; + INP_WUNLOCK(inp); + break; default: +bad_setoptname: INP_WUNLOCK(inp); error = ENOPROTOOPT; break; @@ -927,7 +1049,21 @@ udp_ctloutput(struct socket *so, struct sockopt *s error = sooptcopyout(sopt, &optval, sizeof optval); break; #endif + case UDPLITE_SEND_CSCOV: + case UDPLITE_RECV_CSCOV: + if (!isudplite) + goto bad_getoptname; + 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: +bad_getoptname: INP_WUNLOCK(inp); error = ENOPROTOOPT; break; @@ -949,12 +1085,16 @@ udp_output(struct inpcb *inp, struct mbuf *m, stru 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 @@ -1049,12 +1189,13 @@ udp_output(struct inpcb *inp, struct mbuf *m, stru * * XXXRW: Check that hash locking update here is correct. */ + pcbinfo = inp->inp_pcbinfo; 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) || @@ -1062,7 +1203,7 @@ udp_output(struct inpcb *inp, struct mbuf *m, stru (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; @@ -1075,7 +1216,7 @@ udp_output(struct inpcb *inp, struct mbuf *m, stru 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)) { @@ -1126,7 +1267,7 @@ udp_output(struct inpcb *inp, struct mbuf *m, stru 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); @@ -1141,7 +1282,7 @@ udp_output(struct inpcb *inp, struct mbuf *m, stru 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. @@ -1188,15 +1329,35 @@ udp_output(struct inpcb *inp, struct mbuf *m, stru * Fill in mbuf with extended UDP header and addresses and length put * into network format. */ + pr = IPPROTO_UDP; ui = mtod(m, struct udpiphdr *); bzero(ui->ui_x1, sizeof(ui->ui_x1)); /* XXX still needed? */ - ui->ui_pr = IPPROTO_UDP; 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 (inp->inp_socket->so_proto->pr_protocol == IPPROTO_UDPLITE) { + struct udpcb *up; + uint16_t plen; + + pr = IPPROTO_UDPLITE; + 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 UDPLite, checksum coverage length of zero means + * the entire UDPLite packet is covered by the checksum. + */ + cscov_partial = (cscov == 0) ? 0 : 1; + } + ui->ui_pr = pr; + /* * Set the Don't Fragment bit in the IP header. */ @@ -1222,24 +1383,29 @@ udp_output(struct inpcb *inp, struct mbuf *m, stru /* * 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); error = ip_output(m, inp->inp_options, NULL, ipflags, inp->inp_moptions, inp); if (unlock_udbinfo == UH_WLOCKED) @@ -1250,10 +1416,10 @@ udp_output(struct inpcb *inp, struct mbuf *m, stru 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); @@ -1405,10 +1571,10 @@ udp_abort(struct socket *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(inp->inp_pcbinfo); in_pcbdisconnect(inp); inp->inp_laddr.s_addr = INADDR_ANY; - INP_HASH_WUNLOCK(&V_udbinfo); + INP_HASH_WUNLOCK(inp->inp_pcbinfo); soisdisconnected(so); } INP_WUNLOCK(inp); @@ -1418,17 +1584,23 @@ static int udp_attach(struct socket *so, int proto, struct thread *td) { struct inpcb *inp; + struct inpcbinfo *pcbinfo; int error; + if (so->so_proto->pr_protocol == IPPROTO_UDP) + pcbinfo = &V_udbinfo; + else + pcbinfo = &V_ulitecbinfo; + 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); } @@ -1440,13 +1612,14 @@ udp_attach(struct socket *so, int proto, struct th 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 */ @@ -1481,9 +1654,9 @@ udp_bind(struct socket *so, struct sockaddr *nam, inp = sotoinpcb(so); KASSERT(inp != NULL, ("udp_bind: inp == NULL")); INP_WLOCK(inp); - INP_HASH_WLOCK(&V_udbinfo); + INP_HASH_WLOCK(inp->inp_pcbinfo); error = in_pcbbind(inp, nam, td->td_ucred); - INP_HASH_WUNLOCK(&V_udbinfo); + INP_HASH_WUNLOCK(inp->inp_pcbinfo); INP_WUNLOCK(inp); return (error); } @@ -1497,10 +1670,10 @@ udp_close(struct socket *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(inp->inp_pcbinfo); in_pcbdisconnect(inp); inp->inp_laddr.s_addr = INADDR_ANY; - INP_HASH_WUNLOCK(&V_udbinfo); + INP_HASH_WUNLOCK(inp->inp_pcbinfo); soisdisconnected(so); } INP_WUNLOCK(inp); @@ -1526,9 +1699,9 @@ udp_connect(struct socket *so, struct sockaddr *na INP_WUNLOCK(inp); return (error); } - INP_HASH_WLOCK(&V_udbinfo); + INP_HASH_WLOCK(inp->inp_pcbinfo); error = in_pcbconnect(inp, nam, td->td_ucred); - INP_HASH_WUNLOCK(&V_udbinfo); + INP_HASH_WUNLOCK(inp->inp_pcbinfo); if (error == 0) soisconnected(so); INP_WUNLOCK(inp); @@ -1540,20 +1713,23 @@ udp_detach(struct socket *so) { struct inpcb *inp; struct udpcb *up; + int isudp; + isudp = (so->so_proto->pr_protocol == IPPROTO_UDP) ? 1 : 0; + 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(inp->inp_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); - udp_discardcb(up); + INP_INFO_WUNLOCK(inp->inp_pcbinfo); + udp_discardcb(up, isudp); } static int @@ -1568,10 +1744,10 @@ udp_disconnect(struct socket *so) INP_WUNLOCK(inp); return (ENOTCONN); } - INP_HASH_WLOCK(&V_udbinfo); + INP_HASH_WLOCK(inp->inp_pcbinfo); in_pcbdisconnect(inp); inp->inp_laddr.s_addr = INADDR_ANY; - INP_HASH_WUNLOCK(&V_udbinfo); + INP_HASH_WUNLOCK(inp->inp_pcbinfo); SOCK_LOCK(so); so->so_state &= ~SS_ISCONNECTED; /* XXX */ SOCK_UNLOCK(so); @@ -1622,4 +1798,5 @@ struct pr_usrreqs udp_usrreqs = { .pru_sosetlabel = in_pcbsosetlabel, .pru_close = udp_close, }; + #endif /* INET */ Index: sys/netinet/udp_var.h =================================================================== --- sys/netinet/udp_var.h (revision 248458) +++ sys/netinet/udp_var.h (working copy) @@ -62,10 +62,12 @@ typedef void(*udp_tun_func_t)(struct mbuf *, int o 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) -#define sotoudpcb(so) (intoudpcb(sotoinpcb(so))) +#define intoudpcb(ip) ((struct udpcb *)(ip)->inp_ppcb) +#define sotoudpcb(so) (intoudpcb(sotoinpcb(so))) /* IPsec: ESP in UDP tunneling: */ #define UF_ESPINUDP_NON_IKE 0x00000001 /* w/ non-IKE marker .. */ @@ -134,8 +136,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; @@ -147,16 +153,20 @@ VNET_DECLARE(int, udp_blackhole); #define V_udp_blackhole VNET(udp_blackhole) extern int udp_log_in_vain; -int udp_newudpcb(struct inpcb *); -void udp_discardcb(struct udpcb *); +int udp_newudpcb(struct inpcb *); +void udp_discardcb(struct udpcb *, int); -void udp_ctlinput(int, struct sockaddr *, void *); -int udp_ctloutput(struct socket *, struct sockopt *); -void udp_init(void); +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 udp_destroy(void); +void udplite_destroy(void); #endif -void udp_input(struct mbuf *, int); +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); Index: sys/netinet/udplite.h =================================================================== --- sys/netinet/udplite.h (revision 0) +++ sys/netinet/udplite.h (working copy) @@ -0,0 +1,38 @@ +/*- + * Copyright (c) 2013, 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_ */ Index: sys/netinet6/in6_ifattach.c =================================================================== --- sys/netinet6/in6_ifattach.c (revision 248458) +++ sys/netinet6/in6_ifattach.c (working copy) @@ -837,6 +837,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); Index: sys/netinet6/in6_proto.c =================================================================== --- sys/netinet6/in6_proto.c (revision 248458) +++ sys/netinet6/in6_proto.c (working copy) @@ -218,6 +218,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, @@ -468,6 +481,7 @@ SYSCTL_NODE(_net_inet6, IPPROTO_TCP, tcp6, CTLFLAG #ifdef SCTP SYSCTL_NODE(_net_inet6, IPPROTO_SCTP, sctp6, CTLFLAG_RW, 0, "SCTP6"); #endif +SYSCTL_NODE(_net_inet6, IPPROTO_UDPLITE,udplite6,CTLFLAG_RW, 0, "UDPLITE6"); #ifdef IPSEC SYSCTL_NODE(_net_inet6, IPPROTO_ESP, ipsec6, CTLFLAG_RW, 0, "IPSEC6"); #endif /* IPSEC */ Index: sys/netinet6/udp6_usrreq.c =================================================================== --- sys/netinet6/udp6_usrreq.c (revision 248458) +++ sys/netinet6/udp6_usrreq.c (working copy) @@ -106,6 +106,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> @@ -178,11 +179,14 @@ udp6_input(struct mbuf **mp, int *offp, int proto) struct ip6_hdr *ip6; struct udphdr *uh; struct inpcb *inp; - struct udpcb *up; + struct inpcbinfo *pcbinfo; + struct udpcb *up = NULL; int off = *offp; + int cscov_partial; int plen, ulen; struct sockaddr_in6 fromsa; struct m_tag *fwd_tag; + uint8_t nxt; uint16_t uh_sum; ifp = m->m_pkthdr.rcvif; @@ -215,9 +219,19 @@ udp6_input(struct mbuf **mp, int *offp, int proto) 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; + if (ulen > plen || ulen < sizeof(struct udphdr)) { + UDPSTAT_INC(udps_badlen); + goto badunlocked; + } } /* @@ -228,15 +242,16 @@ udp6_input(struct mbuf **mp, int *offp, int proto) 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); @@ -249,11 +264,13 @@ udp6_input(struct mbuf **mp, int *offp, int proto) init_sin6(&fromsa, m); fromsa.sin6_port = uh->uh_sport; + pcbinfo = (nxt == IPPROTO_UDP) ? &V_udbinfo : &V_ulitecbinfo; if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { struct inpcb *last; + struct inpcbhead *pcblist; struct ip6_moptions *imo; - INP_INFO_RLOCK(&V_udbinfo); + INP_INFO_RLOCK(pcbinfo); /* * In the event that laddr should be set to the link-local * address (this happens in RIPng), the multicast address @@ -269,8 +286,9 @@ udp6_input(struct mbuf **mp, int *offp, int proto) * here. We need udphdr for IPsec processing so we do that * later. */ + pcblist = (nxt == IPPROTO_UDP) ? &V_udb : &V_ulitecb; last = NULL; - LIST_FOREACH(inp, &V_udb, inp_list) { + LIST_FOREACH(inp, pcblist, inp_list) { if ((inp->inp_vflag & INP_IPV6) == 0) continue; if (inp->inp_lport != uh->uh_dport) @@ -375,7 +393,7 @@ udp6_input(struct mbuf **mp, int *offp, int proto) goto badheadlocked; } INP_RLOCK(last); - INP_INFO_RUNLOCK(&V_udbinfo); + INP_INFO_RUNLOCK(pcbinfo); up = intoudpcb(last); if (up->u_tun_func == NULL) { udp6_append(last, m, off, &fromsa); @@ -405,8 +423,8 @@ udp6_input(struct mbuf **mp, int *offp, int proto) * Transparently forwarded. Pretend to be the destination. * Already got one like this? */ - inp = in6_pcblookup_mbuf(&V_udbinfo, - &ip6->ip6_src, uh->uh_sport, &ip6->ip6_dst, uh->uh_dport, + inp = in6_pcblookup_mbuf(pcbinfo, &ip6->ip6_src, + uh->uh_sport, &ip6->ip6_dst, uh->uh_dport, INPLOOKUP_RLOCKPCB, m->m_pkthdr.rcvif, m); if (!inp) { /* @@ -414,7 +432,7 @@ udp6_input(struct mbuf **mp, int *offp, int proto) * Because we've rewritten the destination address, * any hardware-generated hash is ignored. */ - inp = in6_pcblookup(&V_udbinfo, &ip6->ip6_src, + inp = in6_pcblookup(pcbinfo, &ip6->ip6_src, uh->uh_sport, &next_hop6->sin6_addr, next_hop6->sin6_port ? htons(next_hop6->sin6_port) : uh->uh_dport, INPLOOKUP_WILDCARD | @@ -424,7 +442,7 @@ udp6_input(struct mbuf **mp, int *offp, int proto) m_tag_delete(m, fwd_tag); m->m_flags &= ~M_IP6_NEXTHOP; } else - inp = in6_pcblookup_mbuf(&V_udbinfo, &ip6->ip6_src, + inp = in6_pcblookup_mbuf(pcbinfo, &ip6->ip6_src, uh->uh_sport, &ip6->ip6_dst, uh->uh_dport, INPLOOKUP_WILDCARD | INPLOOKUP_RLOCKPCB, m->m_pkthdr.rcvif, m); @@ -454,7 +472,14 @@ udp6_input(struct mbuf **mp, int *offp, int proto) return (IPPROTO_DONE); } INP_RLOCK_ASSERT(inp); - up = intoudpcb(inp); + if (cscov_partial) { + up = intoudpcb(inp); + if (up->u_rxcslen > ulen) { + INP_RUNLOCK(inp); + m_freem(m); + return (IPPROTO_DONE); + } + } if (up->u_tun_func == NULL) { udp6_append(inp, m, off, &fromsa); } else { @@ -468,15 +493,16 @@ udp6_input(struct mbuf **mp, int *offp, int proto) return (IPPROTO_DONE); badheadlocked: - INP_INFO_RUNLOCK(&V_udbinfo); + INP_INFO_RUNLOCK(pcbinfo); badunlocked: if (m) m_freem(m); return (IPPROTO_DONE); } -void -udp6_ctlinput(int cmd, struct sockaddr *sa, void *d) +static void +udp6_common_ctlinput(int cmd, struct sockaddr *sa, void *d, + struct inpcbinfo *pcbinfo) { struct udphdr uh; struct ip6_hdr *ip6; @@ -532,14 +558,26 @@ badunlocked: bzero(&uh, sizeof(uh)); m_copydata(m, off, sizeof(*uhp), (caddr_t)&uh); - (void) in6_pcbnotify(&V_udbinfo, sa, uh.uh_dport, + (void) in6_pcbnotify(pcbinfo, sa, uh.uh_dport, (struct sockaddr *)ip6cp->ip6c_src, uh.uh_sport, cmd, cmdarg, notify); } else - (void) in6_pcbnotify(&V_udbinfo, sa, 0, + (void) in6_pcbnotify(pcbinfo, sa, 0, (const struct sockaddr *)sa6_src, 0, cmd, cmdarg, notify); } +void +udp6_ctlinput(int cmd, struct sockaddr *sa, void *d) +{ + return (udp6_common_ctlinput(cmd, sa, d, &V_udbinfo)); +} + +void +udplite6_ctlinput(int cmd, struct sockaddr *sa, void *d) +{ + return (udp6_common_ctlinput(cmd, sa, d, &V_ulitecbinfo)); +} + static int udp6_getcred(SYSCTL_HANDLER_ARGS) { @@ -597,9 +635,12 @@ udp6_output(struct inpcb *inp, struct mbuf *m, str struct in6_addr *laddr, *faddr, in6a; struct sockaddr_in6 *sin6 = NULL; struct ifnet *oifp = NULL; + int cscov_partial = 0; int scope_ambiguous = 0; u_short fport; int error = 0; + uint8_t nxt; + uint16_t cscov = 0; struct ip6_pktopts *optp, opt; int af = AF_INET6, hlen = sizeof(struct ip6_hdr); int flags; @@ -756,13 +797,29 @@ udp6_output(struct inpcb *inp, struct mbuf *m, str /* * Stuff checksum and output datagram. */ + nxt = IPPROTO_UDP; udp6 = (struct udphdr *)(mtod(m, caddr_t) + hlen); udp6->uh_sport = inp->inp_lport; /* lport is always set in the PCB */ udp6->uh_dport = fport; - if (plen <= 0xffff) + if (inp->inp_socket->so_proto->pr_protocol == IPPROTO_UDPLITE) { + struct udpcb *up; + + nxt = IPPROTO_UDPLITE; + up = intoudpcb(inp); + cscov = up->u_txcslen; + if (cscov >= plen) + cscov = 0; + udp6->uh_ulen = htons(cscov); + + /* For UDPLite, checksum coverage length of zero means + * the entire UDPLite packet is covered by the checksum. + */ + cscov_partial = (cscov == 0) ? 0 : 1; + } else if (plen <= 0xffff) udp6->uh_ulen = htons((u_short)plen); else udp6->uh_ulen = 0; + udp6->uh_sum = 0; switch (af) { @@ -771,17 +828,22 @@ udp6_output(struct inpcb *inp, struct mbuf *m, str ip6->ip6_flow = inp->inp_flow & IPV6_FLOWINFO_MASK; ip6->ip6_vfc &= ~IPV6_VERSION_MASK; ip6->ip6_vfc |= IPV6_VERSION; -#if 0 /* ip6_plen will be filled in ip6_output. */ - ip6->ip6_plen = htons((u_short)plen); -#endif - ip6->ip6_nxt = IPPROTO_UDP; + if (nxt == IPPROTO_UDPLITE) + ip6->ip6_plen = htons((u_short)plen); + ip6->ip6_nxt = nxt; ip6->ip6_hlim = in6_selecthlim(inp, NULL); ip6->ip6_src = *laddr; ip6->ip6_dst = *faddr; - udp6->uh_sum = in6_cksum_pseudo(ip6, plen, IPPROTO_UDP, 0); - m->m_pkthdr.csum_flags = CSUM_UDP_IPV6; - m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum); + if (cscov_partial) { + if ((udp6->uh_sum = in6_cksum(m, 0, + sizeof(struct ip6_hdr), cscov)) == 0) + udp6->uh_sum = 0xffff; + } else { + udp6->uh_sum = in6_cksum_pseudo(ip6, plen, nxt, 0); + m->m_pkthdr.csum_flags = CSUM_UDP_IPV6; + m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum); + } flags = 0; @@ -826,10 +888,10 @@ udp6_abort(struct socket *so) INP_WLOCK(inp); if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) { - INP_HASH_WLOCK(&V_udbinfo); + INP_HASH_WLOCK(inp->inp_pcbinfo); in6_pcbdisconnect(inp); inp->in6p_laddr = in6addr_any; - INP_HASH_WUNLOCK(&V_udbinfo); + INP_HASH_WUNLOCK(inp->inp_pcbinfo); soisdisconnected(so); } INP_WUNLOCK(inp); @@ -839,8 +901,13 @@ static int udp6_attach(struct socket *so, int proto, struct thread *td) { struct inpcb *inp; + struct inpcbinfo *pcbinfo; int error; + if (so->so_proto->pr_protocol == IPPROTO_UDP) + pcbinfo = &V_udbinfo; + else + pcbinfo = &V_ulitecbinfo; inp = sotoinpcb(so); KASSERT(inp == NULL, ("udp6_attach: inp != NULL")); @@ -849,10 +916,10 @@ udp6_attach(struct socket *so, int proto, struct t 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); } inp = (struct inpcb *)so->so_pcb; @@ -873,11 +940,11 @@ udp6_attach(struct socket *so, int proto, struct t 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); } @@ -891,7 +958,7 @@ udp6_bind(struct socket *so, struct sockaddr *nam, KASSERT(inp != NULL, ("udp6_bind: inp == NULL")); INP_WLOCK(inp); - INP_HASH_WLOCK(&V_udbinfo); + INP_HASH_WLOCK(inp->inp_pcbinfo); inp->inp_vflag &= ~INP_IPV4; inp->inp_vflag |= INP_IPV6; if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) { @@ -919,7 +986,7 @@ udp6_bind(struct socket *so, struct sockaddr *nam, #ifdef INET out: #endif - INP_HASH_WUNLOCK(&V_udbinfo); + INP_HASH_WUNLOCK(inp->inp_pcbinfo); INP_WUNLOCK(inp); return (error); } @@ -943,10 +1010,10 @@ udp6_close(struct socket *so) #endif INP_WLOCK(inp); if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) { - INP_HASH_WLOCK(&V_udbinfo); + INP_HASH_WLOCK(inp->inp_pcbinfo); in6_pcbdisconnect(inp); inp->in6p_laddr = in6addr_any; - INP_HASH_WUNLOCK(&V_udbinfo); + INP_HASH_WUNLOCK(inp->inp_pcbinfo); soisdisconnected(so); } INP_WUNLOCK(inp); @@ -985,10 +1052,10 @@ udp6_connect(struct socket *so, struct sockaddr *n error = prison_remote_ip4(td->td_ucred, &sin.sin_addr); if (error != 0) goto out; - INP_HASH_WLOCK(&V_udbinfo); + INP_HASH_WLOCK(inp->inp_pcbinfo); error = in_pcbconnect(inp, (struct sockaddr *)&sin, td->td_ucred); - INP_HASH_WUNLOCK(&V_udbinfo); + INP_HASH_WUNLOCK(inp->inp_pcbinfo); if (error == 0) soisconnected(so); goto out; @@ -1003,9 +1070,9 @@ udp6_connect(struct socket *so, struct sockaddr *n error = prison_remote_ip6(td->td_ucred, &sin6->sin6_addr); if (error != 0) goto out; - INP_HASH_WLOCK(&V_udbinfo); + INP_HASH_WLOCK(inp->inp_pcbinfo); error = in6_pcbconnect(inp, nam, td->td_ucred); - INP_HASH_WUNLOCK(&V_udbinfo); + INP_HASH_WUNLOCK(inp->inp_pcbinfo); if (error == 0) soisconnected(so); out: @@ -1018,18 +1085,21 @@ udp6_detach(struct socket *so) { struct inpcb *inp; struct udpcb *up; + int isudp; + isudp = (so->so_proto->pr_protocol == IPPROTO_UDP) ? 1 : 0; + inp = sotoinpcb(so); KASSERT(inp != NULL, ("udp6_detach: inp == NULL")); - INP_INFO_WLOCK(&V_udbinfo); + INP_INFO_WLOCK(inp->inp_pcbinfo); INP_WLOCK(inp); up = intoudpcb(inp); KASSERT(up != NULL, ("%s: up == NULL", __func__)); in_pcbdetach(inp); in_pcbfree(inp); - INP_INFO_WUNLOCK(&V_udbinfo); - udp_discardcb(up); + INP_INFO_WUNLOCK(inp->inp_pcbinfo); + udp_discardcb(up, isudp); } static int @@ -1058,10 +1128,10 @@ udp6_disconnect(struct socket *so) goto out; } - INP_HASH_WLOCK(&V_udbinfo); + INP_HASH_WLOCK(inp->inp_pcbinfo); in6_pcbdisconnect(inp); inp->in6p_laddr = in6addr_any; - INP_HASH_WUNLOCK(&V_udbinfo); + INP_HASH_WUNLOCK(inp->inp_pcbinfo); SOCK_LOCK(so); so->so_state &= ~SS_ISCONNECTED; /* XXX */ SOCK_UNLOCK(so); @@ -1128,9 +1198,9 @@ udp6_send(struct socket *so, int flags, struct mbu #ifdef MAC mac_inpcb_create_mbuf(inp, m); #endif - INP_HASH_WLOCK(&V_udbinfo); + INP_HASH_WLOCK(inp->inp_pcbinfo); error = udp6_output(inp, m, addr, control, td); - INP_HASH_WUNLOCK(&V_udbinfo); + INP_HASH_WUNLOCK(inp->inp_pcbinfo); #ifdef INET #endif INP_WUNLOCK(inp); Index: sys/netinet6/udp6_var.h =================================================================== --- sys/netinet6/udp6_var.h (revision 248458) +++ sys/netinet6/udp6_var.h (working copy) @@ -69,6 +69,7 @@ SYSCTL_DECL(_net_inet6_udp6); extern struct pr_usrreqs udp6_usrreqs; void udp6_ctlinput(int, struct sockaddr *, void *); +void udplite6_ctlinput(int, struct sockaddr *, void *); int udp6_input(struct mbuf **, int *, int); #endif
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?CA%2BWntOvLHYAxp5_s%2BCrWnQSy5H=G_p07WMRZn-=77Dx2kecr0g>
