📄 ip6_output.c
字号:
/* check policy */
switch (sp->policy) {
case IPSEC_POLICY_DISCARD:
/*
* This packet is just discarded.
*/
ipsec6stat.out_polvio++;
goto freehdrs;
case IPSEC_POLICY_BYPASS:
case IPSEC_POLICY_NONE:
/* no need to do IPsec. */
needipsec = 0;
break;
case IPSEC_POLICY_IPSEC:
if (sp->req == NULL) {
/* acquire a policy */
error = key_spdacquire(sp);
goto freehdrs;
}
needipsec = 1;
break;
case IPSEC_POLICY_ENTRUST:
default:
printf("ip6_output: Invalid policy found. %d\n", sp->policy);
}
#endif /* OpenBSD */
#endif /* IPSEC */
/*
* Calculate the total length of the extension header chain.
* Keep the length of the unfragmentable part for fragmentation.
*/
optlen = 0;
if (exthdrs.ip6e_hbh) optlen += exthdrs.ip6e_hbh->m_len;
if (exthdrs.ip6e_dest1) optlen += exthdrs.ip6e_dest1->m_len;
if (exthdrs.ip6e_rthdr) optlen += exthdrs.ip6e_rthdr->m_len;
#ifdef MIP6
if (exthdrs.ip6e_haddr) optlen += exthdrs.ip6e_haddr->m_len;
#endif /* MIP6 */
unfragpartlen = optlen + sizeof(struct ip6_hdr);
/* NOTE: we don't add AH/ESP length here. do that later. */
if (exthdrs.ip6e_dest2) optlen += exthdrs.ip6e_dest2->m_len;
/*
* If we need IPsec, or there is at least one extension header,
* separate IP6 header from the payload.
*/
#ifdef __OpenBSD__
if ((sproto || optlen) && !hdrsplit)
#else
if ((needipsec || optlen) && !hdrsplit)
#endif
{
if ((error = ip6_splithdr(m, &exthdrs)) != 0) {
m = NULL;
goto freehdrs;
}
m = exthdrs.ip6e_ip6;
hdrsplit++;
}
/* adjust pointer */
ip6 = mtod(m, struct ip6_hdr *);
/* adjust mbuf packet header length */
m->m_pkthdr.len += optlen;
plen = m->m_pkthdr.len - sizeof(*ip6);
/* If this is a jumbo payload, insert a jumbo payload option. */
if (plen > IPV6_MAXPACKET) {
if (!hdrsplit) {
if ((error = ip6_splithdr(m, &exthdrs)) != 0) {
m = NULL;
goto freehdrs;
}
m = exthdrs.ip6e_ip6;
hdrsplit++;
}
/* adjust pointer */
ip6 = mtod(m, struct ip6_hdr *);
if ((error = ip6_insert_jumboopt(&exthdrs, plen)) != 0)
goto freehdrs;
ip6->ip6_plen = 0;
} else
ip6->ip6_plen = htons(plen);
/*
* Concatenate headers and fill in next header fields.
* Here we have, on "m"
* IPv6 payload
* and we insert headers accordingly. Finally, we should be getting:
* IPv6 hbh dest1 rthdr ah* [esp* dest2 payload]
*
* during the header composing process, "m" points to IPv6 header.
* "mprev" points to an extension header prior to esp.
*/
{
u_char *nexthdrp = &ip6->ip6_nxt;
struct mbuf *mprev = m;
/*
* we treat dest2 specially. this makes IPsec processing
* much easier. the goal here is to make mprev point the
* mbuf prior to dest2.
*
* result: IPv6 dest2 payload
* m and mprev will point to IPv6 header.
*/
if (exthdrs.ip6e_dest2) {
if (!hdrsplit)
panic("assumption failed: hdr not split");
exthdrs.ip6e_dest2->m_next = m->m_next;
m->m_next = exthdrs.ip6e_dest2;
*mtod(exthdrs.ip6e_dest2, u_char *) = ip6->ip6_nxt;
ip6->ip6_nxt = IPPROTO_DSTOPTS;
}
#define MAKE_CHAIN(m, mp, p, i)\
do {\
if (m) {\
if (!hdrsplit) \
panic("assumption failed: hdr not split"); \
*mtod((m), u_char *) = *(p);\
*(p) = (i);\
p = mtod((m), u_char *);\
(m)->m_next = (mp)->m_next;\
(mp)->m_next = (m);\
(mp) = (m);\
}\
} while (0)
/*
* result: IPv6 hbh dest1 rthdr dest2 payload
* m will point to IPv6 header. mprev will point to the
* extension header prior to dest2 (rthdr in the above case).
*/
MAKE_CHAIN(exthdrs.ip6e_hbh, mprev,
nexthdrp, IPPROTO_HOPOPTS);
MAKE_CHAIN(exthdrs.ip6e_dest1, mprev,
nexthdrp, IPPROTO_DSTOPTS);
MAKE_CHAIN(exthdrs.ip6e_rthdr, mprev,
nexthdrp, IPPROTO_ROUTING);
#ifdef MIP6
/*
* XXX
* MIP6 homeaddress destination option must reside
* after rthdr and before ah/esp/frag hdr.
* this order is not recommended in the ipv6 spec of course.
* result: IPv6 hbh dest1 rthdr ha dest2 payload.
*/
MAKE_CHAIN(exthdrs.ip6e_haddr, mprev,
nexthdrp, IPPROTO_DSTOPTS);
#endif /* MIP6 */
#if defined(IPSEC) && !defined(__OpenBSD__)
if (!needipsec)
goto skip_ipsec2;
/*
* pointers after IPsec headers are not valid any more.
* other pointers need a great care too.
* (IPsec routines should not mangle mbufs prior to AH/ESP)
*/
exthdrs.ip6e_dest2 = NULL;
{
struct ip6_rthdr *rh = NULL;
int segleft_org = 0;
struct ipsec_output_state state;
if (exthdrs.ip6e_rthdr) {
rh = mtod(exthdrs.ip6e_rthdr, struct ip6_rthdr *);
segleft_org = rh->ip6r_segleft;
rh->ip6r_segleft = 0;
}
bzero(&state, sizeof(state));
state.m = m;
error = ipsec6_output_trans(&state, nexthdrp, mprev, sp, flags,
&needipsectun);
m = state.m;
if (error) {
/* mbuf is already reclaimed in ipsec6_output_trans. */
m = NULL;
switch (error) {
case EHOSTUNREACH:
case ENETUNREACH:
case EMSGSIZE:
case ENOBUFS:
case ENOMEM:
break;
default:
printf("ip6_output (ipsec): error code %d\n", error);
/* fall through */
case ENOENT:
/* don't show these error codes to the user */
error = 0;
break;
}
goto bad;
}
if (exthdrs.ip6e_rthdr) {
/* ah6_output doesn't modify mbuf chain */
rh->ip6r_segleft = segleft_org;
}
}
skip_ipsec2:;
#endif
}
#ifdef MIP6
if ((flags & IPV6_FORWARDING) == 0) {
/*
* After the IPsec processing the IPv6 header source
* address (this is the homeaddress of this node) and
* the address currently stored in the Home Address
* destination option (this is the coa of this node)
* must be swapped.
*/
if ((error = mip6_addr_exchange(m, exthdrs.ip6e_haddr)) != 0) {
mip6log((LOG_ERR,
"%s:%d: "
"addr exchange between haddr and "
"coa failed.\n",
__FILE__, __LINE__));
goto bad;
}
} else {
/*
* this is the forwarding packet. The typical (and
* only ?) case is multicast packet forwarding. The
* swapping has been already done before (if
* necessary). we must not touch any extension
* headers at all.
*/
}
#endif /* MIP6 */
/*
* If there is a routing header, replace destination address field
* with the first hop of the routing header.
*/
if (exthdrs.ip6e_rthdr) {
struct ip6_rthdr *rh =
(struct ip6_rthdr *)(mtod(exthdrs.ip6e_rthdr,
struct ip6_rthdr *));
struct ip6_rthdr0 *rh0;
struct in6_addr *addr;
finaldst = ip6->ip6_dst;
switch (rh->ip6r_type) {
case IPV6_RTHDR_TYPE_0:
rh0 = (struct ip6_rthdr0 *)rh;
addr = (struct in6_addr *)(rh0 + 1);
ip6->ip6_dst = *addr;
bcopy((caddr_t)(addr + 1), (caddr_t)addr,
sizeof(struct in6_addr) * (rh0->ip6r0_segleft - 1)
);
*(addr + rh0->ip6r0_segleft - 1) = finaldst;
break;
default: /* is it possible? */
error = EINVAL;
goto bad;
}
}
/* Source address validation */
if (!(flags & IPV6_UNSPECSRC) &&
IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src)) {
/*
* XXX: we can probably assume validation in the caller, but
* we explicitly check the address here for safety.
*/
error = EOPNOTSUPP;
ip6stat.ip6s_badscope++;
goto bad;
}
if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_src)) {
error = EOPNOTSUPP;
ip6stat.ip6s_badscope++;
goto bad;
}
ip6stat.ip6s_localout++;
/*
* Route packet.
*/
if (ro == 0) {
ro = &ip6route;
bzero((caddr_t)ro, sizeof(*ro));
}
ro_pmtu = ro;
if (opt && opt->ip6po_rthdr)
ro = &opt->ip6po_route;
#ifdef MIP6
else if (exthdrs.ip6e_rthdr) {
struct sockaddr_in6 *firsthop;
struct ip6_hdr *ip6
= mtod(m, struct ip6_hdr *); /* needed ? */;
ro = &mip6_ip6route;
bzero((caddr_t)ro, sizeof(*ro));
firsthop = (struct sockaddr_in6 *)&ro->ro_dst;
bzero(firsthop, sizeof(*firsthop));
firsthop->sin6_family = AF_INET6;
firsthop->sin6_len = sizeof(struct sockaddr_in6);
firsthop->sin6_addr = ip6->ip6_dst;
}
#endif /* MIP6 */
dst = (struct sockaddr_in6 *)&ro->ro_dst;
#ifdef IPSEC
#ifdef __OpenBSD__
/*
* Check if the packet needs encapsulation.
* ipsp_process_packet will never come back to here.
*/
if (sproto != 0) {
s = splnet();
/* fill in IPv6 header which would be filled later */
if (!IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
if (opt && opt->ip6po_hlim != -1)
ip6->ip6_hlim = opt->ip6po_hlim & 0xff;
} else {
if (im6o != NULL)
ip6->ip6_hlim = im6o->im6o_multicast_hlim;
else
ip6->ip6_hlim = ip6_defmcasthlim;
if (opt && opt->ip6po_hlim != -1)
ip6->ip6_hlim = opt->ip6po_hlim & 0xff;
/*
* XXX what should we do if ip6_hlim == 0 and the packet
* gets tunnelled?
*/
}
tdb = gettdb(sspi, &sdst, sproto);
if (tdb == NULL) {
splx(s);
error = EHOSTUNREACH;
m_freem(m);
goto done;
}
/* Latch to PCB */
if (inp)
tdb_add_inp(tdb, inp, 0);
m->m_flags &= ~(M_BCAST | M_MCAST); /* just in case */
/* Callee frees mbuf */
error = ipsp_process_packet(m, tdb, AF_INET6, 0);
splx(s);
return error; /* Nothing more to be done */
}
#else
if (needipsec && needipsectun) {
struct ipsec_output_state state;
/*
* All the extension headers will become inaccessible
* (since they can be encrypted).
* Don't panic, we need no more updates to extension headers
* on inner IPv6 packet (since they are now encapsulated).
*
* IPv6 [ESP|AH] IPv6 [extension headers] payload
*/
bzero(&exthdrs, sizeof(exthdrs));
exthdrs.ip6e_ip6 = m;
bzero(&state, sizeof(state));
state.m = m;
state.ro = (struct route *)ro;
state.dst = (struct sockaddr *)dst;
error = ipsec6_output_tunnel(&state, sp, flags);
m = state.m;
#ifdef NEW_STRUCT_ROUTE
ro = state.ro;
#else
ro = (struct route_in6 *)state.ro;
#endif
dst = (struct sockaddr_in6 *)state.dst;
if (error) {
/* mbuf is already reclaimed in ipsec6_output_tunnel. */
m0 = m = NULL;
m = NULL;
switch (error) {
case EHOSTUNREACH:
case ENETUNREACH:
case EMSGSIZE:
case ENOBUFS:
case ENOMEM:
break;
default:
printf("ip6_output (ipsec): error code %d\n", error);
/* fall through */
case ENOENT:
/* don't show these error codes to the user */
error = 0;
break;
}
goto bad;
}
exthdrs.ip6e_ip6 = m;
}
#endif /* OpenBSD */
#endif /* IPSEC */
/* if specified, fill in the traffic class field. */
if (opt) {
ip6->ip6_flow &= ~htonl(0xff << 20);
if (opt->ip6po_tclass >= 0)
ip6->ip6_flow |=
htonl((opt->ip6po_tclass & 0xff) << 20);
}
/* fill in or override the hop limit field, if necessary. */
if (opt && opt->ip6po_hlim != -1)
ip6->ip6_hlim = opt->ip6po_hlim & 0xff;
else if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) {
if (im6o != NULL)
ip6->ip6_hlim = im6o->im6o_multicast_hlim;
else
ip6->ip6_hlim = ip6_defmcasthlim;
}
{
/*
* XXX: using a block just to define a local variables is not
* a good style....
*/
struct ifnet *ifp0 = NULL;
struct sockaddr_in6 src;
struct sockaddr_in6 dst0;
int clone = 0;
int64_t zone;
/*
* XXX: sockaddr_in6 for the destination should be passed
* from the upper layer with a proper scope zone ID, in order
* to make a copy here.
*/
bzero(&dst0, sizeof(dst0));
dst0.sin6_family = AF_INET6;
dst0.sin6_len = sizeof(dst0);
dst0.sin6_addr = ip6->ip6_dst;
#ifdef SCOPEDROUTING
/* XXX: in6_recoverscope will clear the embedded ID */
error = in6_recoverscope(&dst0, &dst0.sin6_addr, NULL);
if (error != 0) {
ip6stat.ip6s_badscope++;
in6_ifstat_inc(ifp, ifs6_out_discard);
goto bad;
}
#endif
#if defined(__bsdi__) || defined(__FreeBSD__)
if (ro != &ip6route && !IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst))
clone = 1;
#endif
if ((error = in6_selectroute(&dst0, opt, im6o, ro,
&ifp, &rt, clone)) != 0) {
switch (error) {
case EHOSTUNREACH:
ip6stat.ip6s_noroute++;
break;
case EADDRNOTAVAIL:
default:
break; /* XXX statistics? */
}
if (ifp != NULL)
in6_ifstat_inc(ifp, ifs6_out_discard);
goto bad;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -