📄 ip6_forward.c
字号:
if (mbc &&
(mbc->mbc_flags & IP6_BUF_HOME) &&
(mbc->mbc_encap != NULL)) {
/*
* if we have a binding cache entry for the
* ip6_dst, we are acting as a home agent for
* that node. before sending a packet as a
* tunneled packet, we must make sure that
* encaptab is ready. if dad is enabled and
* not completed yet, encaptab will be NULL.
*/
if (mip6_tunnel_output(&m, mbc) != 0) {
ip6stat.ip6s_cantforward++;
}
if (mcopy)
m_freem(mcopy);
return;
}
}
#endif /* MIP6 */
dst = (struct sockaddr_in6 *)&ip6_forward_rt.ro_dst;
if (!srcrt) {
#ifdef MEASURE_PERFORMANCE
ctr_beg = read_tsc();
#endif
/*
* ip6_forward_rt.ro_dst.sin6_addr is equal to ip6->ip6_dst
*/
if (ip6_forward_rt.ro_rt == 0 ||
(ip6_forward_rt.ro_rt->rt_flags & RTF_UP) == 0
#ifdef MEASURE_PERFORMANCE
|| (ip6_ours_check_algorithm != OURS_CHECK_ALG_RTABLE &&
!IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &dst->sin6_addr))
#endif
) {
if (ip6_forward_rt.ro_rt) {
RTFREE(ip6_forward_rt.ro_rt);
ip6_forward_rt.ro_rt = 0;
}
#ifdef MEASURE_PERFORMANCE
ip6_forward_cache_miss++;
bzero(dst, sizeof(*dst));
dst->sin6_family = AF_INET6;
dst->sin6_len = sizeof(*dst);
dst->sin6_addr = ip6->ip6_dst;
#endif
/* this probably fails but give it a try again */
#ifdef __FreeBSD__
rtalloc_ign((struct route *)&ip6_forward_rt,
RTF_PRCLONING);
#else
rtalloc((struct route *)&ip6_forward_rt);
#endif
}
#ifdef MEASURE_PERFORMANCE
ctr_end = read_tsc();
#ifdef MEASURE_PERFORMANCE_UDPONLY
if (ip6->ip6_nxt == IPPROTO_UDP)
#endif
add_performance_log2(ctr_end - ctr_beg);
#endif
if (ip6_forward_rt.ro_rt == 0) {
ip6stat.ip6s_noroute++;
in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_noroute);
if (mcopy) {
icmp6_error(mcopy, ICMP6_DST_UNREACH,
ICMP6_DST_UNREACH_NOROUTE, 0);
}
m_freem(m);
return;
}
} else if ((rt = ip6_forward_rt.ro_rt) == 0 ||
!IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &dst->sin6_addr)) {
if (ip6_forward_rt.ro_rt) {
RTFREE(ip6_forward_rt.ro_rt);
ip6_forward_rt.ro_rt = 0;
}
bzero(dst, sizeof(*dst));
dst->sin6_len = sizeof(struct sockaddr_in6);
dst->sin6_family = AF_INET6;
dst->sin6_addr = ip6->ip6_dst;
#ifdef __FreeBSD__
rtalloc_ign((struct route *)&ip6_forward_rt, RTF_PRCLONING);
#else
rtalloc((struct route *)&ip6_forward_rt);
#endif
if (ip6_forward_rt.ro_rt == 0) {
ip6stat.ip6s_noroute++;
in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_noroute);
if (mcopy) {
icmp6_error(mcopy, ICMP6_DST_UNREACH,
ICMP6_DST_UNREACH_NOROUTE, 0);
}
m_freem(m);
return;
}
}
rt = ip6_forward_rt.ro_rt;
/*
* Scope check: if a packet can't be delivered to its destination
* for the reason that the destination is beyond the scope of the
* source address, discard the packet and return an icmp6 destination
* unreachable error with Code 2 (beyond scope of source address).
* [draft-ietf-ipngwg-icmp-v3-00.txt, Section 3.1]
*/
if ((srczone = in6_addr2zoneid(m->m_pkthdr.rcvif, &ip6->ip6_src)) < 0
|| (dstzone = in6_addr2zoneid(rt->rt_ifp, &ip6->ip6_src)) < 0) {
/* XXX: will this really happen? should return an icmp error? */
ip6stat.ip6s_cantforward++;
ip6stat.ip6s_badscope++;
m_freem(m);
return;
}
if (srczone != dstzone) {
ip6stat.ip6s_cantforward++;
ip6stat.ip6s_badscope++;
in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard);
if (ip6_log_time + ip6_log_interval < time_second) {
ip6_log_time = time_second;
log(LOG_DEBUG,
"cannot forward "
"src %s, dst %s, nxt %d, rcvif %s, outif %s\n",
ip6_sprintf(&ip6->ip6_src),
ip6_sprintf(&ip6->ip6_dst),
ip6->ip6_nxt,
if_name(m->m_pkthdr.rcvif), if_name(rt->rt_ifp));
}
if (mcopy)
icmp6_error(mcopy, ICMP6_DST_UNREACH,
ICMP6_DST_UNREACH_BEYONDSCOPE, 0);
m_freem(m);
return;
}
if (m->m_pkthdr.len > rt->rt_ifp->if_mtu) {
in6_ifstat_inc(rt->rt_ifp, ifs6_in_toobig);
if (mcopy) {
u_long mtu;
#ifdef IPSEC
struct secpolicy *sp;
int ipsecerror;
size_t ipsechdrsiz;
#endif
mtu = rt->rt_ifp->if_mtu;
#ifdef IPSEC
/*
* When we do IPsec tunnel ingress, we need to play
* with if_mtu value (decrement IPsec header size
* from mtu value). The code is much simpler than v4
* case, as we have the outgoing interface for
* encapsulated packet as "rt->rt_ifp".
*/
sp = ipsec6_getpolicybyaddr(mcopy, IPSEC_DIR_OUTBOUND,
IP_FORWARDING, &ipsecerror);
if (sp) {
ipsechdrsiz = ipsec6_hdrsiz(mcopy,
IPSEC_DIR_OUTBOUND, NULL);
if (ipsechdrsiz < mtu)
mtu -= ipsechdrsiz;
}
/*
* if mtu becomes less than minimum MTU,
* tell minimum MTU (and I'll need to fragment it).
*/
if (mtu < IPV6_MMTU)
mtu = IPV6_MMTU;
#endif
icmp6_error(mcopy, ICMP6_PACKET_TOO_BIG, 0, mtu);
}
m_freem(m);
return;
}
if (rt->rt_flags & RTF_GATEWAY)
dst = (struct sockaddr_in6 *)rt->rt_gateway;
/*
* If we are to forward the packet using the same interface
* as one we got the packet from, perhaps we should send a redirect
* to sender to shortcut a hop.
* Only send redirect if source is sending directly to us,
* and if packet was not source routed (or has any options).
* Also, don't send redirect if forwarding using a route
* modified by a redirect.
*/
if (rt->rt_ifp == m->m_pkthdr.rcvif && !srcrt &&
(rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0) {
if ((rt->rt_ifp->if_flags & IFF_POINTOPOINT) &&
nd6_is_addr_neighbor((struct sockaddr_in6 *)&ip6_forward_rt.ro_dst, rt->rt_ifp)) {
/*
* If the incoming interface is equal to the outgoing
* one, the link attached to the interface is
* point-to-point, and the IPv6 destination is
* regarded as on-link on the link, then it will be
* highly probable that the destination address does
* not exist on the link and that the packet is going
* to loop. Thus, we immediately drop the packet and
* send an ICMPv6 error message.
* For other routing loops, we dare to let the packet
* go to the loop, so that a remote diagnosing host
* can detect the loop by traceroute.
* type/code is based on suggestion by Rich Draves.
* not sure if it is the best pick.
*/
icmp6_error(mcopy, ICMP6_DST_UNREACH,
ICMP6_DST_UNREACH_ADDR, 0);
m_freem(m);
return;
}
type = ND_REDIRECT;
}
#if defined(IPV6FIREWALL) || (defined(__FreeBSD__) && __FreeBSD__ >= 4)
/*
* Check with the firewall...
*/
#if defined(__FreeBSD__) && __FreeBSD__ >= 4
if (ip6_fw_enable && ip6_fw_chk_ptr) {
#else
if (ip6_fw_chk_ptr) {
#endif
/* If ipfw says divert, we have to just drop packet */
if ((*ip6_fw_chk_ptr)(&ip6, rt->rt_ifp, &m)) {
m_freem(m);
goto freecopy;
}
if (!m)
goto freecopy;
}
#endif
/*
* Fake scoped addresses. Note that even link-local source or
* destinaion can appear, if the originating node just sends the
* packet to us (without address resolution for the destination).
* Since both icmp6_error and icmp6_redirect_output fill the embedded
* link identifiers, we can do this stuff after making a copy for
* returning an error.
*/
if ((rt->rt_ifp->if_flags & IFF_LOOPBACK) != 0) {
/*
* See corresponding comments in ip6_output.
* XXX: but is it possible that ip6_forward() sends a packet
* to a loopback interface? I don't think so, and thus
* I bark here. (jinmei@kame.net)
* XXX: it is common to route invalid packets to loopback.
* also, the codepath will be visited on use of ::1 in
* rthdr. (itojun)
*/
#if 1
if (0)
#else
if ((rt->rt_flags & (RTF_BLACKHOLE|RTF_REJECT)) == 0)
#endif
{
printf("ip6_forward: outgoing interface is loopback. "
"src %s, dst %s, nxt %d, rcvif %s, outif %s\n",
ip6_sprintf(&ip6->ip6_src),
ip6_sprintf(&ip6->ip6_dst),
ip6->ip6_nxt, if_name(m->m_pkthdr.rcvif),
if_name(rt->rt_ifp));
}
/* we can just use rcvif in forwarding. */
origifp = m->m_pkthdr.rcvif;
}
else
origifp = rt->rt_ifp;
#ifndef SCOPEDROUTING
/*
* clear embedded scope identifiers if necessary.
* in6_clearscope will touch the addresses only when necessary.
*/
in6_clearscope(&ip6->ip6_src);
in6_clearscope(&ip6->ip6_dst);
#endif
#if defined(__NetBSD__) && defined(PFIL_HOOKS)
{
struct packet_filter_hook *pfh;
struct mbuf *m1;
int rv;
/*
* Run through list of hooks for output packets.
*/
m1 = m;
pfh = pfil_hook_get(PFIL_OUT, &inetsw[ip_protox[IPPROTO_IPV6]].pr_pfh);
for (; pfh; pfh = pfh->pfil_link.tqe_next)
if (pfh->pfil_func) {
rv = pfh->pfil_func(ip6, sizeof(*ip6), rt->rt_ifp, 1, &m1);
if (rv) {
error = EHOSTUNREACH;
goto senderr;
}
m = m1;
if (m == NULL)
goto freecopy;
ip6 = mtod(m, struct ip6_hdr *);
}
}
#endif /* PFIL_HOOKS */
#if defined(__OpenBSD__) && NPF > 0
if (pf_test6(PF_OUT, rt->rt_ifp, &m) != PF_PASS) {
m_freem(m);
goto senderr;
}
ip6 = mtod(m, struct ip6_hdr *);
#endif
error = nd6_output(rt->rt_ifp, origifp, m, dst, rt);
if (error) {
in6_ifstat_inc(rt->rt_ifp, ifs6_out_discard);
ip6stat.ip6s_cantforward++;
} else {
ip6stat.ip6s_forward++;
in6_ifstat_inc(rt->rt_ifp, ifs6_out_forward);
if (type)
ip6stat.ip6s_redirectsent++;
else {
if (mcopy)
goto freecopy;
}
}
#if (defined(__NetBSD__) && defined(PFIL_HOOKS)) || (defined(__OpenBSD__) && NPF > 0)
senderr:
#endif
if (mcopy == NULL)
return;
switch (error) {
case 0:
#if 1
if (type == ND_REDIRECT) {
icmp6_redirect_output(mcopy, rt);
return;
}
#endif
goto freecopy;
case EMSGSIZE:
/* xxx MTU is constant in PPP? */
goto freecopy;
case ENOBUFS:
/* Tell source to slow down like source quench in IP? */
goto freecopy;
case ENETUNREACH: /* shouldn't happen, checked above */
case EHOSTUNREACH:
case ENETDOWN:
case EHOSTDOWN:
default:
type = ICMP6_DST_UNREACH;
code = ICMP6_DST_UNREACH_ADDR;
break;
}
icmp6_error(mcopy, type, code, 0);
return;
freecopy:
m_freem(mcopy);
return;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -