udp6_usrreq.c revision 256281
1249259Sdim/*- 2249259Sdim * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. 3249259Sdim * Copyright (c) 2010-2011 Juniper Networks, Inc. 4249259Sdim * All rights reserved. 5249259Sdim * 6249259Sdim * Portions of this software were developed by Robert N. M. Watson under 7249259Sdim * contract to Juniper Networks, Inc. 8249259Sdim * 9249259Sdim * Redistribution and use in source and binary forms, with or without 10249259Sdim * modification, are permitted provided that the following conditions 11249259Sdim * are met: 12249259Sdim * 1. Redistributions of source code must retain the above copyright 13249259Sdim * notice, this list of conditions and the following disclaimer. 14249259Sdim * 2. Redistributions in binary form must reproduce the above copyright 15249259Sdim * notice, this list of conditions and the following disclaimer in the 16249259Sdim * documentation and/or other materials provided with the distribution. 17249259Sdim * 3. Neither the name of the project nor the names of its contributors 18249259Sdim * may be used to endorse or promote products derived from this software 19249259Sdim * without specific prior written permission. 20249259Sdim * 21249259Sdim * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 22249259Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23249259Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24249259Sdim * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 25249259Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26249259Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27249259Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28249259Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29249259Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30249259Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31249259Sdim * SUCH DAMAGE. 32249259Sdim * 33249259Sdim * $KAME: udp6_usrreq.c,v 1.27 2001/05/21 05:45:10 jinmei Exp $ 34249259Sdim * $KAME: udp6_output.c,v 1.31 2001/05/21 16:39:15 jinmei Exp $ 35249259Sdim */ 36249259Sdim 37249259Sdim/*- 38249259Sdim * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1995 39249259Sdim * The Regents of the University of California. 40249259Sdim * All rights reserved. 41249259Sdim * 42249259Sdim * Redistribution and use in source and binary forms, with or without 43249259Sdim * modification, are permitted provided that the following conditions 44249259Sdim * are met: 45249259Sdim * 1. Redistributions of source code must retain the above copyright 46249259Sdim * notice, this list of conditions and the following disclaimer. 47249259Sdim * 2. Redistributions in binary form must reproduce the above copyright 48249259Sdim * notice, this list of conditions and the following disclaimer in the 49249259Sdim * documentation and/or other materials provided with the distribution. 50249259Sdim * 4. Neither the name of the University nor the names of its contributors 51249259Sdim * may be used to endorse or promote products derived from this software 52249259Sdim * without specific prior written permission. 53249259Sdim * 54249259Sdim * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 55249259Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 56249259Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 57249259Sdim * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 58249259Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 59249259Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 60249259Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 61249259Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 62249259Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 63249259Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 64249259Sdim * SUCH DAMAGE. 65249259Sdim * 66249259Sdim * @(#)udp_usrreq.c 8.6 (Berkeley) 5/23/95 67249259Sdim */ 68249259Sdim 69249259Sdim#include <sys/cdefs.h> 70249259Sdim__FBSDID("$FreeBSD: stable/10/sys/netinet6/udp6_usrreq.c 254889 2013-08-25 21:54:41Z markj $"); 71249259Sdim 72249259Sdim#include "opt_inet.h" 73249259Sdim#include "opt_inet6.h" 74249259Sdim#include "opt_ipfw.h" 75249259Sdim#include "opt_ipsec.h" 76249259Sdim#include "opt_kdtrace.h" 77249259Sdim 78249259Sdim#include <sys/param.h> 79249259Sdim#include <sys/jail.h> 80249259Sdim#include <sys/kernel.h> 81249259Sdim#include <sys/lock.h> 82249259Sdim#include <sys/mbuf.h> 83249259Sdim#include <sys/priv.h> 84249259Sdim#include <sys/proc.h> 85249259Sdim#include <sys/protosw.h> 86249259Sdim#include <sys/sdt.h> 87249259Sdim#include <sys/signalvar.h> 88249259Sdim#include <sys/socket.h> 89249259Sdim#include <sys/socketvar.h> 90249259Sdim#include <sys/sx.h> 91249259Sdim#include <sys/sysctl.h> 92249259Sdim#include <sys/syslog.h> 93249259Sdim#include <sys/systm.h> 94249259Sdim 95249259Sdim#include <net/if.h> 96249259Sdim#include <net/if_types.h> 97249259Sdim#include <net/route.h> 98249259Sdim 99249259Sdim#include <netinet/in.h> 100249259Sdim#include <netinet/in_kdtrace.h> 101249259Sdim#include <netinet/in_pcb.h> 102249259Sdim#include <netinet/in_systm.h> 103249259Sdim#include <netinet/in_var.h> 104249259Sdim#include <netinet/ip.h> 105249259Sdim#include <netinet/ip_icmp.h> 106249259Sdim#include <netinet/ip6.h> 107249259Sdim#include <netinet/icmp_var.h> 108249259Sdim#include <netinet/icmp6.h> 109249259Sdim#include <netinet/ip_var.h> 110249259Sdim#include <netinet/udp.h> 111249259Sdim#include <netinet/udp_var.h> 112249259Sdim 113249259Sdim#include <netinet6/ip6protosw.h> 114249259Sdim#include <netinet6/ip6_var.h> 115249259Sdim#include <netinet6/in6_pcb.h> 116249259Sdim#include <netinet6/udp6_var.h> 117249259Sdim#include <netinet6/scope6_var.h> 118249259Sdim 119249259Sdim#ifdef IPSEC 120249259Sdim#include <netipsec/ipsec.h> 121249259Sdim#include <netipsec/ipsec6.h> 122249259Sdim#endif /* IPSEC */ 123249259Sdim 124249259Sdim#include <security/mac/mac_framework.h> 125249259Sdim 126249259Sdim/* 127249259Sdim * UDP protocol implementation. 128249259Sdim * Per RFC 768, August, 1980. 129249259Sdim */ 130249259Sdim 131249259Sdimextern struct protosw inetsw[]; 132249259Sdimstatic void udp6_detach(struct socket *so); 133249259Sdim 134249259Sdimstatic void 135249259Sdimudp6_append(struct inpcb *inp, struct mbuf *n, int off, 136249259Sdim struct sockaddr_in6 *fromsa) 137249259Sdim{ 138249259Sdim struct socket *so; 139249259Sdim struct mbuf *opts; 140249259Sdim 141249259Sdim INP_LOCK_ASSERT(inp); 142249259Sdim 143249259Sdim#ifdef IPSEC 144249259Sdim /* Check AH/ESP integrity. */ 145249259Sdim if (ipsec6_in_reject(n, inp)) { 146249259Sdim m_freem(n); 147249259Sdim IPSEC6STAT_INC(ips_in_polvio); 148249259Sdim return; 149249259Sdim } 150249259Sdim#endif /* IPSEC */ 151249259Sdim#ifdef MAC 152249259Sdim if (mac_inpcb_check_deliver(inp, n) != 0) { 153249259Sdim m_freem(n); 154249259Sdim return; 155249259Sdim } 156249259Sdim#endif 157249259Sdim opts = NULL; 158249259Sdim if (inp->inp_flags & INP_CONTROLOPTS || 159249259Sdim inp->inp_socket->so_options & SO_TIMESTAMP) 160249259Sdim ip6_savecontrol(inp, n, &opts); 161249259Sdim m_adj(n, off + sizeof(struct udphdr)); 162249259Sdim 163249259Sdim so = inp->inp_socket; 164249259Sdim SOCKBUF_LOCK(&so->so_rcv); 165249259Sdim if (sbappendaddr_locked(&so->so_rcv, (struct sockaddr *)fromsa, n, 166249259Sdim opts) == 0) { 167249259Sdim SOCKBUF_UNLOCK(&so->so_rcv); 168249259Sdim m_freem(n); 169249259Sdim if (opts) 170249259Sdim m_freem(opts); 171249259Sdim UDPSTAT_INC(udps_fullsock); 172249259Sdim } else 173249259Sdim sorwakeup_locked(so); 174249259Sdim} 175249259Sdim 176249259Sdimint 177249259Sdimudp6_input(struct mbuf **mp, int *offp, int proto) 178249259Sdim{ 179249259Sdim struct mbuf *m = *mp; 180249259Sdim struct ifnet *ifp; 181249259Sdim struct ip6_hdr *ip6; 182249259Sdim struct udphdr *uh; 183249259Sdim struct inpcb *inp; 184249259Sdim struct udpcb *up; 185249259Sdim int off = *offp; 186249259Sdim int plen, ulen; 187249259Sdim struct sockaddr_in6 fromsa; 188249259Sdim struct m_tag *fwd_tag; 189249259Sdim uint16_t uh_sum; 190249259Sdim 191249259Sdim ifp = m->m_pkthdr.rcvif; 192249259Sdim ip6 = mtod(m, struct ip6_hdr *); 193249259Sdim 194249259Sdim if (faithprefix_p != NULL && (*faithprefix_p)(&ip6->ip6_dst)) { 195249259Sdim /* XXX send icmp6 host/port unreach? */ 196249259Sdim m_freem(m); 197249259Sdim return (IPPROTO_DONE); 198249259Sdim } 199249259Sdim 200249259Sdim#ifndef PULLDOWN_TEST 201249259Sdim IP6_EXTHDR_CHECK(m, off, sizeof(struct udphdr), IPPROTO_DONE); 202249259Sdim ip6 = mtod(m, struct ip6_hdr *); 203249259Sdim uh = (struct udphdr *)((caddr_t)ip6 + off); 204249259Sdim#else 205249259Sdim IP6_EXTHDR_GET(uh, struct udphdr *, m, off, sizeof(*uh)); 206249259Sdim if (!uh) 207249259Sdim return (IPPROTO_DONE); 208249259Sdim#endif 209249259Sdim 210249259Sdim UDPSTAT_INC(udps_ipackets); 211249259Sdim 212249259Sdim /* 213249259Sdim * Destination port of 0 is illegal, based on RFC768. 214249259Sdim */ 215249259Sdim if (uh->uh_dport == 0) 216249259Sdim goto badunlocked; 217249259Sdim 218249259Sdim plen = ntohs(ip6->ip6_plen) - off + sizeof(*ip6); 219249259Sdim ulen = ntohs((u_short)uh->uh_ulen); 220249259Sdim 221249259Sdim if (plen != ulen) { 222249259Sdim UDPSTAT_INC(udps_badlen); 223249259Sdim goto badunlocked; 224249259Sdim } 225249259Sdim 226249259Sdim /* 227249259Sdim * Checksum extended UDP header and data. 228249259Sdim */ 229249259Sdim if (uh->uh_sum == 0) { 230249259Sdim UDPSTAT_INC(udps_nosum); 231249259Sdim goto badunlocked; 232249259Sdim } 233249259Sdim 234249259Sdim if (m->m_pkthdr.csum_flags & CSUM_DATA_VALID_IPV6) { 235249259Sdim if (m->m_pkthdr.csum_flags & CSUM_PSEUDO_HDR) 236249259Sdim uh_sum = m->m_pkthdr.csum_data; 237249259Sdim else 238249259Sdim uh_sum = in6_cksum_pseudo(ip6, ulen, 239249259Sdim IPPROTO_UDP, m->m_pkthdr.csum_data); 240249259Sdim uh_sum ^= 0xffff; 241249259Sdim } else 242249259Sdim uh_sum = in6_cksum(m, IPPROTO_UDP, off, ulen); 243249259Sdim 244249259Sdim if (uh_sum != 0) { 245249259Sdim UDPSTAT_INC(udps_badsum); 246249259Sdim goto badunlocked; 247249259Sdim } 248249259Sdim 249249259Sdim /* 250249259Sdim * Construct sockaddr format source address. 251249259Sdim */ 252249259Sdim init_sin6(&fromsa, m); 253249259Sdim fromsa.sin6_port = uh->uh_sport; 254249259Sdim 255249259Sdim if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { 256249259Sdim struct inpcb *last; 257249259Sdim struct ip6_moptions *imo; 258249259Sdim 259249259Sdim INP_INFO_RLOCK(&V_udbinfo); 260249259Sdim /* 261249259Sdim * In the event that laddr should be set to the link-local 262249259Sdim * address (this happens in RIPng), the multicast address 263249259Sdim * specified in the received packet will not match laddr. To 264249259Sdim * handle this situation, matching is relaxed if the 265249259Sdim * receiving interface is the same as one specified in the 266249259Sdim * socket and if the destination multicast address matches 267249259Sdim * one of the multicast groups specified in the socket. 268249259Sdim */ 269249259Sdim 270249259Sdim /* 271249259Sdim * KAME note: traditionally we dropped udpiphdr from mbuf 272249259Sdim * here. We need udphdr for IPsec processing so we do that 273249259Sdim * later. 274249259Sdim */ 275249259Sdim last = NULL; 276249259Sdim LIST_FOREACH(inp, &V_udb, inp_list) { 277249259Sdim if ((inp->inp_vflag & INP_IPV6) == 0) 278249259Sdim continue; 279249259Sdim if (inp->inp_lport != uh->uh_dport) 280249259Sdim continue; 281249259Sdim if (inp->inp_fport != 0 && 282249259Sdim inp->inp_fport != uh->uh_sport) 283249259Sdim continue; 284249259Sdim if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) { 285249259Sdim if (!IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr, 286249259Sdim &ip6->ip6_dst)) 287249259Sdim continue; 288249259Sdim } 289249259Sdim if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) { 290249259Sdim if (!IN6_ARE_ADDR_EQUAL(&inp->in6p_faddr, 291249259Sdim &ip6->ip6_src) || 292249259Sdim inp->inp_fport != uh->uh_sport) 293249259Sdim continue; 294249259Sdim } 295249259Sdim 296249259Sdim /* 297249259Sdim * XXXRW: Because we weren't holding either the inpcb 298249259Sdim * or the hash lock when we checked for a match 299249259Sdim * before, we should probably recheck now that the 300249259Sdim * inpcb lock is (supposed to be) held. 301249259Sdim */ 302249259Sdim 303249259Sdim /* 304249259Sdim * Handle socket delivery policy for any-source 305249259Sdim * and source-specific multicast. [RFC3678] 306249259Sdim */ 307249259Sdim imo = inp->in6p_moptions; 308249259Sdim if (imo && IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { 309249259Sdim struct sockaddr_in6 mcaddr; 310249259Sdim int blocked; 311249259Sdim 312249259Sdim INP_RLOCK(inp); 313249259Sdim 314249259Sdim bzero(&mcaddr, sizeof(struct sockaddr_in6)); 315249259Sdim mcaddr.sin6_len = sizeof(struct sockaddr_in6); 316249259Sdim mcaddr.sin6_family = AF_INET6; 317249259Sdim mcaddr.sin6_addr = ip6->ip6_dst; 318249259Sdim 319249259Sdim blocked = im6o_mc_filter(imo, ifp, 320249259Sdim (struct sockaddr *)&mcaddr, 321249259Sdim (struct sockaddr *)&fromsa); 322249259Sdim if (blocked != MCAST_PASS) { 323249259Sdim if (blocked == MCAST_NOTGMEMBER) 324249259Sdim IP6STAT_INC(ip6s_notmember); 325249259Sdim if (blocked == MCAST_NOTSMEMBER || 326249259Sdim blocked == MCAST_MUTED) 327249259Sdim UDPSTAT_INC(udps_filtermcast); 328249259Sdim INP_RUNLOCK(inp); /* XXX */ 329249259Sdim continue; 330249259Sdim } 331249259Sdim 332249259Sdim INP_RUNLOCK(inp); 333249259Sdim } 334249259Sdim if (last != NULL) { 335249259Sdim struct mbuf *n; 336249259Sdim 337249259Sdim if ((n = m_copy(m, 0, M_COPYALL)) != NULL) { 338249259Sdim INP_RLOCK(last); 339249259Sdim up = intoudpcb(last); 340249259Sdim if (up->u_tun_func == NULL) { 341249259Sdim udp6_append(last, n, off, &fromsa); 342249259Sdim } else { 343249259Sdim /* 344249259Sdim * Engage the tunneling 345249259Sdim * protocol we will have to 346249259Sdim * leave the info_lock up, 347249259Sdim * since we are hunting 348249259Sdim * through multiple UDP's. 349249259Sdim * 350249259Sdim */ 351249259Sdim (*up->u_tun_func)(n, off, last); 352249259Sdim } 353249259Sdim INP_RUNLOCK(last); 354249259Sdim } 355249259Sdim } 356249259Sdim last = inp; 357249259Sdim /* 358249259Sdim * Don't look for additional matches if this one does 359249259Sdim * not have either the SO_REUSEPORT or SO_REUSEADDR 360249259Sdim * socket options set. This heuristic avoids 361249259Sdim * searching through all pcbs in the common case of a 362249259Sdim * non-shared port. It assumes that an application 363249259Sdim * will never clear these options after setting them. 364249259Sdim */ 365249259Sdim if ((last->inp_socket->so_options & 366249259Sdim (SO_REUSEPORT|SO_REUSEADDR)) == 0) 367249259Sdim break; 368249259Sdim } 369249259Sdim 370249259Sdim if (last == NULL) { 371249259Sdim /* 372249259Sdim * No matching pcb found; discard datagram. (No need 373249259Sdim * to send an ICMP Port Unreachable for a broadcast 374249259Sdim * or multicast datgram.) 375249259Sdim */ 376249259Sdim UDPSTAT_INC(udps_noport); 377249259Sdim UDPSTAT_INC(udps_noportmcast); 378249259Sdim goto badheadlocked; 379249259Sdim } 380249259Sdim INP_RLOCK(last); 381249259Sdim INP_INFO_RUNLOCK(&V_udbinfo); 382249259Sdim up = intoudpcb(last); 383249259Sdim UDP_PROBE(receive, NULL, last, ip6, last, uh); 384249259Sdim if (up->u_tun_func == NULL) { 385249259Sdim udp6_append(last, m, off, &fromsa); 386249259Sdim } else { 387249259Sdim /* 388249259Sdim * Engage the tunneling protocol. 389249259Sdim */ 390249259Sdim (*up->u_tun_func)(m, off, last); 391249259Sdim } 392249259Sdim INP_RUNLOCK(last); 393249259Sdim return (IPPROTO_DONE); 394249259Sdim } 395249259Sdim /* 396249259Sdim * Locate pcb for datagram. 397249259Sdim */ 398249259Sdim 399249259Sdim /* 400249259Sdim * Grab info from PACKET_TAG_IPFORWARD tag prepended to the chain. 401249259Sdim */ 402249259Sdim if ((m->m_flags & M_IP6_NEXTHOP) && 403249259Sdim (fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL)) != NULL) { 404249259Sdim struct sockaddr_in6 *next_hop6; 405249259Sdim 406249259Sdim next_hop6 = (struct sockaddr_in6 *)(fwd_tag + 1); 407249259Sdim 408249259Sdim /* 409249259Sdim * Transparently forwarded. Pretend to be the destination. 410249259Sdim * Already got one like this? 411249259Sdim */ 412249259Sdim inp = in6_pcblookup_mbuf(&V_udbinfo, 413249259Sdim &ip6->ip6_src, uh->uh_sport, &ip6->ip6_dst, uh->uh_dport, 414249259Sdim INPLOOKUP_RLOCKPCB, m->m_pkthdr.rcvif, m); 415249259Sdim if (!inp) { 416249259Sdim /* 417249259Sdim * It's new. Try to find the ambushing socket. 418249259Sdim * Because we've rewritten the destination address, 419249259Sdim * any hardware-generated hash is ignored. 420249259Sdim */ 421249259Sdim inp = in6_pcblookup(&V_udbinfo, &ip6->ip6_src, 422249259Sdim uh->uh_sport, &next_hop6->sin6_addr, 423249259Sdim next_hop6->sin6_port ? htons(next_hop6->sin6_port) : 424249259Sdim uh->uh_dport, INPLOOKUP_WILDCARD | 425249259Sdim INPLOOKUP_RLOCKPCB, m->m_pkthdr.rcvif); 426249259Sdim } 427249259Sdim /* Remove the tag from the packet. We don't need it anymore. */ 428249259Sdim m_tag_delete(m, fwd_tag); 429249259Sdim m->m_flags &= ~M_IP6_NEXTHOP; 430249259Sdim } else 431249259Sdim inp = in6_pcblookup_mbuf(&V_udbinfo, &ip6->ip6_src, 432249259Sdim uh->uh_sport, &ip6->ip6_dst, uh->uh_dport, 433249259Sdim INPLOOKUP_WILDCARD | INPLOOKUP_RLOCKPCB, 434249259Sdim m->m_pkthdr.rcvif, m); 435249259Sdim if (inp == NULL) { 436249259Sdim if (udp_log_in_vain) { 437249259Sdim char ip6bufs[INET6_ADDRSTRLEN]; 438249259Sdim char ip6bufd[INET6_ADDRSTRLEN]; 439249259Sdim 440249259Sdim log(LOG_INFO, 441249259Sdim "Connection attempt to UDP [%s]:%d from [%s]:%d\n", 442249259Sdim ip6_sprintf(ip6bufd, &ip6->ip6_dst), 443249259Sdim ntohs(uh->uh_dport), 444249259Sdim ip6_sprintf(ip6bufs, &ip6->ip6_src), 445249259Sdim ntohs(uh->uh_sport)); 446249259Sdim } 447249259Sdim UDPSTAT_INC(udps_noport); 448249259Sdim if (m->m_flags & M_MCAST) { 449249259Sdim printf("UDP6: M_MCAST is set in a unicast packet.\n"); 450249259Sdim UDPSTAT_INC(udps_noportmcast); 451249259Sdim goto badunlocked; 452249259Sdim } 453249259Sdim if (V_udp_blackhole) 454249259Sdim goto badunlocked; 455249259Sdim if (badport_bandlim(BANDLIM_ICMP6_UNREACH) < 0) 456249259Sdim goto badunlocked; 457249259Sdim icmp6_error(m, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOPORT, 0); 458249259Sdim return (IPPROTO_DONE); 459249259Sdim } 460249259Sdim INP_RLOCK_ASSERT(inp); 461249259Sdim up = intoudpcb(inp); 462249259Sdim UDP_PROBE(receive, NULL, inp, ip6, inp, uh); 463249259Sdim if (up->u_tun_func == NULL) { 464249259Sdim udp6_append(inp, m, off, &fromsa); 465249259Sdim } else { 466249259Sdim /* 467249259Sdim * Engage the tunneling protocol. 468249259Sdim */ 469249259Sdim 470249259Sdim (*up->u_tun_func)(m, off, inp); 471249259Sdim } 472249259Sdim INP_RUNLOCK(inp); 473249259Sdim return (IPPROTO_DONE); 474249259Sdim 475249259Sdimbadheadlocked: 476249259Sdim INP_INFO_RUNLOCK(&V_udbinfo); 477249259Sdimbadunlocked: 478249259Sdim if (m) 479249259Sdim m_freem(m); 480249259Sdim return (IPPROTO_DONE); 481249259Sdim} 482249259Sdim 483249259Sdimvoid 484249259Sdimudp6_ctlinput(int cmd, struct sockaddr *sa, void *d) 485249259Sdim{ 486249259Sdim struct udphdr uh; 487249259Sdim struct ip6_hdr *ip6; 488249259Sdim struct mbuf *m; 489249259Sdim int off = 0; 490249259Sdim struct ip6ctlparam *ip6cp = NULL; 491249259Sdim const struct sockaddr_in6 *sa6_src = NULL; 492249259Sdim void *cmdarg; 493249259Sdim struct inpcb *(*notify)(struct inpcb *, int) = udp_notify; 494249259Sdim struct udp_portonly { 495249259Sdim u_int16_t uh_sport; 496249259Sdim u_int16_t uh_dport; 497249259Sdim } *uhp; 498249259Sdim 499249259Sdim if (sa->sa_family != AF_INET6 || 500249259Sdim sa->sa_len != sizeof(struct sockaddr_in6)) 501249259Sdim return; 502249259Sdim 503249259Sdim if ((unsigned)cmd >= PRC_NCMDS) 504249259Sdim return; 505249259Sdim if (PRC_IS_REDIRECT(cmd)) 506249259Sdim notify = in6_rtchange, d = NULL; 507249259Sdim else if (cmd == PRC_HOSTDEAD) 508249259Sdim d = NULL; 509249259Sdim else if (inet6ctlerrmap[cmd] == 0) 510249259Sdim return; 511249259Sdim 512249259Sdim /* if the parameter is from icmp6, decode it. */ 513249259Sdim if (d != NULL) { 514249259Sdim ip6cp = (struct ip6ctlparam *)d; 515249259Sdim m = ip6cp->ip6c_m; 516249259Sdim ip6 = ip6cp->ip6c_ip6; 517249259Sdim off = ip6cp->ip6c_off; 518249259Sdim cmdarg = ip6cp->ip6c_cmdarg; 519249259Sdim sa6_src = ip6cp->ip6c_src; 520249259Sdim } else { 521249259Sdim m = NULL; 522249259Sdim ip6 = NULL; 523249259Sdim cmdarg = NULL; 524249259Sdim sa6_src = &sa6_any; 525249259Sdim } 526249259Sdim 527249259Sdim if (ip6) { 528249259Sdim /* 529249259Sdim * XXX: We assume that when IPV6 is non NULL, 530249259Sdim * M and OFF are valid. 531249259Sdim */ 532249259Sdim 533249259Sdim /* Check if we can safely examine src and dst ports. */ 534249259Sdim if (m->m_pkthdr.len < off + sizeof(*uhp)) 535249259Sdim return; 536249259Sdim 537249259Sdim bzero(&uh, sizeof(uh)); 538249259Sdim m_copydata(m, off, sizeof(*uhp), (caddr_t)&uh); 539249259Sdim 540249259Sdim (void) in6_pcbnotify(&V_udbinfo, sa, uh.uh_dport, 541249259Sdim (struct sockaddr *)ip6cp->ip6c_src, uh.uh_sport, cmd, 542249259Sdim cmdarg, notify); 543249259Sdim } else 544249259Sdim (void) in6_pcbnotify(&V_udbinfo, sa, 0, 545249259Sdim (const struct sockaddr *)sa6_src, 0, cmd, cmdarg, notify); 546249259Sdim} 547249259Sdim 548249259Sdimstatic int 549249259Sdimudp6_getcred(SYSCTL_HANDLER_ARGS) 550249259Sdim{ 551249259Sdim struct xucred xuc; 552249259Sdim struct sockaddr_in6 addrs[2]; 553249259Sdim struct inpcb *inp; 554249259Sdim int error; 555249259Sdim 556249259Sdim error = priv_check(req->td, PRIV_NETINET_GETCRED); 557249259Sdim if (error) 558249259Sdim return (error); 559249259Sdim 560249259Sdim if (req->newlen != sizeof(addrs)) 561249259Sdim return (EINVAL); 562249259Sdim if (req->oldlen != sizeof(struct xucred)) 563249259Sdim return (EINVAL); 564249259Sdim error = SYSCTL_IN(req, addrs, sizeof(addrs)); 565249259Sdim if (error) 566249259Sdim return (error); 567249259Sdim if ((error = sa6_embedscope(&addrs[0], V_ip6_use_defzone)) != 0 || 568249259Sdim (error = sa6_embedscope(&addrs[1], V_ip6_use_defzone)) != 0) { 569249259Sdim return (error); 570249259Sdim } 571249259Sdim inp = in6_pcblookup(&V_udbinfo, &addrs[1].sin6_addr, 572249259Sdim addrs[1].sin6_port, &addrs[0].sin6_addr, addrs[0].sin6_port, 573249259Sdim INPLOOKUP_WILDCARD | INPLOOKUP_RLOCKPCB, NULL); 574249259Sdim if (inp != NULL) { 575249259Sdim INP_RLOCK_ASSERT(inp); 576249259Sdim if (inp->inp_socket == NULL) 577249259Sdim error = ENOENT; 578249259Sdim if (error == 0) 579249259Sdim error = cr_canseesocket(req->td->td_ucred, 580249259Sdim inp->inp_socket); 581249259Sdim if (error == 0) 582249259Sdim cru2x(inp->inp_cred, &xuc); 583249259Sdim INP_RUNLOCK(inp); 584249259Sdim } else 585249259Sdim error = ENOENT; 586249259Sdim if (error == 0) 587249259Sdim error = SYSCTL_OUT(req, &xuc, sizeof(struct xucred)); 588249259Sdim return (error); 589249259Sdim} 590249259Sdim 591249259SdimSYSCTL_PROC(_net_inet6_udp6, OID_AUTO, getcred, CTLTYPE_OPAQUE|CTLFLAG_RW, 0, 592249259Sdim 0, udp6_getcred, "S,xucred", "Get the xucred of a UDP6 connection"); 593249259Sdim 594249259Sdimstatic int 595249259Sdimudp6_output(struct inpcb *inp, struct mbuf *m, struct sockaddr *addr6, 596249259Sdim struct mbuf *control, struct thread *td) 597249259Sdim{ 598249259Sdim u_int32_t ulen = m->m_pkthdr.len; 599249259Sdim u_int32_t plen = sizeof(struct udphdr) + ulen; 600249259Sdim struct ip6_hdr *ip6; 601249259Sdim struct udphdr *udp6; 602249259Sdim struct in6_addr *laddr, *faddr, in6a; 603249259Sdim struct sockaddr_in6 *sin6 = NULL; 604249259Sdim struct ifnet *oifp = NULL; 605249259Sdim int scope_ambiguous = 0; 606249259Sdim u_short fport; 607249259Sdim int error = 0; 608249259Sdim struct ip6_pktopts *optp, opt; 609249259Sdim int af = AF_INET6, hlen = sizeof(struct ip6_hdr); 610249259Sdim int flags; 611249259Sdim struct sockaddr_in6 tmp; 612249259Sdim 613249259Sdim INP_WLOCK_ASSERT(inp); 614249259Sdim INP_HASH_WLOCK_ASSERT(inp->inp_pcbinfo); 615249259Sdim 616249259Sdim if (addr6) { 617249259Sdim /* addr6 has been validated in udp6_send(). */ 618249259Sdim sin6 = (struct sockaddr_in6 *)addr6; 619249259Sdim 620249259Sdim /* protect *sin6 from overwrites */ 621249259Sdim tmp = *sin6; 622249259Sdim sin6 = &tmp; 623249259Sdim 624249259Sdim /* 625249259Sdim * Application should provide a proper zone ID or the use of 626249259Sdim * default zone IDs should be enabled. Unfortunately, some 627249259Sdim * applications do not behave as it should, so we need a 628249259Sdim * workaround. Even if an appropriate ID is not determined, 629249259Sdim * we'll see if we can determine the outgoing interface. If we 630249259Sdim * can, determine the zone ID based on the interface below. 631249259Sdim */ 632249259Sdim if (sin6->sin6_scope_id == 0 && !V_ip6_use_defzone) 633249259Sdim scope_ambiguous = 1; 634249259Sdim if ((error = sa6_embedscope(sin6, V_ip6_use_defzone)) != 0) 635249259Sdim return (error); 636249259Sdim } 637249259Sdim 638249259Sdim if (control) { 639249259Sdim if ((error = ip6_setpktopts(control, &opt, 640249259Sdim inp->in6p_outputopts, td->td_ucred, IPPROTO_UDP)) != 0) 641249259Sdim goto release; 642249259Sdim optp = &opt; 643249259Sdim } else 644249259Sdim optp = inp->in6p_outputopts; 645249259Sdim 646249259Sdim if (sin6) { 647249259Sdim faddr = &sin6->sin6_addr; 648249259Sdim 649249259Sdim /* 650249259Sdim * Since we saw no essential reason for calling in_pcbconnect, 651249259Sdim * we get rid of such kind of logic, and call in6_selectsrc 652249259Sdim * and in6_pcbsetport in order to fill in the local address 653249259Sdim * and the local port. 654249259Sdim */ 655249259Sdim if (sin6->sin6_port == 0) { 656249259Sdim error = EADDRNOTAVAIL; 657249259Sdim goto release; 658249259Sdim } 659249259Sdim 660249259Sdim if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) { 661249259Sdim /* how about ::ffff:0.0.0.0 case? */ 662249259Sdim error = EISCONN; 663249259Sdim goto release; 664249259Sdim } 665249259Sdim 666249259Sdim fport = sin6->sin6_port; /* allow 0 port */ 667249259Sdim 668249259Sdim if (IN6_IS_ADDR_V4MAPPED(faddr)) { 669249259Sdim if ((inp->inp_flags & IN6P_IPV6_V6ONLY)) { 670249259Sdim /* 671249259Sdim * I believe we should explicitly discard the 672249259Sdim * packet when mapped addresses are disabled, 673249259Sdim * rather than send the packet as an IPv6 one. 674249259Sdim * If we chose the latter approach, the packet 675249259Sdim * might be sent out on the wire based on the 676249259Sdim * default route, the situation which we'd 677249259Sdim * probably want to avoid. 678249259Sdim * (20010421 jinmei@kame.net) 679249259Sdim */ 680249259Sdim error = EINVAL; 681249259Sdim goto release; 682249259Sdim } 683249259Sdim if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr) && 684249259Sdim !IN6_IS_ADDR_V4MAPPED(&inp->in6p_laddr)) { 685249259Sdim /* 686249259Sdim * when remote addr is an IPv4-mapped address, 687249259Sdim * local addr should not be an IPv6 address, 688249259Sdim * since you cannot determine how to map IPv6 689249259Sdim * source address to IPv4. 690249259Sdim */ 691249259Sdim error = EINVAL; 692249259Sdim goto release; 693249259Sdim } 694249259Sdim 695249259Sdim af = AF_INET; 696249259Sdim } 697249259Sdim 698249259Sdim if (!IN6_IS_ADDR_V4MAPPED(faddr)) { 699249259Sdim error = in6_selectsrc(sin6, optp, inp, NULL, 700249259Sdim td->td_ucred, &oifp, &in6a); 701249259Sdim if (error) 702249259Sdim goto release; 703249259Sdim if (oifp && scope_ambiguous && 704249259Sdim (error = in6_setscope(&sin6->sin6_addr, 705249259Sdim oifp, NULL))) { 706249259Sdim goto release; 707249259Sdim } 708249259Sdim laddr = &in6a; 709249259Sdim } else 710249259Sdim laddr = &inp->in6p_laddr; /* XXX */ 711249259Sdim if (laddr == NULL) { 712249259Sdim if (error == 0) 713249259Sdim error = EADDRNOTAVAIL; 714249259Sdim goto release; 715249259Sdim } 716249259Sdim if (inp->inp_lport == 0 && 717249259Sdim (error = in6_pcbsetport(laddr, inp, td->td_ucred)) != 0) { 718249259Sdim /* Undo an address bind that may have occurred. */ 719249259Sdim inp->in6p_laddr = in6addr_any; 720249259Sdim goto release; 721249259Sdim } 722249259Sdim } else { 723249259Sdim if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) { 724249259Sdim error = ENOTCONN; 725249259Sdim goto release; 726249259Sdim } 727249259Sdim if (IN6_IS_ADDR_V4MAPPED(&inp->in6p_faddr)) { 728249259Sdim if ((inp->inp_flags & IN6P_IPV6_V6ONLY)) { 729249259Sdim /* 730249259Sdim * XXX: this case would happen when the 731249259Sdim * application sets the V6ONLY flag after 732249259Sdim * connecting the foreign address. 733249259Sdim * Such applications should be fixed, 734249259Sdim * so we bark here. 735249259Sdim */ 736249259Sdim log(LOG_INFO, "udp6_output: IPV6_V6ONLY " 737249259Sdim "option was set for a connected socket\n"); 738249259Sdim error = EINVAL; 739249259Sdim goto release; 740249259Sdim } else 741249259Sdim af = AF_INET; 742249259Sdim } 743249259Sdim laddr = &inp->in6p_laddr; 744249259Sdim faddr = &inp->in6p_faddr; 745249259Sdim fport = inp->inp_fport; 746249259Sdim } 747249259Sdim 748249259Sdim if (af == AF_INET) 749249259Sdim hlen = sizeof(struct ip); 750249259Sdim 751249259Sdim /* 752249259Sdim * Calculate data length and get a mbuf 753249259Sdim * for UDP and IP6 headers. 754249259Sdim */ 755249259Sdim M_PREPEND(m, hlen + sizeof(struct udphdr), M_NOWAIT); 756249259Sdim if (m == 0) { 757249259Sdim error = ENOBUFS; 758249259Sdim goto release; 759249259Sdim } 760249259Sdim 761249259Sdim /* 762249259Sdim * Stuff checksum and output datagram. 763249259Sdim */ 764249259Sdim udp6 = (struct udphdr *)(mtod(m, caddr_t) + hlen); 765249259Sdim udp6->uh_sport = inp->inp_lport; /* lport is always set in the PCB */ 766249259Sdim udp6->uh_dport = fport; 767249259Sdim if (plen <= 0xffff) 768249259Sdim udp6->uh_ulen = htons((u_short)plen); 769249259Sdim else 770249259Sdim udp6->uh_ulen = 0; 771249259Sdim udp6->uh_sum = 0; 772249259Sdim 773249259Sdim switch (af) { 774249259Sdim case AF_INET6: 775249259Sdim ip6 = mtod(m, struct ip6_hdr *); 776249259Sdim ip6->ip6_flow = inp->inp_flow & IPV6_FLOWINFO_MASK; 777249259Sdim ip6->ip6_vfc &= ~IPV6_VERSION_MASK; 778249259Sdim ip6->ip6_vfc |= IPV6_VERSION; 779249259Sdim ip6->ip6_plen = htons((u_short)plen); 780249259Sdim ip6->ip6_nxt = IPPROTO_UDP; 781249259Sdim ip6->ip6_hlim = in6_selecthlim(inp, NULL); 782249259Sdim ip6->ip6_src = *laddr; 783249259Sdim ip6->ip6_dst = *faddr; 784249259Sdim 785249259Sdim udp6->uh_sum = in6_cksum_pseudo(ip6, plen, IPPROTO_UDP, 0); 786249259Sdim m->m_pkthdr.csum_flags = CSUM_UDP_IPV6; 787249259Sdim m->m_pkthdr.csum_data = offsetof(struct udphdr, uh_sum); 788249259Sdim 789249259Sdim flags = 0; 790249259Sdim 791249259Sdim UDP_PROBE(send, NULL, inp, ip6, inp, udp6); 792249259Sdim UDPSTAT_INC(udps_opackets); 793249259Sdim error = ip6_output(m, optp, NULL, flags, inp->in6p_moptions, 794249259Sdim NULL, inp); 795249259Sdim break; 796249259Sdim case AF_INET: 797249259Sdim error = EAFNOSUPPORT; 798249259Sdim goto release; 799249259Sdim } 800249259Sdim goto releaseopt; 801249259Sdim 802249259Sdimrelease: 803249259Sdim m_freem(m); 804249259Sdim 805249259Sdimreleaseopt: 806249259Sdim if (control) { 807249259Sdim ip6_clearpktopts(&opt, -1); 808249259Sdim m_freem(control); 809249259Sdim } 810249259Sdim return (error); 811249259Sdim} 812249259Sdim 813249259Sdimstatic void 814249259Sdimudp6_abort(struct socket *so) 815249259Sdim{ 816249259Sdim struct inpcb *inp; 817249259Sdim 818249259Sdim inp = sotoinpcb(so); 819249259Sdim KASSERT(inp != NULL, ("udp6_abort: inp == NULL")); 820249259Sdim 821249259Sdim#ifdef INET 822249259Sdim if (inp->inp_vflag & INP_IPV4) { 823249259Sdim struct pr_usrreqs *pru; 824249259Sdim 825249259Sdim pru = inetsw[ip_protox[IPPROTO_UDP]].pr_usrreqs; 826249259Sdim (*pru->pru_abort)(so); 827249259Sdim return; 828249259Sdim } 829249259Sdim#endif 830249259Sdim 831249259Sdim INP_WLOCK(inp); 832249259Sdim if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) { 833249259Sdim INP_HASH_WLOCK(&V_udbinfo); 834249259Sdim in6_pcbdisconnect(inp); 835249259Sdim inp->in6p_laddr = in6addr_any; 836249259Sdim INP_HASH_WUNLOCK(&V_udbinfo); 837249259Sdim soisdisconnected(so); 838249259Sdim } 839249259Sdim INP_WUNLOCK(inp); 840249259Sdim} 841249259Sdim 842249259Sdimstatic int 843249259Sdimudp6_attach(struct socket *so, int proto, struct thread *td) 844249259Sdim{ 845249259Sdim struct inpcb *inp; 846249259Sdim int error; 847249259Sdim 848249259Sdim inp = sotoinpcb(so); 849249259Sdim KASSERT(inp == NULL, ("udp6_attach: inp != NULL")); 850249259Sdim 851249259Sdim if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) { 852249259Sdim error = soreserve(so, udp_sendspace, udp_recvspace); 853249259Sdim if (error) 854249259Sdim return (error); 855249259Sdim } 856249259Sdim INP_INFO_WLOCK(&V_udbinfo); 857249259Sdim error = in_pcballoc(so, &V_udbinfo); 858249259Sdim if (error) { 859249259Sdim INP_INFO_WUNLOCK(&V_udbinfo); 860249259Sdim return (error); 861249259Sdim } 862249259Sdim inp = (struct inpcb *)so->so_pcb; 863249259Sdim inp->inp_vflag |= INP_IPV6; 864249259Sdim if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) 865249259Sdim inp->inp_vflag |= INP_IPV4; 866249259Sdim inp->in6p_hops = -1; /* use kernel default */ 867249259Sdim inp->in6p_cksum = -1; /* just to be sure */ 868249259Sdim /* 869249259Sdim * XXX: ugly!! 870249259Sdim * IPv4 TTL initialization is necessary for an IPv6 socket as well, 871249259Sdim * because the socket may be bound to an IPv6 wildcard address, 872249259Sdim * which may match an IPv4-mapped IPv6 address. 873249259Sdim */ 874249259Sdim inp->inp_ip_ttl = V_ip_defttl; 875249259Sdim 876249259Sdim error = udp_newudpcb(inp); 877249259Sdim if (error) { 878249259Sdim in_pcbdetach(inp); 879249259Sdim in_pcbfree(inp); 880249259Sdim INP_INFO_WUNLOCK(&V_udbinfo); 881249259Sdim return (error); 882249259Sdim } 883249259Sdim INP_WUNLOCK(inp); 884249259Sdim INP_INFO_WUNLOCK(&V_udbinfo); 885249259Sdim return (0); 886249259Sdim} 887249259Sdim 888249259Sdimstatic int 889249259Sdimudp6_bind(struct socket *so, struct sockaddr *nam, struct thread *td) 890249259Sdim{ 891249259Sdim struct inpcb *inp; 892249259Sdim int error; 893249259Sdim 894249259Sdim inp = sotoinpcb(so); 895249259Sdim KASSERT(inp != NULL, ("udp6_bind: inp == NULL")); 896249259Sdim 897249259Sdim INP_WLOCK(inp); 898249259Sdim INP_HASH_WLOCK(&V_udbinfo); 899249259Sdim inp->inp_vflag &= ~INP_IPV4; 900249259Sdim inp->inp_vflag |= INP_IPV6; 901249259Sdim if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) { 902249259Sdim struct sockaddr_in6 *sin6_p; 903249259Sdim 904249259Sdim sin6_p = (struct sockaddr_in6 *)nam; 905249259Sdim 906249259Sdim if (IN6_IS_ADDR_UNSPECIFIED(&sin6_p->sin6_addr)) 907249259Sdim inp->inp_vflag |= INP_IPV4; 908249259Sdim#ifdef INET 909249259Sdim else if (IN6_IS_ADDR_V4MAPPED(&sin6_p->sin6_addr)) { 910249259Sdim struct sockaddr_in sin; 911249259Sdim 912249259Sdim in6_sin6_2_sin(&sin, sin6_p); 913249259Sdim inp->inp_vflag |= INP_IPV4; 914249259Sdim inp->inp_vflag &= ~INP_IPV6; 915249259Sdim error = in_pcbbind(inp, (struct sockaddr *)&sin, 916249259Sdim td->td_ucred); 917249259Sdim goto out; 918249259Sdim } 919249259Sdim#endif 920249259Sdim } 921249259Sdim 922249259Sdim error = in6_pcbbind(inp, nam, td->td_ucred); 923249259Sdim#ifdef INET 924249259Sdimout: 925249259Sdim#endif 926249259Sdim INP_HASH_WUNLOCK(&V_udbinfo); 927249259Sdim INP_WUNLOCK(inp); 928249259Sdim return (error); 929249259Sdim} 930249259Sdim 931249259Sdimstatic void 932249259Sdimudp6_close(struct socket *so) 933249259Sdim{ 934249259Sdim struct inpcb *inp; 935249259Sdim 936249259Sdim inp = sotoinpcb(so); 937249259Sdim KASSERT(inp != NULL, ("udp6_close: inp == NULL")); 938249259Sdim 939249259Sdim#ifdef INET 940249259Sdim if (inp->inp_vflag & INP_IPV4) { 941249259Sdim struct pr_usrreqs *pru; 942249259Sdim 943249259Sdim pru = inetsw[ip_protox[IPPROTO_UDP]].pr_usrreqs; 944249259Sdim (*pru->pru_disconnect)(so); 945249259Sdim return; 946249259Sdim } 947249259Sdim#endif 948249259Sdim INP_WLOCK(inp); 949249259Sdim if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) { 950249259Sdim INP_HASH_WLOCK(&V_udbinfo); 951249259Sdim in6_pcbdisconnect(inp); 952249259Sdim inp->in6p_laddr = in6addr_any; 953249259Sdim INP_HASH_WUNLOCK(&V_udbinfo); 954249259Sdim soisdisconnected(so); 955249259Sdim } 956249259Sdim INP_WUNLOCK(inp); 957249259Sdim} 958249259Sdim 959249259Sdimstatic int 960249259Sdimudp6_connect(struct socket *so, struct sockaddr *nam, struct thread *td) 961249259Sdim{ 962249259Sdim struct inpcb *inp; 963249259Sdim struct sockaddr_in6 *sin6; 964249259Sdim int error; 965249259Sdim 966249259Sdim inp = sotoinpcb(so); 967249259Sdim sin6 = (struct sockaddr_in6 *)nam; 968249259Sdim KASSERT(inp != NULL, ("udp6_connect: inp == NULL")); 969249259Sdim 970249259Sdim /* 971249259Sdim * XXXRW: Need to clarify locking of v4/v6 flags. 972249259Sdim */ 973249259Sdim INP_WLOCK(inp); 974249259Sdim#ifdef INET 975249259Sdim if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { 976249259Sdim struct sockaddr_in sin; 977249259Sdim 978249259Sdim if ((inp->inp_flags & IN6P_IPV6_V6ONLY) != 0) { 979249259Sdim error = EINVAL; 980249259Sdim goto out; 981249259Sdim } 982249259Sdim if (inp->inp_faddr.s_addr != INADDR_ANY) { 983249259Sdim error = EISCONN; 984249259Sdim goto out; 985249259Sdim } 986249259Sdim in6_sin6_2_sin(&sin, sin6); 987249259Sdim inp->inp_vflag |= INP_IPV4; 988249259Sdim inp->inp_vflag &= ~INP_IPV6; 989249259Sdim error = prison_remote_ip4(td->td_ucred, &sin.sin_addr); 990249259Sdim if (error != 0) 991249259Sdim goto out; 992249259Sdim INP_HASH_WLOCK(&V_udbinfo); 993249259Sdim error = in_pcbconnect(inp, (struct sockaddr *)&sin, 994249259Sdim td->td_ucred); 995249259Sdim INP_HASH_WUNLOCK(&V_udbinfo); 996249259Sdim if (error == 0) 997249259Sdim soisconnected(so); 998249259Sdim goto out; 999249259Sdim } 1000249259Sdim#endif 1001249259Sdim if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) { 1002249259Sdim error = EISCONN; 1003249259Sdim goto out; 1004249259Sdim } 1005249259Sdim inp->inp_vflag &= ~INP_IPV4; 1006249259Sdim inp->inp_vflag |= INP_IPV6; 1007249259Sdim error = prison_remote_ip6(td->td_ucred, &sin6->sin6_addr); 1008249259Sdim if (error != 0) 1009249259Sdim goto out; 1010249259Sdim INP_HASH_WLOCK(&V_udbinfo); 1011249259Sdim error = in6_pcbconnect(inp, nam, td->td_ucred); 1012249259Sdim INP_HASH_WUNLOCK(&V_udbinfo); 1013249259Sdim if (error == 0) 1014249259Sdim soisconnected(so); 1015249259Sdimout: 1016249259Sdim INP_WUNLOCK(inp); 1017249259Sdim return (error); 1018249259Sdim} 1019249259Sdim 1020249259Sdimstatic void 1021249259Sdimudp6_detach(struct socket *so) 1022249259Sdim{ 1023249259Sdim struct inpcb *inp; 1024249259Sdim struct udpcb *up; 1025249259Sdim 1026249259Sdim inp = sotoinpcb(so); 1027249259Sdim KASSERT(inp != NULL, ("udp6_detach: inp == NULL")); 1028249259Sdim 1029249259Sdim INP_INFO_WLOCK(&V_udbinfo); 1030249259Sdim INP_WLOCK(inp); 1031249259Sdim up = intoudpcb(inp); 1032249259Sdim KASSERT(up != NULL, ("%s: up == NULL", __func__)); 1033249259Sdim in_pcbdetach(inp); 1034249259Sdim in_pcbfree(inp); 1035249259Sdim INP_INFO_WUNLOCK(&V_udbinfo); 1036249259Sdim udp_discardcb(up); 1037249259Sdim} 1038249259Sdim 1039249259Sdimstatic int 1040249259Sdimudp6_disconnect(struct socket *so) 1041249259Sdim{ 1042249259Sdim struct inpcb *inp; 1043249259Sdim int error; 1044249259Sdim 1045249259Sdim inp = sotoinpcb(so); 1046249259Sdim KASSERT(inp != NULL, ("udp6_disconnect: inp == NULL")); 1047249259Sdim 1048249259Sdim#ifdef INET 1049249259Sdim if (inp->inp_vflag & INP_IPV4) { 1050249259Sdim struct pr_usrreqs *pru; 1051249259Sdim 1052249259Sdim pru = inetsw[ip_protox[IPPROTO_UDP]].pr_usrreqs; 1053249259Sdim (void)(*pru->pru_disconnect)(so); 1054249259Sdim return (0); 1055249259Sdim } 1056249259Sdim#endif 1057249259Sdim 1058249259Sdim INP_WLOCK(inp); 1059249259Sdim 1060249259Sdim if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) { 1061249259Sdim error = ENOTCONN; 1062249259Sdim goto out; 1063249259Sdim } 1064249259Sdim 1065249259Sdim INP_HASH_WLOCK(&V_udbinfo); 1066249259Sdim in6_pcbdisconnect(inp); 1067249259Sdim inp->in6p_laddr = in6addr_any; 1068249259Sdim INP_HASH_WUNLOCK(&V_udbinfo); 1069249259Sdim SOCK_LOCK(so); 1070249259Sdim so->so_state &= ~SS_ISCONNECTED; /* XXX */ 1071249259Sdim SOCK_UNLOCK(so); 1072249259Sdimout: 1073249259Sdim INP_WUNLOCK(inp); 1074249259Sdim return (0); 1075249259Sdim} 1076249259Sdim 1077249259Sdimstatic int 1078249259Sdimudp6_send(struct socket *so, int flags, struct mbuf *m, 1079249259Sdim struct sockaddr *addr, struct mbuf *control, struct thread *td) 1080249259Sdim{ 1081249259Sdim struct inpcb *inp; 1082249259Sdim int error = 0; 1083249259Sdim 1084249259Sdim inp = sotoinpcb(so); 1085249259Sdim KASSERT(inp != NULL, ("udp6_send: inp == NULL")); 1086249259Sdim 1087249259Sdim INP_WLOCK(inp); 1088249259Sdim if (addr) { 1089249259Sdim if (addr->sa_len != sizeof(struct sockaddr_in6)) { 1090249259Sdim error = EINVAL; 1091249259Sdim goto bad; 1092249259Sdim } 1093249259Sdim if (addr->sa_family != AF_INET6) { 1094249259Sdim error = EAFNOSUPPORT; 1095249259Sdim goto bad; 1096249259Sdim } 1097249259Sdim } 1098249259Sdim 1099249259Sdim#ifdef INET 1100249259Sdim if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) { 1101249259Sdim int hasv4addr; 1102249259Sdim struct sockaddr_in6 *sin6 = 0; 1103249259Sdim 1104249259Sdim if (addr == 0) 1105249259Sdim hasv4addr = (inp->inp_vflag & INP_IPV4); 1106249259Sdim else { 1107249259Sdim sin6 = (struct sockaddr_in6 *)addr; 1108249259Sdim hasv4addr = IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr) 1109249259Sdim ? 1 : 0; 1110249259Sdim } 1111249259Sdim if (hasv4addr) { 1112249259Sdim struct pr_usrreqs *pru; 1113249259Sdim 1114249259Sdim /* 1115249259Sdim * XXXRW: We release UDP-layer locks before calling 1116249259Sdim * udp_send() in order to avoid recursion. However, 1117249259Sdim * this does mean there is a short window where inp's 1118249259Sdim * fields are unstable. Could this lead to a 1119249259Sdim * potential race in which the factors causing us to 1120249259Sdim * select the UDPv4 output routine are invalidated? 1121249259Sdim */ 1122249259Sdim INP_WUNLOCK(inp); 1123249259Sdim if (sin6) 1124249259Sdim in6_sin6_2_sin_in_sock(addr); 1125249259Sdim pru = inetsw[ip_protox[IPPROTO_UDP]].pr_usrreqs; 1126249259Sdim /* addr will just be freed in sendit(). */ 1127249259Sdim return ((*pru->pru_send)(so, flags, m, addr, control, 1128249259Sdim td)); 1129249259Sdim } 1130249259Sdim } 1131249259Sdim#endif 1132249259Sdim#ifdef MAC 1133249259Sdim mac_inpcb_create_mbuf(inp, m); 1134249259Sdim#endif 1135249259Sdim INP_HASH_WLOCK(&V_udbinfo); 1136249259Sdim error = udp6_output(inp, m, addr, control, td); 1137249259Sdim INP_HASH_WUNLOCK(&V_udbinfo); 1138249259Sdim#ifdef INET 1139249259Sdim#endif 1140249259Sdim INP_WUNLOCK(inp); 1141249259Sdim return (error); 1142249259Sdim 1143249259Sdimbad: 1144249259Sdim INP_WUNLOCK(inp); 1145249259Sdim m_freem(m); 1146249259Sdim return (error); 1147249259Sdim} 1148249259Sdim 1149249259Sdimstruct pr_usrreqs udp6_usrreqs = { 1150249259Sdim .pru_abort = udp6_abort, 1151249259Sdim .pru_attach = udp6_attach, 1152249259Sdim .pru_bind = udp6_bind, 1153249259Sdim .pru_connect = udp6_connect, 1154249259Sdim .pru_control = in6_control, 1155249259Sdim .pru_detach = udp6_detach, 1156249259Sdim .pru_disconnect = udp6_disconnect, 1157249259Sdim .pru_peeraddr = in6_mapped_peeraddr, 1158249259Sdim .pru_send = udp6_send, 1159249259Sdim .pru_shutdown = udp_shutdown, 1160249259Sdim .pru_sockaddr = in6_mapped_sockaddr, 1161249259Sdim .pru_soreceive = soreceive_dgram, 1162249259Sdim .pru_sosend = sosend_dgram, 1163249259Sdim .pru_sosetlabel = in_pcbsosetlabel, 1164249259Sdim .pru_close = udp6_close 1165249259Sdim}; 1166249259Sdim