X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/d41d1dae2cd00cc08c7982087d1c445180cad9f5..143464d58d2bd6378e74eec636961ceb0d32fb91:/bsd/netinet6/raw_ip6.c diff --git a/bsd/netinet6/raw_ip6.c b/bsd/netinet6/raw_ip6.c index 3a665a2c4..09728f630 100644 --- a/bsd/netinet6/raw_ip6.c +++ b/bsd/netinet6/raw_ip6.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2010 Apple Inc. All rights reserved. + * Copyright (c) 2000-2013 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * @@ -93,6 +93,7 @@ #include #include #include +#include #include #include #include @@ -115,9 +116,7 @@ #include #include #include -#if ENABLE_DEFAULT_SCOPE #include -#endif #include #include @@ -127,9 +126,6 @@ extern int ipsec_bypass; #endif /*IPSEC*/ -#define satosin6(sa) ((struct sockaddr_in6 *)(sa)) -#define ifatoia6(ifa) ((struct in6_ifaddr *)(ifa)) - /* * Raw interface to IP6 protocol. */ @@ -149,29 +145,26 @@ struct rip6stat rip6stat; int rip6_input( struct mbuf **mp, - int *offp) + int *offp, + int proto) { struct mbuf *m = *mp; - register struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); - register struct inpcb *in6p; + struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); + struct inpcb *in6p; struct inpcb *last = 0; struct mbuf *opts = NULL; struct sockaddr_in6 rip6src; - int proto = ip6->ip6_nxt; + int ret; + struct ifnet *ifp = m->m_pkthdr.rcvif; - rip6stat.rip6s_ipackets++; + /* Expect 32-bit aligned data pointer on strict-align platforms */ + MBUF_STRICT_DATA_ALIGNMENT_CHECK_32(m); -#if defined(NFAITH) && 0 < NFAITH - if (faithprefix(&ip6->ip6_dst)) { - /* XXX send icmp6 host/port unreach? */ - m_freem(m); - return IPPROTO_DONE; - } -#endif + rip6stat.rip6s_ipackets++; init_sin6(&rip6src, m); /* general init */ - lck_rw_lock_shared(ripcbinfo.mtx); + lck_rw_lock_shared(ripcbinfo.ipi_lock); LIST_FOREACH(in6p, &ripcb, inp_list) { if ((in6p->in6p_vflag & INP_IPV6) == 0) continue; @@ -184,7 +177,15 @@ rip6_input( if (!IN6_IS_ADDR_UNSPECIFIED(&in6p->in6p_faddr) && !IN6_ARE_ADDR_EQUAL(&in6p->in6p_faddr, &ip6->ip6_src)) continue; - if (in6p->in6p_cksum != -1) { + + if (inp_restricted(in6p, ifp)) + continue; + + if (ifp != NULL && IFNET_IS_CELLULAR(ifp) && + (in6p->in6p_flags & INP_NO_IFT_CELLULAR)) + continue; + + if (proto == IPPROTO_ICMPV6 || in6p->in6p_cksum != -1) { rip6stat.rip6s_isum++; if (in6_cksum(m, ip6->ip6_nxt, *offp, m->m_pkthdr.len - *offp)) { @@ -206,11 +207,20 @@ rip6_input( } else #endif /*IPSEC*/ if (n) { - if (last->in6p_flags & IN6P_CONTROLOPTS || - last->in6p_socket->so_options & SO_TIMESTAMP) - ip6_savecontrol(last, &opts, ip6, n); + if ((last->in6p_flags & INP_CONTROLOPTS) != 0 || + (last->in6p_socket->so_options & SO_TIMESTAMP) != 0 || + (last->in6p_socket->so_options & SO_TIMESTAMP_MONOTONIC) != 0) { + ret = ip6_savecontrol(last, n, &opts); + if (ret != 0) { + m_freem(n); + m_freem(opts); + last = in6p; + continue; + } + } /* strip intermediate headers */ m_adj(n, *offp); + so_recv_data_stat(last->in6p_socket, m, 0); if (sbappendaddr(&last->in6p_socket->so_rcv, (struct sockaddr *)&rip6src, n, opts, NULL) == 0) { @@ -222,7 +232,7 @@ rip6_input( } last = in6p; } - lck_rw_done(ripcbinfo.mtx); + #if IPSEC /* * Check AH/ESP integrity. @@ -235,11 +245,21 @@ rip6_input( } else #endif /*IPSEC*/ if (last) { - if (last->in6p_flags & IN6P_CONTROLOPTS || - last->in6p_socket->so_options & SO_TIMESTAMP) - ip6_savecontrol(last, &opts, ip6, m); + if ((last->in6p_flags & INP_CONTROLOPTS) != 0 || + (last->in6p_socket->so_options & SO_TIMESTAMP) != 0 || + (last->in6p_socket->so_options & SO_TIMESTAMP_MONOTONIC) != 0) { + ret = ip6_savecontrol(last, m, &opts); + if (ret != 0) { + m_freem(m); + m_freem(opts); + ip6stat.ip6s_delivered--; + goto unlock; + } + + } /* strip intermediate headers */ m_adj(m, *offp); + so_recv_data_stat(last->in6p_socket, m, 0); if (sbappendaddr(&last->in6p_socket->so_rcv, (struct sockaddr *)&rip6src, m, opts, NULL) == 0) { rip6stat.rip6s_fullsock++; @@ -259,6 +279,10 @@ rip6_input( } ip6stat.ip6s_delivered--; } + +unlock: + lck_rw_done(ripcbinfo.ipi_lock); + return IPPROTO_DONE; } @@ -270,6 +294,7 @@ rip6_ctlinput( { struct ip6_hdr *ip6; struct mbuf *m; + void *cmdarg = NULL; int off = 0; struct ip6ctlparam *ip6cp = NULL; const struct sockaddr_in6 *sa6_src = NULL; @@ -294,6 +319,7 @@ rip6_ctlinput( m = ip6cp->ip6c_m; ip6 = ip6cp->ip6c_ip6; off = ip6cp->ip6c_off; + cmdarg = ip6cp->ip6c_cmdarg; sa6_src = ip6cp->ip6c_src; } else { m = NULL; @@ -302,7 +328,7 @@ rip6_ctlinput( } (void) in6_pcbnotify(&ripcbinfo, sa, 0, (const struct sockaddr *)sa6_src, - 0, cmd, notify); + 0, cmd, cmdarg, notify); } /* @@ -311,36 +337,50 @@ rip6_ctlinput( */ int rip6_output( - register struct mbuf *m, + struct mbuf *m, struct socket *so, struct sockaddr_in6 *dstsock, - struct mbuf *control) + struct mbuf *control, + int israw) { struct in6_addr *dst; struct ip6_hdr *ip6; struct inpcb *in6p; u_int plen = m->m_pkthdr.len; int error = 0; - struct ip6_pktopts opt, *optp = 0; + struct ip6_pktopts opt, *optp = NULL; + struct ip6_moptions *im6o = NULL; struct ifnet *oifp = NULL; int type = 0, code = 0; /* for ICMPv6 output statistics only */ - int priv = 0; -#if PKT_PRIORITY - mbuf_traffic_class_t mtc = MBUF_TC_NONE; -#endif /* PKT_PRIORITY */ + mbuf_svc_class_t msc = MBUF_SC_UNSPEC; + struct ip6_out_args ip6oa = + { IFSCOPE_NONE, { 0 }, IP6OAF_SELECT_SRCIF, 0 }; + int flags = IPV6_OUTARGS; in6p = sotoin6pcb(so); - priv = 0; - if (so->so_uid == 0) - priv = 1; + if (in6p == NULL || (in6p->inp_flags2 & INP2_WANT_FLOW_DIVERT)) { + error = (in6p == NULL ? EINVAL : EPROTOTYPE); + goto bad; + } + if (dstsock != NULL && IN6_IS_ADDR_V4MAPPED(&dstsock->sin6_addr)) { + error = EINVAL; + goto bad; + } + + if (in6p->inp_flags & INP_BOUND_IF) { + ip6oa.ip6oa_boundif = in6p->inp_boundifp->if_index; + ip6oa.ip6oa_flags |= IP6OAF_BOUND_IF; + } + if (in6p->inp_flags & INP_NO_IFT_CELLULAR) + ip6oa.ip6oa_flags |= IP6OAF_NO_CELLULAR; + dst = &dstsock->sin6_addr; if (control) { -#if PKT_PRIORITY - mtc = mbuf_traffic_class_from_control(control); -#endif /* PKT_PRIORITY */ + msc = mbuf_service_class_from_control(control); - if ((error = ip6_setpktoptions(control, &opt, priv, 0)) != 0) + if ((error = ip6_setpktopts(control, &opt, NULL, + SOCK_PROTO(so))) != 0) goto bad; optp = &opt; } else @@ -350,7 +390,7 @@ rip6_output( * For an ICMPv6 packet, we should know its type and code * to update statistics. */ - if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) { + if (SOCK_PROTO(so) == IPPROTO_ICMPV6) { struct icmp6_hdr *icmp6; if (m->m_len < sizeof(struct icmp6_hdr) && (m = m_pullup(m, sizeof(struct icmp6_hdr))) == NULL) { @@ -362,6 +402,15 @@ rip6_output( code = icmp6->icmp6_code; } + if (in6p->inp_flowhash == 0) + in6p->inp_flowhash = inp_calc_flowhash(in6p); + /* update flowinfo - RFC 6437 */ + if (in6p->inp_flow == 0 && in6p->in6p_flags & IN6P_AUTOFLOWLABEL) { + in6p->inp_flow &= ~IPV6_FLOWLABEL_MASK; + in6p->inp_flow |= + (htonl(in6p->inp_flowhash) & IPV6_FLOWLABEL_MASK); + } + M_PREPEND(m, sizeof(*ip6), M_WAIT); if (m == NULL) { error = ENOBUFS; @@ -374,6 +423,8 @@ rip6_output( */ ip6->ip6_dst = *dst; + im6o = in6p->in6p_moptions; + /* * If the scope of the destination is link-local, embed the interface * index in the address. @@ -382,7 +433,13 @@ rip6_output( */ if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) { struct in6_pktinfo *pi; + struct ifnet *im6o_multicast_ifp = NULL; + if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) && im6o != NULL) { + IM6O_LOCK(im6o); + im6o_multicast_ifp = im6o->im6o_multicast_ifp; + IM6O_UNLOCK(im6o); + } /* * XXX Boundary check is assumed to be already done in * ip6_setpktoptions(). @@ -391,10 +448,12 @@ rip6_output( if (optp && (pi = optp->ip6po_pktinfo) && pi->ipi6_ifindex) { ip6->ip6_dst.s6_addr16[1] = htons(pi->ipi6_ifindex); oifp = ifindex2ifnet[pi->ipi6_ifindex]; + if (oifp != NULL) + ifnet_reference(oifp); } else if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) && - in6p->in6p_moptions && - in6p->in6p_moptions->im6o_multicast_ifp) { - oifp = in6p->in6p_moptions->im6o_multicast_ifp; + im6o != NULL && im6o_multicast_ifp != NULL) { + oifp = im6o_multicast_ifp; + ifnet_reference(oifp); ip6->ip6_dst.s6_addr16[1] = htons(oifp->if_index); } else if (dstsock->sin6_scope_id) { /* @@ -421,14 +480,18 @@ rip6_output( struct in6_addr *in6a; struct in6_addr storage; u_short index = 0; - if ((in6a = in6_selectsrc(dstsock, optp, - in6p->in6p_moptions, - &in6p->in6p_route, - &in6p->in6p_laddr, - &storage, &error)) == 0) { + + if (israw != 0 && optp && optp->ip6po_pktinfo && !IN6_IS_ADDR_UNSPECIFIED(&optp->ip6po_pktinfo->ipi6_addr)) { + in6a = &optp->ip6po_pktinfo->ipi6_addr; + flags |= IPV6_FLAG_NOSRCIFSEL; + } else if ((in6a = in6_selectsrc(dstsock, optp, in6p, + &in6p->in6p_route, NULL, &storage, ip6oa.ip6oa_boundif, + &error)) == 0) { if (error == 0) error = EADDRNOTAVAIL; goto bad; + } else { + ip6oa.ip6oa_flags |= IP6OAF_BOUND_SRCADDR; } ip6->ip6_src = *in6a; if (in6p->in6p_route.ro_rt != NULL) { @@ -436,34 +499,37 @@ rip6_output( if (in6p->in6p_route.ro_rt->rt_ifp != NULL) index = in6p->in6p_route.ro_rt->rt_ifp->if_index; RT_UNLOCK(in6p->in6p_route.ro_rt); + if (oifp != NULL) + ifnet_release(oifp); ifnet_head_lock_shared(); if (index == 0 || if_index < index) { panic("bad if_index on interface from route"); } oifp = ifindex2ifnet[index]; + if (oifp != NULL) + ifnet_reference(oifp); ifnet_head_done(); } } ip6->ip6_flow = (ip6->ip6_flow & ~IPV6_FLOWINFO_MASK) | - (in6p->in6p_flowinfo & IPV6_FLOWINFO_MASK); + (in6p->inp_flow & IPV6_FLOWINFO_MASK); ip6->ip6_vfc = (ip6->ip6_vfc & ~IPV6_VERSION_MASK) | (IPV6_VERSION & IPV6_VERSION_MASK); /* ip6_plen will be filled in ip6_output, so not fill it here. */ ip6->ip6_nxt = in6p->in6p_ip6_nxt; ip6->ip6_hlim = in6_selecthlim(in6p, oifp); - if (so->so_proto->pr_protocol == IPPROTO_ICMPV6 || - in6p->in6p_cksum != -1) { + if (SOCK_PROTO(so) == IPPROTO_ICMPV6 || in6p->in6p_cksum != -1) { struct mbuf *n; int off; u_int16_t *p; /* compute checksum */ - if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) + if (SOCK_PROTO(so) == IPPROTO_ICMPV6) off = offsetof(struct icmp6_hdr, icmp6_cksum); else off = in6p->in6p_cksum; - if (plen < off + 1) { + if (plen < (unsigned int)(off + 1)) { error = EINVAL; goto bad; } @@ -476,7 +542,7 @@ rip6_output( } if (!n) goto bad; - p = (u_int16_t *)(mtod(n, caddr_t) + off); + p = (u_int16_t *)(void *)(mtod(n, caddr_t) + off); *p = 0; *p = in6_cksum(m, ip6->ip6_nxt, sizeof(*ip6), plen); } @@ -488,34 +554,68 @@ rip6_output( } #endif /*IPSEC*/ - if (in6p->in6p_route.ro_rt != NULL && - in6p->in6p_route.ro_rt->generation_id != route_generation) { - rtfree(in6p->in6p_route.ro_rt); - in6p->in6p_route.ro_rt = NULL; + if (ROUTE_UNUSABLE(&in6p->in6p_route)) + ROUTE_RELEASE(&in6p->in6p_route); + + if (oifp != NULL) { + ifnet_release(oifp); + oifp = NULL; } -#if PKT_PRIORITY - set_traffic_class(m, so, mtc); -#endif /* PKT_PRIORITY */ + set_packet_service_class(m, so, msc, PKT_SCF_IPV6); + m->m_pkthdr.pkt_flowsrc = FLOWSRC_INPCB; + m->m_pkthdr.pkt_flowid = in6p->inp_flowhash; + m->m_pkthdr.pkt_flags |= (PKTF_FLOW_ID | PKTF_FLOW_LOCALSRC | + PKTF_FLOW_RAWSOCK); + m->m_pkthdr.pkt_proto = in6p->in6p_ip6_nxt; + + if (im6o != NULL) + IM6O_ADDREF(im6o); + + error = ip6_output(m, optp, &in6p->in6p_route, flags, im6o, + &oifp, &ip6oa); + + if (im6o != NULL) + IM6O_REMREF(im6o); - error = ip6_output(m, optp, &in6p->in6p_route, 0, - in6p->in6p_moptions, &oifp, 0); + if (in6p->in6p_route.ro_rt != NULL) { + struct rtentry *rt = in6p->in6p_route.ro_rt; + struct ifnet *outif; + + if ((rt->rt_flags & RTF_MULTICAST) || + in6p->in6p_socket == NULL || + !(in6p->in6p_socket->so_state & SS_ISCONNECTED)) { + rt = NULL; /* unusable */ + } + /* + * Always discard the cached route for unconnected + * socket or if it is a multicast route. + */ + if (rt == NULL) + ROUTE_RELEASE(&in6p->in6p_route); + + /* + * If this is a connected socket and the destination + * route is not multicast, update outif with that of + * the route interface index used by IP. + */ + if (rt != NULL && + (outif = rt->rt_ifp) != in6p->in6p_last_outifp) + in6p->in6p_last_outifp = outif; + } else { + ROUTE_RELEASE(&in6p->in6p_route); + } -#if IFNET_ROUTE_REFCNT /* - * Always discard the cached route for unconnected socket - * or if it is a multicast route. + * If output interface was cellular, and this socket is denied + * access to it, generate an event. */ - if (in6p->in6p_route.ro_rt != NULL && - ((in6p->in6p_route.ro_rt->rt_flags & RTF_MULTICAST) || - in6p->in6p_socket == NULL || - in6p->in6p_socket->so_state != SS_ISCONNECTED)) { - rtfree(in6p->in6p_route.ro_rt); - in6p->in6p_route.ro_rt = NULL; - } -#endif /* IFNET_ROUTE_REFCNT */ + if (error != 0 && (ip6oa.ip6oa_retflags & IP6OARF_IFDENIED) && + (in6p->inp_flags & INP_NO_IFT_CELLULAR)) + soevent(in6p->inp_socket, (SO_FILT_HINT_LOCKED| + SO_FILT_HINT_IFDENIED)); - if (so->so_proto->pr_protocol == IPPROTO_ICMPV6) { + if (SOCK_PROTO(so) == IPPROTO_ICMPV6) { if (oifp) icmp6_ifoutstat_inc(oifp, type, code); icmp6stat.icp6s_outhist[type]++; @@ -524,25 +624,26 @@ rip6_output( goto freectl; - bad: - if (m) +bad: + if (m != NULL) m_freem(m); - freectl: - if (optp == &opt && optp->ip6po_rthdr && optp->ip6po_route.ro_rt) { - rtfree(optp->ip6po_route.ro_rt); - optp->ip6po_route.ro_rt = NULL; - } - if (control) { +freectl: + if (optp == &opt && optp->ip6po_rthdr) + ROUTE_RELEASE(&optp->ip6po_route); + + if (control != NULL) { if (optp == &opt) - ip6_clearpktopts(optp, 0, -1); + ip6_clearpktopts(optp, -1); m_freem(control); } + if (oifp != NULL) + ifnet_release(oifp); return(error); } #if IPFW2 -static void +__private_extern__ void load_ip6fw(void) { ip6_fw_init(); @@ -557,15 +658,17 @@ rip6_ctloutput( struct socket *so, struct sockopt *sopt) { - int error; + int error, optval; + /* Allow at this level */ if (sopt->sopt_level == IPPROTO_ICMPV6) /* * XXX: is it better to call icmp6_ctloutput() directly * from protosw? */ return(icmp6_ctloutput(so, sopt)); - else if (sopt->sopt_level != IPPROTO_IPV6) + else if (sopt->sopt_level != IPPROTO_IPV6 && + !(sopt->sopt_level == SOL_SOCKET && sopt->sopt_name == SO_FLUSH)) return (EINVAL); error = 0; @@ -594,8 +697,13 @@ rip6_ctloutput( case MRT6_PIM: #if MROUTING error = ip6_mrouter_get(so, sopt); +#else + error = ENOPROTOOPT; +#endif /* MROUTING */ + break; + case IPV6_CHECKSUM: + error = ip6_raw_ctloutput(so, sopt); break; -#endif default: error = ip6_ctloutput(so, sopt); break; @@ -627,8 +735,22 @@ rip6_ctloutput( case MRT6_PIM: #if MROUTING error = ip6_mrouter_set(so, sopt); - break; +#else + error = ENOPROTOOPT; #endif + break; + case IPV6_CHECKSUM: + error = ip6_raw_ctloutput(so, sopt); + break; + + case SO_FLUSH: + if ((error = sooptcopyin(sopt, &optval, sizeof (optval), + sizeof (optval))) != 0) + break; + + error = inp_flush(sotoinpcb(so), optval); + break; + default: error = ip6_ctloutput(so, sopt); break; @@ -710,50 +832,72 @@ rip6_disconnect(struct socket *so) } static int -rip6_bind(struct socket *so, struct sockaddr *nam, __unused struct proc *p) +rip6_bind(struct socket *so, struct sockaddr *nam, struct proc *p) { +#pragma unused(p) struct inpcb *inp = sotoinpcb(so); - struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam; - struct ifaddr *ia = NULL; + struct sockaddr_in6 sin6; + struct ifaddr *ifa = NULL; + struct ifnet *outif = NULL; + int error; - if (nam->sa_len != sizeof(*addr)) - return EINVAL; + if (inp == NULL || (inp->inp_flags2 & INP2_WANT_FLOW_DIVERT)) + return (inp == NULL ? EINVAL : EPROTOTYPE); - if (TAILQ_EMPTY(&ifnet_head) || addr->sin6_family != AF_INET6) - return EADDRNOTAVAIL; -#if ENABLE_DEFAULT_SCOPE - if (addr->sin6_scope_id == 0) { /* not change if specified */ - addr->sin6_scope_id = scope6_addr2default(&addr->sin6_addr); - } -#endif - if (!IN6_IS_ADDR_UNSPECIFIED(&addr->sin6_addr) && - (ia = ifa_ifwithaddr((struct sockaddr *)addr)) == 0) - return EADDRNOTAVAIL; - if (ia && - ((struct in6_ifaddr *)ia)->ia6_flags & - (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY| - IN6_IFF_DETACHED|IN6_IFF_DEPRECATED)) { - if (ia) ifafree(ia); - return(EADDRNOTAVAIL); + if (nam->sa_len != sizeof (struct sockaddr_in6)) + return (EINVAL); + + if (TAILQ_EMPTY(&ifnet_head) || SIN6(nam)->sin6_family != AF_INET6) + return (EADDRNOTAVAIL); + + bzero(&sin6, sizeof (sin6)); + *(&sin6) = *SIN6(nam); + + if ((error = sa6_embedscope(&sin6, ip6_use_defzone)) != 0) + return (error); + + /* Sanitize local copy for address searches */ + sin6.sin6_flowinfo = 0; + sin6.sin6_scope_id = 0; + sin6.sin6_port = 0; + + if (!IN6_IS_ADDR_UNSPECIFIED(&sin6.sin6_addr) && + (ifa = ifa_ifwithaddr(SA(&sin6))) == 0) + return (EADDRNOTAVAIL); + if (ifa != NULL) { + IFA_LOCK(ifa); + if (((struct in6_ifaddr *)ifa)->ia6_flags & + (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY| + IN6_IFF_DETACHED|IN6_IFF_DEPRECATED)) { + IFA_UNLOCK(ifa); + IFA_REMREF(ifa); + return (EADDRNOTAVAIL); + } + outif = ifa->ifa_ifp; + IFA_UNLOCK(ifa); + IFA_REMREF(ifa); } - if (ia != NULL) - ifafree(ia); - inp->in6p_laddr = addr->sin6_addr; - return 0; + inp->in6p_laddr = sin6.sin6_addr; + inp->in6p_last_outifp = outif; + return (0); } static int rip6_connect(struct socket *so, struct sockaddr *nam, __unused struct proc *p) { struct inpcb *inp = sotoinpcb(so); - struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam; + struct sockaddr_in6 *addr = (struct sockaddr_in6 *)(void *)nam; struct in6_addr *in6a = NULL; struct in6_addr storage; int error = 0; #if ENABLE_DEFAULT_SCOPE struct sockaddr_in6 tmp; #endif + unsigned int ifscope; + struct ifnet *outif = NULL; + if (inp == NULL || (inp->inp_flags2 & INP2_WANT_FLOW_DIVERT)) + return (inp == NULL ? EINVAL : EPROTOTYPE); if (nam->sa_len != sizeof(*addr)) return EINVAL; if (TAILQ_EMPTY(&ifnet_head)) @@ -768,14 +912,20 @@ rip6_connect(struct socket *so, struct sockaddr *nam, __unused struct proc *p) addr->sin6_scope_id = scope6_addr2default(&addr->sin6_addr); } #endif + + ifscope = (inp->inp_flags & INP_BOUND_IF) ? + inp->inp_boundifp->if_index : IFSCOPE_NONE; + /* Source address selection. XXX: need pcblookup? */ - in6a = in6_selectsrc(addr, inp->in6p_outputopts, - inp->in6p_moptions, &inp->in6p_route, - &inp->in6p_laddr, &storage, &error); + in6a = in6_selectsrc(addr, inp->in6p_outputopts, inp, &inp->in6p_route, + NULL, &storage, ifscope, &error); if (in6a == NULL) return (error ? error : EADDRNOTAVAIL); inp->in6p_laddr = *in6a; inp->in6p_faddr = addr->sin6_addr; + if (inp->in6p_route.ro_rt != NULL) + outif = inp->in6p_route.ro_rt->rt_ifp; + inp->in6p_last_outifp = outif; soisconnected(so); return 0; } @@ -788,18 +938,25 @@ rip6_shutdown(struct socket *so) } static int -rip6_send(struct socket *so, __unused int flags, struct mbuf *m, struct sockaddr *nam, - struct mbuf *control, __unused struct proc *p) +rip6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, + struct mbuf *control, struct proc *p) { +#pragma unused(flags, p) struct inpcb *inp = sotoinpcb(so); struct sockaddr_in6 tmp; - struct sockaddr_in6 *dst; + struct sockaddr_in6 *dst = (struct sockaddr_in6 *)(void *)nam; + int error = 0; + + if (inp == NULL || (inp->inp_flags2 & INP2_WANT_FLOW_DIVERT)) { + error = (inp == NULL ? EINVAL : EPROTOTYPE); + goto bad; + } /* always copy sockaddr to avoid overwrites */ if (so->so_state & SS_ISCONNECTED) { - if (nam) { - m_freem(m); - return EISCONN; + if (nam != NULL) { + error = EISCONN; + goto bad; } /* XXX */ bzero(&tmp, sizeof(tmp)); @@ -810,10 +967,10 @@ rip6_send(struct socket *so, __unused int flags, struct mbuf *m, struct sockaddr dst = &tmp; } else { if (nam == NULL) { - m_freem(m); - return ENOTCONN; + error = ENOTCONN; + goto bad; } - tmp = *(struct sockaddr_in6 *)nam; + tmp = *(struct sockaddr_in6 *)(void *)nam; dst = &tmp; } #if ENABLE_DEFAULT_SCOPE @@ -821,24 +978,47 @@ rip6_send(struct socket *so, __unused int flags, struct mbuf *m, struct sockaddr dst->sin6_scope_id = scope6_addr2default(&dst->sin6_addr); } #endif - return rip6_output(m, so, dst, control); + return (rip6_output(m, so, dst, control, 1)); + +bad: + VERIFY(error != 0); + + if (m != NULL) + m_freem(m); + if (control != NULL) + m_freem(control); + + return (error); } struct pr_usrreqs rip6_usrreqs = { - rip6_abort, pru_accept_notsupp, rip6_attach, rip6_bind, rip6_connect, - pru_connect2_notsupp, in6_control, rip6_detach, rip6_disconnect, - pru_listen_notsupp, in6_setpeeraddr, pru_rcvd_notsupp, - pru_rcvoob_notsupp, rip6_send, pru_sense_null, rip6_shutdown, - in6_setsockaddr, sosend, soreceive, pru_sopoll_notsupp + .pru_abort = rip6_abort, + .pru_attach = rip6_attach, + .pru_bind = rip6_bind, + .pru_connect = rip6_connect, + .pru_control = in6_control, + .pru_detach = rip6_detach, + .pru_disconnect = rip6_disconnect, + .pru_peeraddr = in6_getpeeraddr, + .pru_send = rip6_send, + .pru_shutdown = rip6_shutdown, + .pru_sockaddr = in6_getsockaddr, + .pru_sosend = sosend, + .pru_soreceive = soreceive, }; __private_extern__ struct pr_usrreqs icmp6_dgram_usrreqs = { - rip6_abort, pru_accept_notsupp, icmp6_dgram_attach, rip6_bind, rip6_connect, - pru_connect2_notsupp, in6_control, rip6_detach, rip6_disconnect, - pru_listen_notsupp, in6_setpeeraddr, pru_rcvd_notsupp, - pru_rcvoob_notsupp, icmp6_dgram_send, pru_sense_null, rip6_shutdown, - in6_setsockaddr, sosend, soreceive, pru_sopoll_notsupp + .pru_abort = rip6_abort, + .pru_attach = icmp6_dgram_attach, + .pru_bind = rip6_bind, + .pru_connect = rip6_connect, + .pru_control = in6_control, + .pru_detach = rip6_detach, + .pru_disconnect = rip6_disconnect, + .pru_peeraddr = in6_getpeeraddr, + .pru_send = icmp6_dgram_send, + .pru_shutdown = rip6_shutdown, + .pru_sockaddr = in6_getsockaddr, + .pru_sosend = sosend, + .pru_soreceive = soreceive, }; - - -