Changeset View
Changeset View
Standalone View
Standalone View
sys/netinet6/icmp6.c
Show First 20 Lines • Show All 118 Lines • ▼ Show 20 Lines | |||||
VNET_PCPUSTAT_DEFINE(struct icmp6stat, icmp6stat); | VNET_PCPUSTAT_DEFINE(struct icmp6stat, icmp6stat); | ||||
VNET_PCPUSTAT_SYSINIT(icmp6stat); | VNET_PCPUSTAT_SYSINIT(icmp6stat); | ||||
#ifdef VIMAGE | #ifdef VIMAGE | ||||
VNET_PCPUSTAT_SYSUNINIT(icmp6stat); | VNET_PCPUSTAT_SYSUNINIT(icmp6stat); | ||||
#endif /* VIMAGE */ | #endif /* VIMAGE */ | ||||
VNET_DECLARE(struct inpcbinfo, ripcbinfo); | VNET_DECLARE(struct inpcbinfo, ripcbinfo); | ||||
VNET_DECLARE(struct inpcbhead, ripcb); | |||||
VNET_DECLARE(int, icmp6errppslim); | VNET_DECLARE(int, icmp6errppslim); | ||||
VNET_DEFINE_STATIC(int, icmp6errpps_count) = 0; | VNET_DEFINE_STATIC(int, icmp6errpps_count) = 0; | ||||
VNET_DEFINE_STATIC(struct timeval, icmp6errppslim_last); | VNET_DEFINE_STATIC(struct timeval, icmp6errppslim_last); | ||||
VNET_DECLARE(int, icmp6_nodeinfo); | VNET_DECLARE(int, icmp6_nodeinfo); | ||||
#define V_ripcbinfo VNET(ripcbinfo) | #define V_ripcbinfo VNET(ripcbinfo) | ||||
#define V_ripcb VNET(ripcb) | |||||
#define V_icmp6errppslim VNET(icmp6errppslim) | #define V_icmp6errppslim VNET(icmp6errppslim) | ||||
#define V_icmp6errpps_count VNET(icmp6errpps_count) | #define V_icmp6errpps_count VNET(icmp6errpps_count) | ||||
#define V_icmp6errppslim_last VNET(icmp6errppslim_last) | #define V_icmp6errppslim_last VNET(icmp6errppslim_last) | ||||
#define V_icmp6_nodeinfo VNET(icmp6_nodeinfo) | #define V_icmp6_nodeinfo VNET(icmp6_nodeinfo) | ||||
static void icmp6_errcount(int, int); | static void icmp6_errcount(int, int); | ||||
static int icmp6_rip6_input(struct mbuf **, int); | static int icmp6_rip6_input(struct mbuf **, int); | ||||
static void icmp6_reflect(struct mbuf *, size_t); | static void icmp6_reflect(struct mbuf *, size_t); | ||||
▲ Show 20 Lines • Show All 1,727 Lines • ▼ Show 20 Lines | if (allow_deprecated == 0 && ifp_dep != NULL) { | ||||
allow_deprecated = 1; | allow_deprecated = 1; | ||||
goto again; | goto again; | ||||
} | } | ||||
return (copied); | return (copied); | ||||
} | } | ||||
static bool | |||||
icmp6_rip6_match(const struct inpcb *inp, void *v) | |||||
{ | |||||
struct ip6_hdr *ip6 = v; | |||||
if ((inp->inp_vflag & INP_IPV6) == 0) | |||||
return (false); | |||||
if (inp->inp_ip_p != IPPROTO_ICMPV6) | |||||
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); | |||||
} | |||||
/* | /* | ||||
* XXX almost dup'ed code with rip6_input. | * XXX almost dup'ed code with rip6_input. | ||||
*/ | */ | ||||
static int | static int | ||||
icmp6_rip6_input(struct mbuf **mp, int off) | icmp6_rip6_input(struct mbuf **mp, int off) | ||||
{ | { | ||||
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_iterator inpi = INP_ITERATOR(&V_ripcbinfo, | |||||
INPLOOKUP_RLOCKPCB, icmp6_rip6_match, ip6); | |||||
struct inpcb *inp; | struct inpcb *inp; | ||||
struct inpcb *last = NULL; | |||||
struct sockaddr_in6 fromsa; | struct sockaddr_in6 fromsa; | ||||
struct icmp6_hdr *icmp6; | struct icmp6_hdr *icmp6; | ||||
struct mbuf *opts = NULL; | struct mbuf *opts = NULL; | ||||
int delivered = 0; | |||||
NET_EPOCH_ASSERT(); | |||||
/* This is assumed to be safe; icmp6_input() does a pullup. */ | /* This is assumed to be safe; icmp6_input() does a pullup. */ | ||||
icmp6 = (struct icmp6_hdr *)((caddr_t)ip6 + off); | icmp6 = (struct icmp6_hdr *)((caddr_t)ip6 + off); | ||||
/* | /* | ||||
* XXX: the address may have embedded scope zone ID, which should be | * XXX: the address may have embedded scope zone ID, which should be | ||||
* hidden from applications. | * hidden from applications. | ||||
*/ | */ | ||||
bzero(&fromsa, sizeof(fromsa)); | bzero(&fromsa, sizeof(fromsa)); | ||||
fromsa.sin6_family = AF_INET6; | fromsa.sin6_family = AF_INET6; | ||||
fromsa.sin6_len = sizeof(struct sockaddr_in6); | fromsa.sin6_len = sizeof(struct sockaddr_in6); | ||||
fromsa.sin6_addr = ip6->ip6_src; | fromsa.sin6_addr = ip6->ip6_src; | ||||
if (sa6_recoverscope(&fromsa)) { | if (sa6_recoverscope(&fromsa)) { | ||||
m_freem(m); | m_freem(m); | ||||
*mp = NULL; | *mp = NULL; | ||||
return (IPPROTO_DONE); | return (IPPROTO_DONE); | ||||
} | } | ||||
CK_LIST_FOREACH(inp, &V_ripcb, inp_list) { | while ((inp = inp_next(&inpi)) != NULL) { | ||||
if ((inp->inp_vflag & INP_IPV6) == 0) | |||||
continue; | |||||
if (inp->inp_ip_p != IPPROTO_ICMPV6) | |||||
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; | |||||
INP_RLOCK(inp); | |||||
if (__predict_false(inp->inp_flags2 & INP_FREED)) { | |||||
INP_RUNLOCK(inp); | |||||
continue; | |||||
} | |||||
if (ICMP6_FILTER_WILLBLOCK(icmp6->icmp6_type, | if (ICMP6_FILTER_WILLBLOCK(icmp6->icmp6_type, | ||||
inp->in6p_icmp6filt)) { | inp->in6p_icmp6filt)) | ||||
INP_RUNLOCK(inp); | |||||
continue; | continue; | ||||
} | |||||
if (last != NULL) { | |||||
struct mbuf *n = NULL; | |||||
/* | /* | ||||
* Recent network drivers tend to allocate a single | * Recent network drivers tend to allocate a single | ||||
* mbuf cluster, rather than to make a couple of | * mbuf cluster, rather than to make a couple of | ||||
* mbufs without clusters. Also, since the IPv6 code | * mbufs without clusters. Also, since the IPv6 code | ||||
* path tries to avoid m_pullup(), it is highly | * path tries to avoid m_pullup(), it is highly | ||||
* probable that we still have an mbuf cluster here | * probable that we still have an mbuf cluster here | ||||
* even though the necessary length can be stored in an | * even though the necessary length can be stored in an | ||||
* mbuf's internal buffer. | * mbuf's internal buffer. | ||||
* Meanwhile, the default size of the receive socket | * Meanwhile, the default size of the receive socket | ||||
* buffer for raw sockets is not so large. This means | * buffer for raw sockets is not so large. This means | ||||
* the possibility of packet loss is relatively higher | * the possibility of packet loss is relatively higher | ||||
* than before. To avoid this scenario, we copy the | * than before. To avoid this scenario, we copy the | ||||
* received data to a separate mbuf that does not use | * received data to a separate mbuf that does not use | ||||
* a cluster, if possible. | * a cluster, if possible. | ||||
* XXX: it is better to copy the data after stripping | * XXX: it is better to copy the data after stripping | ||||
* intermediate headers. | * intermediate headers. | ||||
*/ | */ | ||||
if ((m->m_flags & M_EXT) && m->m_next == NULL && | if ((m->m_flags & M_EXT) && m->m_next == NULL && | ||||
m->m_len <= MHLEN) { | m->m_len <= MHLEN) { | ||||
n = m_get(M_NOWAIT, m->m_type); | n = m_get(M_NOWAIT, m->m_type); | ||||
if (n != NULL) { | if (n != NULL) { | ||||
if (m_dup_pkthdr(n, m, M_NOWAIT)) { | if (m_dup_pkthdr(n, m, M_NOWAIT)) { | ||||
bcopy(m->m_data, n->m_data, | bcopy(m->m_data, n->m_data, m->m_len); | ||||
m->m_len); | |||||
n->m_len = m->m_len; | n->m_len = m->m_len; | ||||
} else { | } else { | ||||
m_free(n); | m_free(n); | ||||
n = NULL; | n = NULL; | ||||
} | } | ||||
} | } | ||||
} | } else | ||||
if (n != NULL || | n = m_copym(m, 0, M_COPYALL, M_NOWAIT); | ||||
(n = m_copym(m, 0, M_COPYALL, M_NOWAIT)) != NULL) { | if (n == NULL) | ||||
if (last->inp_flags & INP_CONTROLOPTS) | continue; | ||||
ip6_savecontrol(last, n, &opts); | if (inp->inp_flags & INP_CONTROLOPTS) | ||||
ip6_savecontrol(inp, n, &opts); | |||||
/* strip intermediate headers */ | /* strip intermediate headers */ | ||||
m_adj(n, off); | m_adj(n, off); | ||||
SOCKBUF_LOCK(&last->inp_socket->so_rcv); | SOCKBUF_LOCK(&inp->inp_socket->so_rcv); | ||||
if (sbappendaddr_locked( | if (sbappendaddr_locked(&inp->inp_socket->so_rcv, | ||||
&last->inp_socket->so_rcv, | (struct sockaddr *)&fromsa, n, opts) == 0) { | ||||
(struct sockaddr *)&fromsa, n, opts) | soroverflow_locked(inp->inp_socket); | ||||
== 0) { | |||||
soroverflow_locked(last->inp_socket); | |||||
m_freem(n); | m_freem(n); | ||||
if (opts) { | if (opts) | ||||
m_freem(opts); | m_freem(opts); | ||||
} else { | |||||
sorwakeup_locked(inp->inp_socket); | |||||
delivered++; | |||||
} | } | ||||
} else | |||||
sorwakeup_locked(last->inp_socket); | |||||
opts = NULL; | opts = NULL; | ||||
} | } | ||||
INP_RUNLOCK(last); | |||||
} | |||||
last = inp; | |||||
} | |||||
if (last != NULL) { | |||||
if (last->inp_flags & INP_CONTROLOPTS) | |||||
ip6_savecontrol(last, m, &opts); | |||||
/* strip intermediate headers */ | |||||
m_adj(m, off); | |||||
/* avoid using mbuf clusters if possible (see above) */ | |||||
if ((m->m_flags & M_EXT) && m->m_next == NULL && | |||||
m->m_len <= MHLEN) { | |||||
struct mbuf *n; | |||||
n = m_get(M_NOWAIT, m->m_type); | |||||
if (n != NULL) { | |||||
if (m_dup_pkthdr(n, m, M_NOWAIT)) { | |||||
bcopy(m->m_data, n->m_data, m->m_len); | |||||
n->m_len = m->m_len; | |||||
m_freem(m); | m_freem(m); | ||||
m = n; | |||||
} else { | |||||
m_freem(n); | |||||
n = NULL; | |||||
} | |||||
} | |||||
} | |||||
SOCKBUF_LOCK(&last->inp_socket->so_rcv); | |||||
if (sbappendaddr_locked(&last->inp_socket->so_rcv, | |||||
(struct sockaddr *)&fromsa, m, opts) == 0) { | |||||
m_freem(m); | |||||
if (opts) | |||||
m_freem(opts); | |||||
soroverflow_locked(last->inp_socket); | |||||
} else | |||||
sorwakeup_locked(last->inp_socket); | |||||
INP_RUNLOCK(last); | |||||
} else { | |||||
m_freem(m); | |||||
IP6STAT_DEC(ip6s_delivered); | |||||
} | |||||
*mp = NULL; | *mp = NULL; | ||||
if (delivered == 0) | |||||
IP6STAT_DEC(ip6s_delivered); | |||||
return (IPPROTO_DONE); | return (IPPROTO_DONE); | ||||
} | } | ||||
/* | /* | ||||
* Reflect the ip6 packet back to the source. | * Reflect the ip6 packet back to the source. | ||||
* OFF points to the icmp6 header, counted from the top of the mbuf. | * OFF points to the icmp6 header, counted from the top of the mbuf. | ||||
*/ | */ | ||||
static void | static void | ||||
▲ Show 20 Lines • Show All 765 Lines • Show Last 20 Lines |