Changeset View
Changeset View
Standalone View
Standalone View
sys/netinet6/ip6_output.c
Show First 20 Lines • Show All 136 Lines • ▼ Show 20 Lines | static int ip6_pcbopts(struct ip6_pktopts **, struct mbuf *, | ||||
struct socket *, struct sockopt *); | struct socket *, struct sockopt *); | ||||
static int ip6_getpcbopt(struct inpcb *, int, struct sockopt *); | static int ip6_getpcbopt(struct inpcb *, int, struct sockopt *); | ||||
static int ip6_setpktopt(int, u_char *, int, struct ip6_pktopts *, | static int ip6_setpktopt(int, u_char *, int, struct ip6_pktopts *, | ||||
struct ucred *, int, int, int); | struct ucred *, int, int, int); | ||||
static int ip6_copyexthdr(struct mbuf **, caddr_t, int); | static int ip6_copyexthdr(struct mbuf **, caddr_t, int); | ||||
static int ip6_insertfraghdr(struct mbuf *, struct mbuf *, int, | static int ip6_insertfraghdr(struct mbuf *, struct mbuf *, int, | ||||
struct ip6_frag **); | struct ip6_frag **); | ||||
static int ip6_insert_jumboopt(struct ip6_exthdrs *, u_int32_t); | |||||
static int ip6_splithdr(struct mbuf *, struct ip6_exthdrs *); | static int ip6_splithdr(struct mbuf *, struct ip6_exthdrs *); | ||||
static int ip6_getpmtu(struct route_in6 *, int, | static int ip6_getpmtu(struct route_in6 *, int, | ||||
struct ifnet *, const struct in6_addr *, u_long *, int *, u_int, | struct ifnet *, const struct in6_addr *, u_long *, int *, u_int, | ||||
u_int); | u_int); | ||||
static int ip6_calcmtu(struct ifnet *, const struct in6_addr *, u_long, | static int ip6_calcmtu(struct ifnet *, const struct in6_addr *, u_long, | ||||
u_long *, int *, u_int); | u_long *, int *, u_int); | ||||
static int ip6_getpmtu_ctl(u_int, const struct in6_addr *, u_long *); | static int ip6_getpmtu_ctl(u_int, const struct in6_addr *, u_long *); | ||||
static int copypktopts(struct ip6_pktopts *, struct ip6_pktopts *, int); | static int copypktopts(struct ip6_pktopts *, struct ip6_pktopts *, int); | ||||
▲ Show 20 Lines • Show All 240 Lines • ▼ Show 20 Lines | #endif /* IPSEC */ | ||||
} | } | ||||
ip6 = mtod(m, struct ip6_hdr *); | ip6 = mtod(m, struct ip6_hdr *); | ||||
/* adjust mbuf packet header length */ | /* adjust mbuf packet header length */ | ||||
m->m_pkthdr.len += optlen; | m->m_pkthdr.len += optlen; | ||||
plen = m->m_pkthdr.len - sizeof(*ip6); | plen = m->m_pkthdr.len - sizeof(*ip6); | ||||
/* If this is a jumbo payload, insert a jumbo payload option. */ | |||||
if (plen > IPV6_MAXPACKET) { | if (plen > IPV6_MAXPACKET) { | ||||
if (!hdrsplit) { | |||||
if ((error = ip6_splithdr(m, &exthdrs)) != 0) { | |||||
m = NULL; | |||||
goto freehdrs; | 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 | } else | ||||
ip6->ip6_plen = htons(plen); | ip6->ip6_plen = htons(plen); | ||||
/* | /* | ||||
* Concatenate headers and fill in next header fields. | * Concatenate headers and fill in next header fields. | ||||
* Here we have, on "m" | * Here we have, on "m" | ||||
* IPv6 payload | * IPv6 payload | ||||
* and we insert headers accordingly. Finally, we should be getting: | * and we insert headers accordingly. Finally, we should be getting: | ||||
▲ Show 20 Lines • Show All 316 Lines • ▼ Show 20 Lines | again: | ||||
/* | /* | ||||
* If the outgoing packet contains a hop-by-hop options header, | * If the outgoing packet contains a hop-by-hop options header, | ||||
* it must be examined and processed even by the source node. | * it must be examined and processed even by the source node. | ||||
* (RFC 2460, section 4.) | * (RFC 2460, section 4.) | ||||
*/ | */ | ||||
if (exthdrs.ip6e_hbh) { | if (exthdrs.ip6e_hbh) { | ||||
struct ip6_hbh *hbh = mtod(exthdrs.ip6e_hbh, struct ip6_hbh *); | struct ip6_hbh *hbh = mtod(exthdrs.ip6e_hbh, struct ip6_hbh *); | ||||
u_int32_t dummy; /* XXX unused */ | u_int32_t dummy; /* XXX unused */ | ||||
u_int32_t plen = 0; /* XXX: ip6_process will check the value */ | |||||
#ifdef DIAGNOSTIC | #ifdef DIAGNOSTIC | ||||
if ((hbh->ip6h_len + 1) << 3 > exthdrs.ip6e_hbh->m_len) | if ((hbh->ip6h_len + 1) << 3 > exthdrs.ip6e_hbh->m_len) | ||||
panic("ip6e_hbh is not contiguous"); | panic("ip6e_hbh is not contiguous"); | ||||
#endif | #endif | ||||
/* | /* | ||||
* XXX: if we have to send an ICMPv6 error to the sender, | * XXX: if we have to send an ICMPv6 error to the sender, | ||||
* we need the M_LOOP flag since icmp6_error() expects | * we need the M_LOOP flag since icmp6_error() expects | ||||
* the IPv6 and the hop-by-hop options header are | * the IPv6 and the hop-by-hop options header are | ||||
* contiguous unless the flag is set. | * contiguous unless the flag is set. | ||||
*/ | */ | ||||
m->m_flags |= M_LOOP; | m->m_flags |= M_LOOP; | ||||
m->m_pkthdr.rcvif = ifp; | m->m_pkthdr.rcvif = ifp; | ||||
if (ip6_process_hopopts(m, (u_int8_t *)(hbh + 1), | if (ip6_process_hopopts(m, (u_int8_t *)(hbh + 1), | ||||
((hbh->ip6h_len + 1) << 3) - sizeof(struct ip6_hbh), | ((hbh->ip6h_len + 1) << 3) - sizeof(struct ip6_hbh), | ||||
&dummy, &plen) < 0) { | &dummy) < 0) { | ||||
/* m was already freed at this point */ | /* m was already freed at this point */ | ||||
error = EINVAL;/* better error? */ | error = EINVAL;/* better error? */ | ||||
goto done; | goto done; | ||||
} | } | ||||
m->m_flags &= ~M_LOOP; /* XXX */ | m->m_flags &= ~M_LOOP; /* XXX */ | ||||
m->m_pkthdr.rcvif = NULL; | m->m_pkthdr.rcvif = NULL; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 179 Lines • ▼ Show 20 Lines | #endif | ||||
* try to fragment the packet. case 1-b and 3 | * try to fragment the packet. case 1-b and 3 | ||||
*/ | */ | ||||
if (mtu < IPV6_MMTU) { | if (mtu < IPV6_MMTU) { | ||||
/* path MTU cannot be less than IPV6_MMTU */ | /* path MTU cannot be less than IPV6_MMTU */ | ||||
error = EMSGSIZE; | error = EMSGSIZE; | ||||
in6_ifstat_inc(ifp, ifs6_out_fragfail); | in6_ifstat_inc(ifp, ifs6_out_fragfail); | ||||
goto bad; | goto bad; | ||||
} else if (ip6->ip6_plen == 0) { | } else if (ip6->ip6_plen == 0) { | ||||
/* jumbo payload cannot be fragmented */ | /* we don't support jumbo payload return error */ | ||||
error = EMSGSIZE; | error = EMSGSIZE; | ||||
in6_ifstat_inc(ifp, ifs6_out_fragfail); | in6_ifstat_inc(ifp, ifs6_out_fragfail); | ||||
goto bad; | goto bad; | ||||
} else { | } else { | ||||
u_char nextproto; | u_char nextproto; | ||||
/* | /* | ||||
* Too large for the destination or interface; | * Too large for the destination or interface; | ||||
▲ Show 20 Lines • Show All 130 Lines • ▼ Show 20 Lines | ip6_copyexthdr(struct mbuf **mp, caddr_t hdr, int hlen) | ||||
if (m == NULL) | if (m == NULL) | ||||
return (ENOBUFS); | return (ENOBUFS); | ||||
m->m_len = hlen; | m->m_len = hlen; | ||||
if (hdr) | if (hdr) | ||||
bcopy(hdr, mtod(m, caddr_t), hlen); | bcopy(hdr, mtod(m, caddr_t), hlen); | ||||
*mp = m; | *mp = m; | ||||
return (0); | return (0); | ||||
} | |||||
/* | |||||
* Insert jumbo payload option. | |||||
*/ | |||||
static int | |||||
ip6_insert_jumboopt(struct ip6_exthdrs *exthdrs, u_int32_t plen) | |||||
{ | |||||
struct mbuf *mopt; | |||||
u_char *optbuf; | |||||
u_int32_t v; | |||||
#define JUMBOOPTLEN 8 /* length of jumbo payload option and padding */ | |||||
/* | |||||
* If there is no hop-by-hop options header, allocate new one. | |||||
* If there is one but it doesn't have enough space to store the | |||||
* jumbo payload option, allocate a cluster to store the whole options. | |||||
* Otherwise, use it to store the options. | |||||
*/ | |||||
if (exthdrs->ip6e_hbh == NULL) { | |||||
mopt = m_get(M_NOWAIT, MT_DATA); | |||||
if (mopt == NULL) | |||||
return (ENOBUFS); | |||||
mopt->m_len = JUMBOOPTLEN; | |||||
optbuf = mtod(mopt, u_char *); | |||||
optbuf[1] = 0; /* = ((JUMBOOPTLEN) >> 3) - 1 */ | |||||
exthdrs->ip6e_hbh = mopt; | |||||
} else { | |||||
struct ip6_hbh *hbh; | |||||
mopt = exthdrs->ip6e_hbh; | |||||
if (M_TRAILINGSPACE(mopt) < JUMBOOPTLEN) { | |||||
/* | |||||
* XXX assumption: | |||||
* - exthdrs->ip6e_hbh is not referenced from places | |||||
* other than exthdrs. | |||||
* - exthdrs->ip6e_hbh is not an mbuf chain. | |||||
*/ | |||||
int oldoptlen = mopt->m_len; | |||||
struct mbuf *n; | |||||
/* | |||||
* XXX: give up if the whole (new) hbh header does | |||||
* not fit even in an mbuf cluster. | |||||
*/ | |||||
if (oldoptlen + JUMBOOPTLEN > MCLBYTES) | |||||
return (ENOBUFS); | |||||
/* | |||||
* As a consequence, we must always prepare a cluster | |||||
* at this point. | |||||
*/ | |||||
n = m_getcl(M_NOWAIT, MT_DATA, 0); | |||||
if (n == NULL) | |||||
return (ENOBUFS); | |||||
n->m_len = oldoptlen + JUMBOOPTLEN; | |||||
bcopy(mtod(mopt, caddr_t), mtod(n, caddr_t), | |||||
oldoptlen); | |||||
optbuf = mtod(n, caddr_t) + oldoptlen; | |||||
m_freem(mopt); | |||||
mopt = exthdrs->ip6e_hbh = n; | |||||
} else { | |||||
optbuf = mtod(mopt, u_char *) + mopt->m_len; | |||||
mopt->m_len += JUMBOOPTLEN; | |||||
} | |||||
optbuf[0] = IP6OPT_PADN; | |||||
optbuf[1] = 1; | |||||
/* | |||||
* Adjust the header length according to the pad and | |||||
* the jumbo payload option. | |||||
*/ | |||||
hbh = mtod(mopt, struct ip6_hbh *); | |||||
hbh->ip6h_len += (JUMBOOPTLEN >> 3); | |||||
} | |||||
/* fill in the option. */ | |||||
optbuf[2] = IP6OPT_JUMBO; | |||||
optbuf[3] = 4; | |||||
v = (u_int32_t)htonl(plen + JUMBOOPTLEN); | |||||
bcopy(&v, &optbuf[4], sizeof(u_int32_t)); | |||||
/* finally, adjust the packet header length */ | |||||
exthdrs->ip6e_ip6->m_pkthdr.len += JUMBOOPTLEN; | |||||
return (0); | |||||
#undef JUMBOOPTLEN | |||||
} | } | ||||
/* | /* | ||||
* Insert fragment header and copy unfragmentable header portions. | * Insert fragment header and copy unfragmentable header portions. | ||||
*/ | */ | ||||
static int | static int | ||||
ip6_insertfraghdr(struct mbuf *m0, struct mbuf *m, int hlen, | ip6_insertfraghdr(struct mbuf *m0, struct mbuf *m, int hlen, | ||||
struct ip6_frag **frghdrp) | struct ip6_frag **frghdrp) | ||||
▲ Show 20 Lines • Show All 1,918 Lines • Show Last 20 Lines |