static void tcp6_usr_bind(netmsg_t msg) { struct socket *so = msg->bind.base.nm_so; struct sockaddr *nam = msg->bind.nm_nam; struct thread *td = msg->bind.nm_td; int error = 0; struct inpcb *inp; struct tcpcb *tp; struct sockaddr_in6 *sin6p; COMMON_START(so, inp, 0); /* * Must check for multicast addresses and disallow binding * to them. */ sin6p = (struct sockaddr_in6 *)nam; if (sin6p->sin6_family == AF_INET6 && IN6_IS_ADDR_MULTICAST(&sin6p->sin6_addr)) { error = EAFNOSUPPORT; goto out; } error = in6_pcbbind(inp, nam, td); if (error) goto out; COMMON_END(PRU_BIND); }
static void udp6_bind(netmsg_t msg) { struct socket *so =msg->bind.base.nm_so; struct sockaddr *nam = msg->bind.nm_nam; struct thread *td = msg->bind.nm_td; struct sockaddr_in6 *sin6_p = (struct sockaddr_in6 *)nam; struct inpcb *inp; int error; inp = so->so_pcb; if (inp == NULL) { error = EINVAL; goto out; } error = in6_pcbbind(inp, nam, td); if (error == 0) { if (IN6_IS_ADDR_UNSPECIFIED(&sin6_p->sin6_addr)) inp->inp_flags |= INP_WASBOUND_NOTANY; in_pcbinswildcardhash(inp); } out: lwkt_replymsg(&msg->bind.base.lmsg, error); }
static void tcp6_usr_listen(netmsg_t msg) { struct socket *so = msg->listen.base.nm_so; struct thread *td = msg->listen.nm_td; int error = 0; struct inpcb *inp; struct tcpcb *tp; #ifdef SMP struct netmsg_inswildcard nm; #endif COMMON_START(so, inp, 0); if (tp->t_flags & TF_LISTEN) goto out; if (inp->inp_lport == 0) { if (!(inp->inp_flags & IN6P_IPV6_V6ONLY)) inp->inp_vflag |= INP_IPV4; else inp->inp_vflag &= ~INP_IPV4; error = in6_pcbbind(inp, NULL, td); if (error) goto out; } tp->t_state = TCPS_LISTEN; tp->t_flags |= TF_LISTEN; tp->tt_msg = NULL; /* Catch any invalid timer usage */ #ifdef SMP if (ncpus > 1) { /* * We have to set the flag because we can't have other cpus * messing with our inp's flags. */ KASSERT(!(inp->inp_flags & INP_CONNECTED), ("already on connhash\n")); KASSERT(!(inp->inp_flags & INP_WILDCARD), ("already on wildcardhash\n")); KASSERT(!(inp->inp_flags & INP_WILDCARD_MP), ("already on MP wildcardhash\n")); inp->inp_flags |= INP_WILDCARD_MP; KKASSERT(so->so_port == cpu_portfn(0)); KKASSERT(&curthread->td_msgport == cpu_portfn(0)); KKASSERT(inp->inp_pcbinfo == &tcbinfo[0]); netmsg_init(&nm.base, NULL, &curthread->td_msgport, MSGF_PRIORITY, in_pcbinswildcardhash_handler); nm.nm_inp = inp; lwkt_domsg(cpu_portfn(1), &nm.base.lmsg, 0); } #endif in_pcbinswildcardhash(inp); COMMON_END(PRU_LISTEN); }
/* * Outer subroutine: * Connect from a socket to a specified address. * Both address and port must be specified in argument sin. * If don't have a local address for this socket yet, * then pick one. */ int in6_pcbconnect( struct inpcb *inp, struct sockaddr *nam, struct proc *p) { struct in6_addr addr6; struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)nam; struct inpcb *pcb; int error; unsigned int outif = 0; /* * Call inner routine, to assign local interface address. * in6_pcbladdr() may automatically fill in sin6_scope_id. */ if ((error = in6_pcbladdr(inp, nam, &addr6, &outif)) != 0) return(error); socket_unlock(inp->inp_socket, 0); pcb = in6_pcblookup_hash(inp->inp_pcbinfo, &sin6->sin6_addr, sin6->sin6_port, IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr) ? &addr6 : &inp->in6p_laddr, inp->inp_lport, 0, NULL); socket_lock(inp->inp_socket, 0); if (pcb != NULL) { in_pcb_checkstate(pcb, WNT_RELEASE, pcb == inp ? 1 : 0); return (EADDRINUSE); } if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) { if (inp->inp_lport == 0) { error = in6_pcbbind(inp, (struct sockaddr *)0, p); if (error) return (error); } inp->in6p_laddr = addr6; inp->in6p_last_outif = outif; } if (!lck_rw_try_lock_exclusive(inp->inp_pcbinfo->mtx)) { /*lock inversion issue, mostly with udp multicast packets */ socket_unlock(inp->inp_socket, 0); lck_rw_lock_exclusive(inp->inp_pcbinfo->mtx); socket_lock(inp->inp_socket, 0); } inp->in6p_faddr = sin6->sin6_addr; inp->inp_fport = sin6->sin6_port; /* update flowinfo - draft-itojun-ipv6-flowlabel-api-00 */ inp->in6p_flowinfo &= ~IPV6_FLOWLABEL_MASK; if (inp->in6p_flags & IN6P_AUTOFLOWLABEL) inp->in6p_flowinfo |= (htonl(ip6_flow_seq++) & IPV6_FLOWLABEL_MASK); in_pcbrehash(inp); lck_rw_done(inp->inp_pcbinfo->mtx); return (0); }
static int tcp6_usr_bind(struct socket *so, struct sockaddr *nam, struct thread *td) { int error = 0; struct inpcb *inp; struct tcpcb *tp = NULL; struct sockaddr_in6 *sin6p; sin6p = (struct sockaddr_in6 *)nam; if (nam->sa_len != sizeof (*sin6p)) return (EINVAL); /* * Must check for multicast addresses and disallow binding * to them. */ if (sin6p->sin6_family == AF_INET6 && IN6_IS_ADDR_MULTICAST(&sin6p->sin6_addr)) return (EAFNOSUPPORT); TCPDEBUG0; INP_INFO_WLOCK(&V_tcbinfo); inp = sotoinpcb(so); KASSERT(inp != NULL, ("tcp6_usr_bind: inp == NULL")); INP_WLOCK(inp); if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { error = EINVAL; goto out; } tp = intotcpcb(inp); TCPDEBUG1(); inp->inp_vflag &= ~INP_IPV4; inp->inp_vflag |= INP_IPV6; if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) { if (IN6_IS_ADDR_UNSPECIFIED(&sin6p->sin6_addr)) inp->inp_vflag |= INP_IPV4; else if (IN6_IS_ADDR_V4MAPPED(&sin6p->sin6_addr)) { struct sockaddr_in sin; in6_sin6_2_sin(&sin, sin6p); inp->inp_vflag |= INP_IPV4; inp->inp_vflag &= ~INP_IPV6; error = in_pcbbind(inp, (struct sockaddr *)&sin, td->td_ucred); goto out; } } error = in6_pcbbind(inp, nam, td->td_ucred); out: TCPDEBUG2(PRU_BIND); INP_WUNLOCK(inp); INP_INFO_WUNLOCK(&V_tcbinfo); return (error); }
static int udp6_bind(struct socket *so, struct sockaddr *nam, struct thread *td) { struct inpcb *inp; struct inpcbinfo *pcbinfo; int error; pcbinfo = udp_get_inpcbinfo(so->so_proto->pr_protocol); inp = sotoinpcb(so); KASSERT(inp != NULL, ("udp6_bind: inp == NULL")); INP_WLOCK(inp); INP_HASH_WLOCK(pcbinfo); inp->inp_vflag &= ~INP_IPV4; inp->inp_vflag |= INP_IPV6; if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) { struct sockaddr_in6 *sin6_p; sin6_p = (struct sockaddr_in6 *)nam; if (IN6_IS_ADDR_UNSPECIFIED(&sin6_p->sin6_addr)) inp->inp_vflag |= INP_IPV4; #ifdef INET else if (IN6_IS_ADDR_V4MAPPED(&sin6_p->sin6_addr)) { struct sockaddr_in sin; in6_sin6_2_sin(&sin, sin6_p); inp->inp_vflag |= INP_IPV4; inp->inp_vflag &= ~INP_IPV6; error = in_pcbbind(inp, (struct sockaddr *)&sin, td->td_ucred); goto out; } #endif } error = in6_pcbbind(inp, nam, td->td_ucred); #ifdef INET out: #endif INP_HASH_WUNLOCK(pcbinfo); INP_WUNLOCK(inp); return (error); }
static void tcp6_usr_bind(netmsg_t msg) { struct socket *so = msg->bind.base.nm_so; struct sockaddr *nam = msg->bind.nm_nam; struct thread *td = msg->bind.nm_td; int error = 0; struct inpcb *inp; struct tcpcb *tp; struct sockaddr_in6 *sin6p; COMMON_START(so, inp, 0); /* * Must check for multicast addresses and disallow binding * to them. */ sin6p = (struct sockaddr_in6 *)nam; if (sin6p->sin6_family == AF_INET6 && IN6_IS_ADDR_MULTICAST(&sin6p->sin6_addr)) { error = EAFNOSUPPORT; goto out; } inp->inp_vflag &= ~INP_IPV4; inp->inp_vflag |= INP_IPV6; if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) { if (IN6_IS_ADDR_UNSPECIFIED(&sin6p->sin6_addr)) inp->inp_vflag |= INP_IPV4; else if (IN6_IS_ADDR_V4MAPPED(&sin6p->sin6_addr)) { struct sockaddr_in sin; in6_sin6_2_sin(&sin, sin6p); inp->inp_vflag |= INP_IPV4; inp->inp_vflag &= ~INP_IPV6; error = in_pcbbind(inp, (struct sockaddr *)&sin, td); goto out; } } error = in6_pcbbind(inp, nam, td); if (error) goto out; COMMON_END(PRU_BIND); }
static void tcp6_usr_listen(netmsg_t msg) { struct socket *so = msg->listen.base.nm_so; struct thread *td = msg->listen.nm_td; int error = 0; struct inpcb *inp; struct tcpcb *tp; struct netmsg_inswildcard nm; COMMON_START(so, inp, 0); if (tp->t_flags & TF_LISTEN) goto out; if (inp->inp_lport == 0) { error = in6_pcbbind(inp, NULL, td); if (error) goto out; } tp->t_state = TCPS_LISTEN; tp->t_flags |= TF_LISTEN; tp->tt_msg = NULL; /* Catch any invalid timer usage */ if (ncpus2 > 1) { /* * Put this inpcb into wildcard hash on other cpus. */ KKASSERT(so->so_port == netisr_cpuport(0)); ASSERT_IN_NETISR(0); KKASSERT(inp->inp_pcbinfo == &tcbinfo[0]); ASSERT_INP_NOTINHASH(inp); netmsg_init(&nm.base, NULL, &curthread->td_msgport, MSGF_PRIORITY, in_pcbinswildcardhash_handler); nm.nm_inp = inp; lwkt_domsg(netisr_cpuport(1), &nm.base.lmsg, 0); } in_pcbinswildcardhash(inp); COMMON_END(PRU_LISTEN); }
static int tcp6_usr_listen(struct socket *so, struct proc *p) { int s = splnet(); int error = 0; struct inpcb *inp = sotoinpcb(so); struct tcpcb *tp; COMMON_START(); if (inp->inp_lport == 0) { inp->inp_vflag &= ~INP_IPV4; if (ip6_mapped_addr_on && (inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) inp->inp_vflag |= INP_IPV4; error = in6_pcbbind(inp, (struct sockaddr *)0, p); } if (error == 0) tp->t_state = TCPS_LISTEN; COMMON_END(PRU_LISTEN); }
static int tcp6_usr_bind(struct socket *so, struct sockaddr *nam, struct proc *p) { int s = splnet(); int error = 0; struct inpcb *inp = sotoinpcb(so); struct tcpcb *tp; struct sockaddr_in6 *sin6p; COMMON_START(); /* * Must check for multicast addresses and disallow binding * to them. */ sin6p = (struct sockaddr_in6 *)nam; if (sin6p->sin6_family == AF_INET6 && IN6_IS_ADDR_MULTICAST(&sin6p->sin6_addr)) { error = EAFNOSUPPORT; goto out; } inp->inp_vflag &= ~INP_IPV4; inp->inp_vflag |= INP_IPV6; if (ip6_mapped_addr_on && (inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) { if (IN6_IS_ADDR_UNSPECIFIED(&sin6p->sin6_addr)) inp->inp_vflag |= INP_IPV4; else if (IN6_IS_ADDR_V4MAPPED(&sin6p->sin6_addr)) { struct sockaddr_in sin; in6_sin6_2_sin(&sin, sin6p); inp->inp_vflag |= INP_IPV4; inp->inp_vflag &= ~INP_IPV6; error = in_pcbbind(inp, (struct sockaddr *)&sin, p); goto out; } } error = in6_pcbbind(inp, nam, p); if (error) goto out; COMMON_END(PRU_BIND); }
static int tcp6_usr_listen(struct socket *so, int backlog, struct thread *td) { int error = 0; struct inpcb *inp; struct tcpcb *tp = NULL; TCPDEBUG0; INP_INFO_WLOCK(&V_tcbinfo); inp = sotoinpcb(so); KASSERT(inp != NULL, ("tcp6_usr_listen: inp == NULL")); INP_WLOCK(inp); if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { error = EINVAL; goto out; } tp = intotcpcb(inp); TCPDEBUG1(); SOCK_LOCK(so); error = solisten_proto_check(so); if (error == 0 && inp->inp_lport == 0) { inp->inp_vflag &= ~INP_IPV4; if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) inp->inp_vflag |= INP_IPV4; error = in6_pcbbind(inp, (struct sockaddr *)0, td->td_ucred); } if (error == 0) { tp->t_state = TCPS_LISTEN; solisten_proto(so, backlog); } SOCK_UNLOCK(so); out: TCPDEBUG2(PRU_LISTEN); INP_WUNLOCK(inp); INP_INFO_WUNLOCK(&V_tcbinfo); return (error); }
static int tcp6_connect(struct tcpcb *tp, struct sockaddr *nam, struct thread *td) { struct inpcb *inp = tp->t_inpcb, *oinp; struct socket *so = inp->inp_socket; struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)nam; struct in6_addr addr6; int error; INP_INFO_WLOCK_ASSERT(&V_tcbinfo); INP_WLOCK_ASSERT(inp); if (inp->inp_lport == 0) { error = in6_pcbbind(inp, (struct sockaddr *)0, td->td_ucred); if (error) return error; } /* * Cannot simply call in_pcbconnect, because there might be an * earlier incarnation of this same connection still in * TIME_WAIT state, creating an ADDRINUSE error. * in6_pcbladdr() also handles scope zone IDs. */ error = in6_pcbladdr(inp, nam, &addr6); if (error) return error; oinp = in6_pcblookup_hash(inp->inp_pcbinfo, &sin6->sin6_addr, sin6->sin6_port, IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr) ? &addr6 : &inp->in6p_laddr, inp->inp_lport, 0, NULL); if (oinp) return EADDRINUSE; if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr)) inp->in6p_laddr = addr6; inp->in6p_faddr = sin6->sin6_addr; inp->inp_fport = sin6->sin6_port; /* update flowinfo - draft-itojun-ipv6-flowlabel-api-00 */ inp->inp_flow &= ~IPV6_FLOWLABEL_MASK; if (inp->inp_flags & IN6P_AUTOFLOWLABEL) inp->inp_flow |= (htonl(ip6_randomflowlabel()) & IPV6_FLOWLABEL_MASK); in_pcbrehash(inp); /* Compute window scaling to request. */ while (tp->request_r_scale < TCP_MAX_WINSHIFT && (TCP_MAXWIN << tp->request_r_scale) < sb_max) tp->request_r_scale++; soisconnecting(so); TCPSTAT_INC(tcps_connattempt); tp->t_state = TCPS_SYN_SENT; tcp_timer_activate(tp, TT_KEEP, tcp_keepinit); tp->iss = tcp_new_isn(tp); tp->t_bw_rtseq = tp->iss; tcp_sendseqinit(tp); return 0; }
/* * Connect from a socket to a specified address. * Both address and port must be specified in argument sin6. * Eventually, flow labels will have to be dealt with here, as well. * * If don't have a local address for this socket yet, * then pick one. * * I believe this has to be called at splnet(). */ int in6_pcbconnect(struct inpcb *inp, struct mbuf *nam) { struct in6_addr *in6a = NULL; struct sockaddr_in6 *sin6 = mtod(nam, struct sockaddr_in6 *); struct ifnet *ifp = NULL; /* outgoing interface */ int error = 0; struct sockaddr_in6 tmp; (void)&in6a; /* XXX fool gcc */ if (nam->m_len != sizeof(*sin6)) return (EINVAL); if (sin6->sin6_family != AF_INET6) return (EAFNOSUPPORT); if (sin6->sin6_port == 0) return (EADDRNOTAVAIL); /* reject IPv4 mapped address, we have no support for it */ if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) return EADDRNOTAVAIL; /* sanity check for mapped address case */ if (IN6_IS_ADDR_V4MAPPED(&inp->inp_laddr6)) return EINVAL; /* protect *sin6 from overwrites */ tmp = *sin6; sin6 = &tmp; /* KAME hack: embed scopeid */ if (in6_embedscope(&sin6->sin6_addr, sin6, inp, &ifp) != 0) return EINVAL; /* this must be cleared for ifa_ifwithaddr() */ sin6->sin6_scope_id = 0; /* Source address selection. */ /* * XXX: in6_selectsrc might replace the bound local address * with the address specified by setsockopt(IPV6_PKTINFO). * Is it the intended behavior? */ in6a = in6_selectsrc(sin6, inp->inp_outputopts6, inp->inp_moptions6, &inp->inp_route6, &inp->inp_laddr6, &error); if (in6a == 0) { if (error == 0) error = EADDRNOTAVAIL; return (error); } if (inp->inp_route6.ro_rt) ifp = inp->inp_route6.ro_rt->rt_ifp; inp->inp_ipv6.ip6_hlim = (u_int8_t)in6_selecthlim(inp, ifp); if (in_pcblookup(inp->inp_table, &sin6->sin6_addr, sin6->sin6_port, IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6) ? in6a : &inp->inp_laddr6, inp->inp_lport, INPLOOKUP_IPV6)) { return (EADDRINUSE); } if (IN6_IS_ADDR_UNSPECIFIED(&inp->inp_laddr6)) { if (inp->inp_lport == 0) (void)in6_pcbbind(inp, NULL, curproc); inp->inp_laddr6 = *in6a; } inp->inp_faddr6 = sin6->sin6_addr; inp->inp_fport = sin6->sin6_port; inp->inp_flowinfo &= ~IPV6_FLOWLABEL_MASK; if (ip6_auto_flowlabel) inp->inp_flowinfo |= (htonl(ip6_randomflowlabel()) & IPV6_FLOWLABEL_MASK); in_pcbrehash(inp); return (0); }
static void tcp6_connect(netmsg_t msg) { struct tcpcb *tp; struct socket *so = msg->connect.base.nm_so; struct sockaddr *nam = msg->connect.nm_nam; struct thread *td = msg->connect.nm_td; struct inpcb *inp; struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)nam; struct in6_addr *addr6; #ifdef SMP lwkt_port_t port; #endif int error; COMMON_START(so, inp, 0); /* * Reconnect our pcb if we have to */ if (msg->connect.nm_reconnect & NMSG_RECONNECT_RECONNECT) { msg->connect.nm_reconnect &= ~NMSG_RECONNECT_RECONNECT; in_pcblink(so->so_pcb, &tcbinfo[mycpu->gd_cpuid]); } /* * Bind if we have to */ if (inp->inp_lport == 0) { error = in6_pcbbind(inp, NULL, td); if (error) goto out; } /* * Cannot simply call in_pcbconnect, because there might be an * earlier incarnation of this same connection still in * TIME_WAIT state, creating an ADDRINUSE error. */ error = in6_pcbladdr(inp, nam, &addr6, td); if (error) goto out; #ifdef SMP port = tcp6_addrport(); /* XXX hack for now, always cpu0 */ if (port != &curthread->td_msgport) { struct route *ro = &inp->inp_route; /* * in_pcbladdr() may have allocated a route entry for us * on the current CPU, but we need a route entry on the * inpcb's owner CPU, so free it here. */ if (ro->ro_rt != NULL) RTFREE(ro->ro_rt); bzero(ro, sizeof(*ro)); in_pcbunlink(so->so_pcb, &tcbinfo[mycpu->gd_cpuid]); sosetport(so, port); msg->connect.nm_reconnect |= NMSG_RECONNECT_RECONNECT; msg->connect.base.nm_dispatch = tcp6_connect; lwkt_forwardmsg(port, &msg->connect.base.lmsg); /* msg invalid now */ return; } #endif error = tcp6_connect_oncpu(tp, msg->connect.nm_flags, &msg->connect.nm_m, sin6, addr6); /* nm_m may still be intact */ out: if (error && (msg->connect.nm_reconnect & NMSG_RECONNECT_FALLBACK)) { tcp_connect(msg); /* msg invalid now */ } else { if (msg->connect.nm_m) { m_freem(msg->connect.nm_m); msg->connect.nm_m = NULL; } if (msg->connect.nm_reconnect & NMSG_RECONNECT_NAMALLOC) { kfree(msg->connect.nm_nam, M_LWKTMSG); msg->connect.nm_nam = NULL; } lwkt_replymsg(&msg->connect.base.lmsg, error); /* msg invalid now */ } }
/* * Connect from a socket to a specified address. * Both address and port must be specified in argument sin6. * If don't have a local address for this socket yet, * then pick one. */ int in6_pcbconnect(void *v, struct mbuf *nam, struct lwp *l) { struct rtentry *rt; struct in6pcb *in6p = v; struct in6_addr *in6a = NULL; struct sockaddr_in6 *sin6 = mtod(nam, struct sockaddr_in6 *); struct ifnet *ifp = NULL; /* outgoing interface */ int error = 0; int scope_ambiguous = 0; #ifdef INET struct in6_addr mapped; #endif struct sockaddr_in6 tmp; (void)&in6a; /* XXX fool gcc */ if (in6p->in6p_af != AF_INET6) return (EINVAL); if (nam->m_len != sizeof(*sin6)) return (EINVAL); if (sin6->sin6_family != AF_INET6) return (EAFNOSUPPORT); if (sin6->sin6_port == 0) return (EADDRNOTAVAIL); if (sin6->sin6_scope_id == 0 && !ip6_use_defzone) scope_ambiguous = 1; if ((error = sa6_embedscope(sin6, ip6_use_defzone)) != 0) return(error); /* sanity check for mapped address case */ if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { if ((in6p->in6p_flags & IN6P_IPV6_V6ONLY) != 0) return EINVAL; if (IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr)) in6p->in6p_laddr.s6_addr16[5] = htons(0xffff); if (!IN6_IS_ADDR_V4MAPPED(&in6p->in6p_laddr)) return EINVAL; } else { if (IN6_IS_ADDR_V4MAPPED(&in6p->in6p_laddr)) return EINVAL; } /* protect *sin6 from overwrites */ tmp = *sin6; sin6 = &tmp; /* Source address selection. */ if (IN6_IS_ADDR_V4MAPPED(&in6p->in6p_laddr) && in6p->in6p_laddr.s6_addr32[3] == 0) { #ifdef INET struct sockaddr_in sin, *sinp; bzero(&sin, sizeof(sin)); sin.sin_len = sizeof(sin); sin.sin_family = AF_INET; bcopy(&sin6->sin6_addr.s6_addr32[3], &sin.sin_addr, sizeof(sin.sin_addr)); sinp = in_selectsrc(&sin, &in6p->in6p_route, in6p->in6p_socket->so_options, NULL, &error); if (sinp == 0) { if (error == 0) error = EADDRNOTAVAIL; return (error); } bzero(&mapped, sizeof(mapped)); mapped.s6_addr16[5] = htons(0xffff); bcopy(&sinp->sin_addr, &mapped.s6_addr32[3], sizeof(sinp->sin_addr)); in6a = &mapped; #else return EADDRNOTAVAIL; #endif } else { /* * XXX: in6_selectsrc might replace the bound local address * with the address specified by setsockopt(IPV6_PKTINFO). * Is it the intended behavior? */ in6a = in6_selectsrc(sin6, in6p->in6p_outputopts, in6p->in6p_moptions, &in6p->in6p_route, &in6p->in6p_laddr, &ifp, &error); if (ifp && scope_ambiguous && (error = in6_setscope(&sin6->sin6_addr, ifp, NULL)) != 0) { return(error); } if (in6a == 0) { if (error == 0) error = EADDRNOTAVAIL; return (error); } } if (ifp == NULL && (rt = rtcache_validate(&in6p->in6p_route)) != NULL) ifp = rt->rt_ifp; in6p->in6p_ip6.ip6_hlim = (u_int8_t)in6_selecthlim(in6p, ifp); if (in6_pcblookup_connect(in6p->in6p_table, &sin6->sin6_addr, sin6->sin6_port, IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr) ? in6a : &in6p->in6p_laddr, in6p->in6p_lport, 0)) return (EADDRINUSE); if (IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_laddr) || (IN6_IS_ADDR_V4MAPPED(&in6p->in6p_laddr) && in6p->in6p_laddr.s6_addr32[3] == 0)) { if (in6p->in6p_lport == 0) { error = in6_pcbbind(in6p, (struct mbuf *)0, l); if (error != 0) return error; } in6p->in6p_laddr = *in6a; } in6p->in6p_faddr = sin6->sin6_addr; in6p->in6p_fport = sin6->sin6_port; in6_pcbstate(in6p, IN6P_CONNECTED); in6p->in6p_flowinfo &= ~IPV6_FLOWLABEL_MASK; if (ip6_auto_flowlabel) in6p->in6p_flowinfo |= (htonl(ip6_randomflowlabel()) & IPV6_FLOWLABEL_MASK); #if defined(IPSEC) || defined(FAST_IPSEC) if (in6p->in6p_socket->so_type == SOCK_STREAM) ipsec_pcbconn(in6p->in6p_sp); #endif return (0); }