📄 ip6_output.c
字号:
}
if (rt == NULL) {
/*
* If in6_selectroute() does not return a route entry,
* dst may not have been updated.
*/
*dst = dst0; /* XXX */
}
/*
* then rt (for unicast) and ifp must be non-NULL valid values.
*/
if ((flags & IPV6_FORWARDING) == 0) {
/* XXX: the FORWARDING flag can be set for mrouting. */
in6_ifstat_inc(ifp, ifs6_out_request);
}
if (rt != NULL) {
ia = (struct in6_ifaddr *)(rt->rt_ifa);
rt->rt_use++;
}
/*
* The outgoing interface must be in the zone of source and
* destination addresses. We should use ia_ifp to support the
* case of sending packets to an address of our own.
*/
if (ia != NULL && ia->ia_ifp)
ifp0 = ia->ia_ifp;
else
ifp0 = ifp;
/* XXX: we should not do this conversion for every packet. */
bzero(&src, sizeof(src));
src.sin6_family = AF_INET6;
src.sin6_len = sizeof(src);
src.sin6_addr = ip6->ip6_src;
if ((error = in6_recoverscope(&src, &ip6->ip6_src, NULL))
!= 0) {
goto badscope;
}
if ((zone = in6_addr2zoneid(ifp0, &src.sin6_addr)) < 0 ||
zone != src.sin6_scope_id) {
#ifdef SCOPEDEBUG /* will be removed shortly */
printf("ip6 output: bad source scope %s for %s on %s\n",
ip6_sprintf(&ip6->ip6_src),
ip6_sprintf(&ip6->ip6_dst), if_name(ifp0));
#endif
goto badscope;
}
/* XXX: in6_recoverscope will clear the embedded ID */
if ((error = in6_recoverscope(&dst0, &ip6->ip6_dst, NULL))
!= 0) {
goto badscope;
}
if ((zone = in6_addr2zoneid(ifp0, &dst0.sin6_addr)) < 0 ||
zone != dst0.sin6_scope_id) {
#ifdef SCOPEDEBUG /* will be removed shortly */
printf("ip6 output: bad dst scope %s on %s\n",
ip6_sprintf(&dst0.sin6_addr), if_name(ifp0));
#endif
goto badscope;
}
/* scope check is done. */
goto routefound;
badscope:
ip6stat.ip6s_badscope++;
in6_ifstat_inc(ifp0, ifs6_out_discard);
if (error == 0)
error = EHOSTUNREACH; /* XXX */
goto bad;
}
routefound:
if (rt) {
if (opt && opt->ip6po_nextroute.ro_rt) {
/*
* The nexthop is explicitly specified by the
* application. We assume the next hop is an IPv6
* address.
*/
dst = (struct sockaddr_in6 *)opt->ip6po_nexthop;
}
else if ((rt->rt_flags & RTF_GATEWAY))
dst = (struct sockaddr_in6 *)rt->rt_gateway;
}
if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
m->m_flags &= ~(M_BCAST | M_MCAST); /* just in case */
} else {
struct in6_multi *in6m;
m->m_flags = (m->m_flags & ~M_BCAST) | M_MCAST;
in6_ifstat_inc(ifp, ifs6_out_mcast);
/*
* Confirm that the outgoing interface supports multicast.
*/
if (!(ifp->if_flags & IFF_MULTICAST)) {
ip6stat.ip6s_noroute++;
in6_ifstat_inc(ifp, ifs6_out_discard);
error = ENETUNREACH;
goto bad;
}
IN6_LOOKUP_MULTI(ip6->ip6_dst, ifp, in6m);
if (in6m != NULL &&
(im6o == NULL || im6o->im6o_multicast_loop)) {
/*
* If we belong to the destination multicast group
* on the outgoing interface, and the caller did not
* forbid loopback, loop back a copy.
*/
ip6_mloopback(ifp, m, dst);
} else {
/*
* If we are acting as a multicast router, perform
* multicast forwarding as if the packet had just
* arrived on the interface to which we are about
* to send. The multicast forwarding function
* recursively calls this function, using the
* IPV6_FORWARDING flag to prevent infinite recursion.
*
* Multicasts that are looped back by ip6_mloopback(),
* above, will be forwarded by the ip6_input() routine,
* if necessary.
*/
if (ip6_mrouter && (flags & IPV6_FORWARDING) == 0) {
/*
* XXX: ip6_mforward expects that rcvif is NULL
* when it is called from the originating path.
* However, it is not always the case, since
* some versions of MGETHDR() does not
* initialize the field.
*/
m->m_pkthdr.rcvif = NULL;
if (ip6_mforward(ip6, ifp, m) != 0) {
m_freem(m);
goto done;
}
}
}
/*
* Multicasts with a hoplimit of zero may be looped back,
* above, but must not be transmitted on a network.
* Also, multicasts addressed to the loopback interface
* are not sent -- the above call to ip6_mloopback() will
* loop back a copy if this host actually belongs to the
* destination group on the loopback interface.
*/
if (ip6->ip6_hlim == 0 || (ifp->if_flags & IFF_LOOPBACK) ||
IN6_IS_ADDR_MC_INTFACELOCAL(&ip6->ip6_dst)) {
m_freem(m);
goto done;
}
}
/*
* Fill the outgoing inteface to tell the upper layer
* to increment per-interface statistics.
*/
if (ifpp)
*ifpp = ifp;
/*
* Upper-layer reachability confirmation
*/
if (opt && (opt->ip6po_flags & IP6PO_REACHCONF))
nd6_nud_hint(rt, NULL, 0);
/* Determine path MTU. */
if ((error = ip6_getpmtu(ro_pmtu, ro, ifp, &finaldst, &mtu)) != 0)
goto bad;
/*
* An advanced API option (IPV6_USE_MIN_MTU) overrides mtu setting.
* We ignore the specified MTU if it is larger than the already-known
* path MTU.
*/
if (mtu > IPV6_MMTU && opt && (opt->ip6po_flags & IP6PO_MINMTU))
mtu = IPV6_MMTU;
/* Fake scoped addresses */
if ((ifp->if_flags & IFF_LOOPBACK) != 0) {
/*
* If source or destination address is a scoped address, and
* the packet is going to be sent to a loopback interface,
* we should keep the original interface.
*/
/*
* XXX: this is a very experimental and temporary solution.
* We eventually have sockaddr_in6 and use the sin6_scope_id
* field of the structure here.
* We rely on the consistency between two scope zone ids
* of source and destination, which should already be assured.
* Larger scopes than link will be supported in the future.
*/
origifp = NULL;
if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) {
#if defined(__FreeBSD__) && __FreeBSD__ >= 5
origifp = ifnet_byindex(ntohs(ip6->ip6_src.s6_addr16[1]));
#else
origifp = ifindex2ifnet[ntohs(ip6->ip6_src.s6_addr16[1])];
#endif
} else if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) {
#if defined(__FreeBSD__) && __FreeBSD__ >= 5
origifp = ifnet_byindex(ntohs(ip6->ip6_dst.s6_addr16[1]));
#else
origifp = ifindex2ifnet[ntohs(ip6->ip6_dst.s6_addr16[1])];
#endif
} else if (IN6_IS_ADDR_MC_INTFACELOCAL(&ip6->ip6_dst)) {
#if defined(__FreeBSD__) && __FreeBSD__ >= 5
origifp = ifnet_byindex(ntohs(ip6->ip6_dst.s6_addr16[1]));
#else
origifp = ifindex2ifnet[ntohs(ip6->ip6_dst.s6_addr16[1])];
#endif
}
/*
* XXX: origifp can be NULL even in those two cases above.
* For example, if we remove the (only) link-local address
* from the loopback interface, and try to send a link-local
* address without link-id information. Then the source
* address is ::1, and the destination address is the
* link-local address with its s6_addr16[1] being zero.
* What is worse, if the packet goes to the loopback interface
* by a default rejected route, the null pointer would be
* passed to looutput, and the kernel would hang.
* The following last resort would prevent such disaster.
*/
if (origifp == NULL)
origifp = ifp;
}
else
origifp = 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(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
m->m_pkthdr.rcvif = NULL; /* XXX */
/* If ipfw says divert, we have to just drop packet */
if ((*ip6_fw_chk_ptr)(&ip6, ifp, &m)) {
m_freem(m);
goto done;
}
if (!m) {
error = EACCES;
goto done;
}
}
#endif
/*
* If the outgoing packet contains a hop-by-hop options header,
* it must be examined and processed even by the source node.
* (RFC 2460, section 4.)
*/
if (exthdrs.ip6e_hbh) {
struct ip6_hbh *hbh = mtod(exthdrs.ip6e_hbh, struct ip6_hbh *);
u_int32_t dummy; /* XXX unused */
u_int32_t plen = 0; /* XXX: ip6_process will check the value */
#ifdef DIAGNOSTIC
if ((hbh->ip6h_len + 1) << 3 > exthdrs.ip6e_hbh->m_len)
panic("ip6e_hbh is not continuous");
#endif
/*
* XXX: if we have to send an ICMPv6 error to the sender,
* we need the M_LOOP flag since icmp6_error() expects
* the IPv6 and the hop-by-hop options header are
* continuous unless the flag is set.
*/
m->m_flags |= M_LOOP;
m->m_pkthdr.rcvif = ifp;
if (ip6_process_hopopts(m,
(u_int8_t *)(hbh + 1),
((hbh->ip6h_len + 1) << 3) -
sizeof(struct ip6_hbh),
&dummy, &plen) < 0) {
/* m was already freed at this point */
error = EINVAL;/* better error? */
goto done;
}
m->m_flags &= ~M_LOOP; /* XXX */
m->m_pkthdr.rcvif = NULL;
}
#if defined(__NetBSD__) && defined(PFIL_HOOKS)
/*
* 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), ifp, 1, &m1);
if (rv) {
error = EHOSTUNREACH;
goto done;
}
m = m1;
if (m == NULL)
goto done;
ip6 = mtod(m, struct ip6_hdr *);
}
#endif /* PFIL_HOOKS */
#if defined(__OpenBSD__) && NPF > 0
if (pf_test6(PF_OUT, ifp, &m) != PF_PASS) {
error = EHOSTUNREACH;
m_freem(m);
goto done;
}
ip6 = mtod(m, struct ip6_hdr *);
#endif
/*
* Send the packet to the outgoing interface.
* If necessary, do IPv6 fragmentation before sending.
*/
tlen = m->m_pkthdr.len;
/*
* Even if the DONTFRAG option is specified, we cannot send the packet
* when the data length is larger than the MTU of the outgoing
* interface.
* Notify the error by sending IPV6_PATHMTU ancillary data as well
* as returning an error code (the latter is not described in the API
* spec.)
*/
if (opt && (opt->ip6po_flags & IP6PO_DONTFRAG) && tlen > ifp->if_mtu
#ifdef notyet
&& !(ifp->if_flags & IFF_FRAGMENTABLE)
#endif
) {
u_int32_t mtu32;
struct ip6ctlparam ip6cp;
mtu32 = (u_int32_t)mtu;
bzero(&ip6cp, sizeof(ip6cp));
ip6cp.ip6c_cmdarg = (void *)&mtu32;
pfctlinput2(PRC_MSGSIZE, &ro_pmtu->ro_dst, (void *)&ip6cp);
error = EMSGSIZE;
goto bad;
}
if (tlen <= mtu || (opt && (opt->ip6po_flags & IP6PO_DONTFRAG))
#ifdef notyet
/*
* On any link that cannot convey a 1280-octet packet in one piece,
* link-specific fragmentation and reassembly must be provided at
* a layer below IPv6. [RFC 2460, sec.5]
* Thus if the interface has ability of link-level fragmentation,
* we can just send the packet even if the packet size is
* larger than the link's MTU.
* XXX: IFF_FRAGMENTABLE (or such) flag has not been defined yet...
*/
|| (ifp->if_flags & IFF_FRAGMENTABLE)
#endif
)
{
struct in6_ifaddr *ia6;
ip6 = mtod(m, struct ip6_hdr *);
ia6 = in6_ifawithifp(ifp, &ip6->ip6_src);
if (ia6) {
/* Record statistics for this interface address. */
#if defined(__NetBSD__) && defined(IFA_STATS)
ia6->ia_ifa.ifa_data.ifad_outbytes +=
m->m_pkthdr.len;
#elif defined(__FreeBSD__) && __FreeBSD__ >= 4
ia6->ia_ifa.if_opackets++;
ia6->ia_ifa.if_obytes += m->m_pkthdr.len;
#elif defined(__bsdi__) && _BSDI_VERSION >= 199802
ia6->ia_ifa.ifa_opackets++;
ia6->ia_ifa.ifa_obytes += m->m_pkthdr.len;
#endif
}
#if defined(IPSEC) && !defined(__OpenBSD__)
/* clean ipsec history once it goes out of the node */
ipsec_delaux(m);
#endif
error = nd6_output(ifp, origifp, m, dst, rt);
goto done;
} else if (mtu < IPV6_MMTU) {
/*
* note that path MTU is never less than IPV6_MMTU
* (see icmp6_input).
*/
error = EMSGSIZE;
in6_ifstat_inc(ifp, ifs6_out_fragfail);
goto bad;
} else if (ip6->ip6_plen == 0) { /* jumbo payload cannot be fragmented */
error = EMSGSIZE;
in6_ifstat_inc(ifp, ifs6_out_fragfail);
goto bad;
} else {
struct mbuf **mnext, *m_frgpart;
struct ip6_frag *ip6f;
u_int32_t id = htonl(ip6_id++);
u_char nextproto;
struct ip6ctlparam ip6cp;
u_int32_t mtu32;
/*
* Too large for the destination or interface;
* fragment if possible.
* Must be able to put at least 8 bytes per fragment.
*/
hlen = unfragpartlen;
if (mtu > IPV6_MAXPACKET)
mtu = IPV6_MAXPACKET;
/* Notify a proper path MTU to applications. */
mtu32 = (u_int32_t)mtu;
bzero(&ip6cp, sizeof(ip6cp));
ip6cp.ip6c_cmdarg = (void *)&mtu32;
pfctlinput2(PRC_MSGSIZE, &ro_pmtu->ro_dst, (void *)&ip6cp);
len = (mtu - hlen - sizeof(struct ip6_frag)) & ~7;
if (len < 8) {
error = EMSGSIZE;
in6_ifstat_inc(ifp, ifs6_out_fragfail);
goto bad;
}
mnext = &m->m_nextpkt;
/*
* Change the next header field of the last header in the
* unfragmentable part.
*/
#ifdef MIP6
if (exthdrs.ip6e_haddr) {
nextproto = *mtod(exthdrs.ip6e_haddr, u_char *);
*mtod(exthdrs.ip6e_haddr, u_char *) = IPPROTO_FRAGMENT;
} else
#endif /* MIP6 */
if (exthdrs.ip6e_rthdr) {
nextproto = *mtod(exthdrs.ip6e_rthdr, u_char *);
*mtod(exthdrs.ip6e_rthdr, u_char *) = IPPROTO_FRAGMENT;
} else if (exthdrs.ip6e_dest1) {
nextproto = *mtod(exthdrs.ip6e_dest1, u_char *);
*mtod(exthdrs.ip6e_dest1, u_char *) = IPPROTO_FRAGMENT;
} else if (exthdrs.ip6e_hbh) {
nextproto = *mtod(exthdrs.ip6e_hbh, u_char *);
*mtod(exthdrs.ip6e_hbh, u_char *) = IPPROTO_FRAGMENT;
} else {
nextproto = ip6->ip6_nxt;
ip6->ip6_nxt = IPPROTO_FRAGMENT;
}
/*
* Loop through length of segment after first fragment,
* make new header and copy data of each part and link onto
* chain.
*/
m0 = m;
for (off = hlen; off < tlen; off += len) {
MGETHDR(m, M_DONTWAIT, MT_HEADER);
if (!m) {
error = ENOBUFS;
ip6stat.ip6s_odropped++;
goto sendorfree;
}
m->m_pkthdr.rcvif = NULL;
m->m_flags = m0->m_flags & M_COPYFLAGS;
*mnext = m;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -