Changeset View
Changeset View
Standalone View
Standalone View
sys/netinet6/raw_ip6.c
Show First 20 Lines • Show All 113 Lines • ▼ Show 20 Lines | |||||
#define satosin6(sa) ((struct sockaddr_in6 *)(sa)) | #define satosin6(sa) ((struct sockaddr_in6 *)(sa)) | ||||
#define ifatoia6(ifa) ((struct in6_ifaddr *)(ifa)) | #define ifatoia6(ifa) ((struct in6_ifaddr *)(ifa)) | ||||
/* | /* | ||||
* Raw interface to IP6 protocol. | * Raw interface to IP6 protocol. | ||||
*/ | */ | ||||
VNET_DECLARE(struct inpcbhead, ripcb); | |||||
VNET_DECLARE(struct inpcbinfo, ripcbinfo); | VNET_DECLARE(struct inpcbinfo, ripcbinfo); | ||||
#define V_ripcb VNET(ripcb) | |||||
#define V_ripcbinfo VNET(ripcbinfo) | #define V_ripcbinfo VNET(ripcbinfo) | ||||
extern u_long rip_sendspace; | extern u_long rip_sendspace; | ||||
extern u_long rip_recvspace; | extern u_long rip_recvspace; | ||||
VNET_PCPUSTAT_DEFINE(struct rip6stat, rip6stat); | VNET_PCPUSTAT_DEFINE(struct rip6stat, rip6stat); | ||||
VNET_PCPUSTAT_SYSINIT(rip6stat); | VNET_PCPUSTAT_SYSINIT(rip6stat); | ||||
Show All 15 Lines | |||||
* The various mrouter functions. | * The various mrouter functions. | ||||
*/ | */ | ||||
int (*ip6_mrouter_set)(struct socket *, struct sockopt *); | int (*ip6_mrouter_set)(struct socket *, struct sockopt *); | ||||
int (*ip6_mrouter_get)(struct socket *, struct sockopt *); | int (*ip6_mrouter_get)(struct socket *, struct sockopt *); | ||||
int (*ip6_mrouter_done)(void); | int (*ip6_mrouter_done)(void); | ||||
int (*ip6_mforward)(struct ip6_hdr *, struct ifnet *, struct mbuf *); | int (*ip6_mforward)(struct ip6_hdr *, struct ifnet *, struct mbuf *); | ||||
int (*mrt6_ioctl)(u_long, caddr_t); | int (*mrt6_ioctl)(u_long, caddr_t); | ||||
struct rip6_inp_match_ctx { | |||||
struct ip6_hdr *ip6; | |||||
int proto; | |||||
}; | |||||
static bool | |||||
rip6_inp_match(const struct inpcb *inp, void *v) | |||||
{ | |||||
struct rip6_inp_match_ctx *c = v; | |||||
struct ip6_hdr *ip6 = c->ip6; | |||||
int proto = c->proto; | |||||
/* XXX inp locking */ | |||||
if ((inp->inp_vflag & INP_IPV6) == 0) | |||||
return (false); | |||||
if (inp->inp_ip_p && inp->inp_ip_p != proto) | |||||
return (false); | |||||
if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr) && | |||||
!IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr, &ip6->ip6_dst)) | |||||
return (false); | |||||
if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr) && | |||||
!IN6_ARE_ADDR_EQUAL(&inp->in6p_faddr, &ip6->ip6_src)) | |||||
return (false); | |||||
return (true); | |||||
} | |||||
/* | /* | ||||
* Setup generic address and protocol structures for raw_input routine, then | * Setup generic address and protocol structures for raw_input routine, then | ||||
* pass them along with mbuf chain. | * pass them along with mbuf chain. | ||||
*/ | */ | ||||
int | int | ||||
rip6_input(struct mbuf **mp, int *offp, int proto) | rip6_input(struct mbuf **mp, int *offp, int proto) | ||||
{ | { | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
struct mbuf *m = *mp; | struct mbuf *n, *m = *mp; | ||||
struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); | struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); | ||||
struct inpcb *inp; | struct inpcb *inp; | ||||
struct inpcb *last = NULL; | |||||
struct mbuf *opts = NULL; | struct mbuf *opts = NULL; | ||||
struct sockaddr_in6 fromsa; | struct sockaddr_in6 fromsa; | ||||
struct rip6_inp_match_ctx ctx = { .ip6 = ip6, .proto = proto }; | |||||
struct inpcb_iterator inpi = INP_ITERATOR(&V_ripcbinfo, | |||||
INPLOOKUP_RLOCKPCB, rip6_inp_match, &ctx); | |||||
int delivered = 0; | |||||
NET_EPOCH_ASSERT(); | NET_EPOCH_ASSERT(); | ||||
RIP6STAT_INC(rip6s_ipackets); | RIP6STAT_INC(rip6s_ipackets); | ||||
init_sin6(&fromsa, m, 0); /* general init */ | init_sin6(&fromsa, m, 0); /* general init */ | ||||
ifp = m->m_pkthdr.rcvif; | ifp = m->m_pkthdr.rcvif; | ||||
CK_LIST_FOREACH(inp, &V_ripcb, inp_list) { | while ((inp = inp_next(&inpi)) != NULL) { | ||||
/* XXX inp locking */ | INP_RLOCK_ASSERT(inp); | ||||
if ((inp->inp_vflag & INP_IPV6) == 0) | |||||
continue; | |||||
if (inp->inp_ip_p && | |||||
inp->inp_ip_p != proto) | |||||
continue; | |||||
if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr) && | |||||
!IN6_ARE_ADDR_EQUAL(&inp->in6p_laddr, &ip6->ip6_dst)) | |||||
continue; | |||||
if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr) && | |||||
!IN6_ARE_ADDR_EQUAL(&inp->in6p_faddr, &ip6->ip6_src)) | |||||
continue; | |||||
if (last != NULL) { | |||||
struct mbuf *n = m_copym(m, 0, M_COPYALL, M_NOWAIT); | |||||
#if defined(IPSEC) || defined(IPSEC_SUPPORT) | #if defined(IPSEC) || defined(IPSEC_SUPPORT) | ||||
/* | /* | ||||
* Check AH/ESP integrity. | * Check AH/ESP integrity. | ||||
*/ | */ | ||||
if (IPSEC_ENABLED(ipv6)) { | if (IPSEC_ENABLED(ipv6) && | ||||
if (n != NULL && | IPSEC_CHECK_POLICY(ipv6, m, inp) != 0) { | ||||
IPSEC_CHECK_POLICY(ipv6, n, last) != 0) { | |||||
m_freem(n); | |||||
/* Do not inject data into pcb. */ | /* Do not inject data into pcb. */ | ||||
n = NULL; | continue; | ||||
} | } | ||||
} | |||||
#endif /* IPSEC */ | #endif /* IPSEC */ | ||||
if (n) { | if (jailed_without_vnet(inp->inp_cred) && | ||||
if (last->inp_flags & INP_CONTROLOPTS || | !IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) && | ||||
last->inp_socket->so_options & SO_TIMESTAMP) | prison_check_ip6(inp->inp_cred, &ip6->ip6_dst) != 0) | ||||
ip6_savecontrol(last, n, &opts); | |||||
/* strip intermediate headers */ | |||||
m_adj(n, *offp); | |||||
if (sbappendaddr(&last->inp_socket->so_rcv, | |||||
(struct sockaddr *)&fromsa, | |||||
n, opts) == 0) { | |||||
soroverflow(last->inp_socket); | |||||
m_freem(n); | |||||
if (opts) | |||||
m_freem(opts); | |||||
RIP6STAT_INC(rip6s_fullsock); | |||||
} else | |||||
sorwakeup(last->inp_socket); | |||||
opts = NULL; | |||||
} | |||||
INP_RUNLOCK(last); | |||||
last = NULL; | |||||
} | |||||
INP_RLOCK(inp); | |||||
if (__predict_false(inp->inp_flags2 & INP_FREED)) | |||||
goto skip_2; | |||||
if (jailed_without_vnet(inp->inp_cred)) { | |||||
/* | /* | ||||
* Allow raw socket in jail to receive multicast; | * Allow raw socket in jail to receive multicast; | ||||
* assume process had PRIV_NETINET_RAW at attach, | * assume process had PRIV_NETINET_RAW at attach, | ||||
* and fall through into normal filter path if so. | * and fall through into normal filter path if so. | ||||
*/ | */ | ||||
if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) && | continue; | ||||
prison_check_ip6(inp->inp_cred, | |||||
&ip6->ip6_dst) != 0) | |||||
goto skip_2; | |||||
} | |||||
if (inp->in6p_cksum != -1) { | if (inp->in6p_cksum != -1) { | ||||
RIP6STAT_INC(rip6s_isum); | RIP6STAT_INC(rip6s_isum); | ||||
if (m->m_pkthdr.len - (*offp + inp->in6p_cksum) < 2 || | if (m->m_pkthdr.len - (*offp + inp->in6p_cksum) < 2 || | ||||
in6_cksum(m, proto, *offp, | in6_cksum(m, proto, *offp, | ||||
m->m_pkthdr.len - *offp)) { | m->m_pkthdr.len - *offp)) { | ||||
RIP6STAT_INC(rip6s_badsum); | RIP6STAT_INC(rip6s_badsum); | ||||
/* | /* | ||||
* Drop the received message, don't send an | * Drop the received message, don't send an | ||||
* ICMP6 message. Set proto to IPPROTO_NONE | * ICMP6 message. Set proto to IPPROTO_NONE | ||||
* to achieve that. | * to achieve that. | ||||
*/ | */ | ||||
INP_RUNLOCK(inp); | |||||
proto = IPPROTO_NONE; | proto = IPPROTO_NONE; | ||||
goto skip_2; | break; | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* If this raw socket has multicast state, and we | * If this raw socket has multicast state, and we | ||||
* have received a multicast, check if this socket | * have received a multicast, check if this socket | ||||
* should receive it, as multicast filtering is now | * should receive it, as multicast filtering is now | ||||
* the responsibility of the transport layer. | * the responsibility of the transport layer. | ||||
*/ | */ | ||||
Show All 29 Lines | if (inp->in6p_moptions && | ||||
blocked = im6o_mc_filter(inp->in6p_moptions, | blocked = im6o_mc_filter(inp->in6p_moptions, | ||||
ifp, | ifp, | ||||
(struct sockaddr *)&mcaddr, | (struct sockaddr *)&mcaddr, | ||||
(struct sockaddr *)&fromsa); | (struct sockaddr *)&fromsa); | ||||
} | } | ||||
if (blocked != MCAST_PASS) { | if (blocked != MCAST_PASS) { | ||||
IP6STAT_INC(ip6s_notmember); | IP6STAT_INC(ip6s_notmember); | ||||
goto skip_2; | continue; | ||||
} | } | ||||
} | } | ||||
last = inp; | if ((n = m_copym(m, 0, M_COPYALL, M_NOWAIT)) == NULL) | ||||
continue; | continue; | ||||
skip_2: | if (inp->inp_flags & INP_CONTROLOPTS || | ||||
INP_RUNLOCK(inp); | inp->inp_socket->so_options & SO_TIMESTAMP) | ||||
} | ip6_savecontrol(inp, n, &opts); | ||||
#if defined(IPSEC) || defined(IPSEC_SUPPORT) | /* strip intermediate headers */ | ||||
/* | m_adj(n, *offp); | ||||
* Check AH/ESP integrity. | if (sbappendaddr(&inp->inp_socket->so_rcv, | ||||
*/ | (struct sockaddr *)&fromsa, n, opts) == 0) { | ||||
if (IPSEC_ENABLED(ipv6) && last != NULL && | soroverflow(inp->inp_socket); | ||||
IPSEC_CHECK_POLICY(ipv6, m, last) != 0) { | m_freem(n); | ||||
m_freem(m); | |||||
IP6STAT_DEC(ip6s_delivered); | |||||
/* Do not inject data into pcb. */ | |||||
INP_RUNLOCK(last); | |||||
} else | |||||
#endif /* IPSEC */ | |||||
if (last != NULL) { | |||||
if (last->inp_flags & INP_CONTROLOPTS || | |||||
last->inp_socket->so_options & SO_TIMESTAMP) | |||||
ip6_savecontrol(last, m, &opts); | |||||
/* Strip intermediate headers. */ | |||||
m_adj(m, *offp); | |||||
if (sbappendaddr(&last->inp_socket->so_rcv, | |||||
(struct sockaddr *)&fromsa, m, opts) == 0) { | |||||
soroverflow(last->inp_socket); | |||||
m_freem(m); | |||||
if (opts) | if (opts) | ||||
m_freem(opts); | m_freem(opts); | ||||
RIP6STAT_INC(rip6s_fullsock); | RIP6STAT_INC(rip6s_fullsock); | ||||
} else | |||||
sorwakeup(last->inp_socket); | |||||
INP_RUNLOCK(last); | |||||
} else { | } else { | ||||
sorwakeup(inp->inp_socket); | |||||
delivered++; | |||||
} | |||||
opts = NULL; | |||||
} | |||||
if (delivered == 0) { | |||||
RIP6STAT_INC(rip6s_nosock); | RIP6STAT_INC(rip6s_nosock); | ||||
if (m->m_flags & M_MCAST) | if (m->m_flags & M_MCAST) | ||||
RIP6STAT_INC(rip6s_nosockmcast); | RIP6STAT_INC(rip6s_nosockmcast); | ||||
if (proto == IPPROTO_NONE) | if (proto == IPPROTO_NONE) | ||||
m_freem(m); | m_freem(m); | ||||
else | else | ||||
icmp6_error(m, ICMP6_PARAM_PROB, | icmp6_error(m, ICMP6_PARAM_PROB, | ||||
ICMP6_PARAMPROB_NEXTHEADER, | ICMP6_PARAMPROB_NEXTHEADER, | ||||
ip6_get_prevhdr(m, *offp)); | ip6_get_prevhdr(m, *offp)); | ||||
IP6STAT_DEC(ip6s_delivered); | IP6STAT_DEC(ip6s_delivered); | ||||
} | } else | ||||
m_freem(m); | |||||
return (IPPROTO_DONE); | return (IPPROTO_DONE); | ||||
} | } | ||||
void | void | ||||
rip6_ctlinput(int cmd, struct sockaddr *sa, void *d) | rip6_ctlinput(int cmd, struct sockaddr *sa, void *d) | ||||
{ | { | ||||
struct ip6ctlparam *ip6cp = NULL; | struct ip6ctlparam *ip6cp = NULL; | ||||
const struct sockaddr_in6 *sa6_src = NULL; | const struct sockaddr_in6 *sa6_src = NULL; | ||||
▲ Show 20 Lines • Show All 316 Lines • ▼ Show 20 Lines | rip6_attach(struct socket *so, int proto, struct thread *td) | ||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
error = soreserve(so, rip_sendspace, rip_recvspace); | error = soreserve(so, rip_sendspace, rip_recvspace); | ||||
if (error) | if (error) | ||||
return (error); | return (error); | ||||
filter = malloc(sizeof(struct icmp6_filter), M_PCB, M_NOWAIT); | filter = malloc(sizeof(struct icmp6_filter), M_PCB, M_NOWAIT); | ||||
if (filter == NULL) | if (filter == NULL) | ||||
return (ENOMEM); | return (ENOMEM); | ||||
INP_INFO_WLOCK(&V_ripcbinfo); | |||||
error = in_pcballoc(so, &V_ripcbinfo); | error = in_pcballoc(so, &V_ripcbinfo); | ||||
if (error) { | if (error) { | ||||
INP_INFO_WUNLOCK(&V_ripcbinfo); | |||||
free(filter, M_PCB); | free(filter, M_PCB); | ||||
return (error); | return (error); | ||||
} | } | ||||
inp = (struct inpcb *)so->so_pcb; | inp = (struct inpcb *)so->so_pcb; | ||||
INP_INFO_WUNLOCK(&V_ripcbinfo); | |||||
inp->inp_vflag |= INP_IPV6; | inp->inp_vflag |= INP_IPV6; | ||||
inp->inp_ip_p = (long)proto; | inp->inp_ip_p = (long)proto; | ||||
inp->in6p_hops = -1; /* use kernel default */ | inp->in6p_hops = -1; /* use kernel default */ | ||||
inp->in6p_cksum = -1; | inp->in6p_cksum = -1; | ||||
inp->in6p_icmp6filt = filter; | inp->in6p_icmp6filt = filter; | ||||
ICMP6_FILTER_SETPASSALL(inp->in6p_icmp6filt); | ICMP6_FILTER_SETPASSALL(inp->in6p_icmp6filt); | ||||
INP_WUNLOCK(inp); | INP_WUNLOCK(inp); | ||||
return (0); | return (0); | ||||
} | } | ||||
static void | static void | ||||
rip6_detach(struct socket *so) | rip6_detach(struct socket *so) | ||||
{ | { | ||||
struct inpcb *inp; | struct inpcb *inp; | ||||
inp = sotoinpcb(so); | inp = sotoinpcb(so); | ||||
KASSERT(inp != NULL, ("rip6_detach: inp == NULL")); | KASSERT(inp != NULL, ("rip6_detach: inp == NULL")); | ||||
if (so == V_ip6_mrouter && ip6_mrouter_done) | if (so == V_ip6_mrouter && ip6_mrouter_done) | ||||
ip6_mrouter_done(); | ip6_mrouter_done(); | ||||
/* xxx: RSVP */ | /* xxx: RSVP */ | ||||
INP_INFO_WLOCK(&V_ripcbinfo); | |||||
INP_WLOCK(inp); | INP_WLOCK(inp); | ||||
free(inp->in6p_icmp6filt, M_PCB); | free(inp->in6p_icmp6filt, M_PCB); | ||||
in_pcbdetach(inp); | in_pcbdetach(inp); | ||||
in_pcbfree(inp); | in_pcbfree(inp); | ||||
INP_INFO_WUNLOCK(&V_ripcbinfo); | |||||
} | } | ||||
/* XXXRW: This can't ever be called. */ | /* XXXRW: This can't ever be called. */ | ||||
static void | static void | ||||
rip6_abort(struct socket *so) | rip6_abort(struct socket *so) | ||||
{ | { | ||||
struct inpcb *inp; | struct inpcb *inp; | ||||
▲ Show 20 Lines • Show All 222 Lines • Show Last 20 Lines |