📄 ip6_output.c
字号:
mnext = &m->m_nextpkt;
m->m_data += max_linkhdr;
mhip6 = mtod(m, struct ip6_hdr *);
*mhip6 = *ip6;
m->m_len = sizeof(*mhip6);
error = ip6_insertfraghdr(m0, m, hlen, &ip6f);
if (error) {
ip6stat.ip6s_odropped++;
goto sendorfree;
}
ip6f->ip6f_offlg = htons((u_short)((off - hlen) & ~7));
if (off + len >= tlen)
len = tlen - off;
else
ip6f->ip6f_offlg |= IP6F_MORE_FRAG;
mhip6->ip6_plen = htons((u_short)(len + hlen +
sizeof(*ip6f) -
sizeof(struct ip6_hdr)));
if ((m_frgpart = m_copy(m0, off, len)) == 0) {
error = ENOBUFS;
ip6stat.ip6s_odropped++;
goto sendorfree;
}
m_cat(m, m_frgpart);
m->m_pkthdr.len = len + hlen + sizeof(*ip6f);
m->m_pkthdr.rcvif = (struct ifnet *)0;
ip6f->ip6f_reserved = 0;
ip6f->ip6f_ident = id;
ip6f->ip6f_nxt = nextproto;
ip6stat.ip6s_ofragments++;
in6_ifstat_inc(ifp, ifs6_out_fragcreat);
}
in6_ifstat_inc(ifp, ifs6_out_fragok);
}
/*
* Remove leading garbages.
*/
sendorfree:
m = m0->m_nextpkt;
m0->m_nextpkt = 0;
m_freem(m0);
for (m0 = m; m; m = m0) {
m0 = m->m_nextpkt;
m->m_nextpkt = 0;
if (error == 0) {
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);
} else
m_freem(m);
}
if (error == 0)
ip6stat.ip6s_fragmented++;
done:
if (ro == &ip6route && ro->ro_rt) { /* brace necessary for RTFREE */
RTFREE(ro->ro_rt);
} else if (ro_pmtu == &ip6route && ro_pmtu->ro_rt) {
RTFREE(ro_pmtu->ro_rt);
}
#if defined(IPSEC) && !defined(__OpenBSD__)
if (sp != NULL)
key_freesp(sp);
#endif /* IPSEC */
#ifdef MIP6
mip6_destopt_discard(&mip6opt);
#endif /* MIP6 */
return(error);
freehdrs:
#ifdef MIP6
mip6_destopt_discard(&mip6opt);
#endif /* MIP6 */
m_freem(exthdrs.ip6e_hbh); /* m_freem will check if mbuf is 0 */
m_freem(exthdrs.ip6e_dest1);
m_freem(exthdrs.ip6e_rthdr);
#ifdef MIP6
m_freem(exthdrs.ip6e_haddr);
#endif /* MIP6 */
m_freem(exthdrs.ip6e_dest2);
/* fall through */
bad:
m_freem(m);
goto done;
}
static int
ip6_copyexthdr(mp, hdr, hlen)
struct mbuf **mp;
caddr_t hdr;
int hlen;
{
struct mbuf *m;
if (hlen > MCLBYTES)
return(ENOBUFS); /* XXX */
MGET(m, M_DONTWAIT, MT_DATA);
if (!m)
return(ENOBUFS);
if (hlen > MLEN) {
MCLGET(m, M_DONTWAIT);
if ((m->m_flags & M_EXT) == 0) {
m_free(m);
return(ENOBUFS);
}
}
m->m_len = hlen;
if (hdr)
bcopy(hdr, mtod(m, caddr_t), hlen);
*mp = m;
return(0);
}
/*
* Insert jumbo payload option.
*/
static int
ip6_insert_jumboopt(exthdrs, plen)
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 == 0) {
MGET(mopt, M_DONTWAIT, MT_DATA);
if (mopt == 0)
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.
*/
MGET(n, M_DONTWAIT, MT_DATA);
if (n) {
MCLGET(n, M_DONTWAIT);
if ((n->m_flags & M_EXT) == 0) {
m_freem(n);
n = NULL;
}
}
if (!n)
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.
*/
static int
ip6_insertfraghdr(m0, m, hlen, frghdrp)
struct mbuf *m0, *m;
int hlen;
struct ip6_frag **frghdrp;
{
struct mbuf *n, *mlast;
if (hlen > sizeof(struct ip6_hdr)) {
n = m_copym(m0, sizeof(struct ip6_hdr),
hlen - sizeof(struct ip6_hdr), M_DONTWAIT);
if (n == 0)
return(ENOBUFS);
m->m_next = n;
} else
n = m;
/* Search for the last mbuf of unfragmentable part. */
for (mlast = n; mlast->m_next; mlast = mlast->m_next)
;
if ((mlast->m_flags & M_EXT) == 0 &&
M_TRAILINGSPACE(mlast) >= sizeof(struct ip6_frag)) {
/* use the trailing space of the last mbuf for the fragment hdr */
*frghdrp =
(struct ip6_frag *)(mtod(mlast, caddr_t) + mlast->m_len);
mlast->m_len += sizeof(struct ip6_frag);
m->m_pkthdr.len += sizeof(struct ip6_frag);
} else {
/* allocate a new mbuf for the fragment header */
struct mbuf *mfrg;
MGET(mfrg, M_DONTWAIT, MT_DATA);
if (mfrg == 0)
return(ENOBUFS);
mfrg->m_len = sizeof(struct ip6_frag);
*frghdrp = mtod(mfrg, struct ip6_frag *);
mlast->m_next = mfrg;
}
return(0);
}
static int
ip6_getpmtu(ro_pmtu, ro, ifp, dst, mtup)
#ifdef NEW_STRUCT_ROUTE
struct route *ro_pmtu, *ro;
#else
struct route_in6 *ro_pmtu, *ro;
#endif
struct ifnet *ifp;
struct in6_addr *dst; /* XXX: should be sockaddr_in6 */
u_long *mtup;
{
u_int32_t mtu = 0;
int error = 0;
if (ro_pmtu != ro) {
/* The first hop and the final destination may differ. */
struct sockaddr_in6 *sa6_dst =
(struct sockaddr_in6 *)&ro_pmtu->ro_dst;
if (ro_pmtu->ro_rt && ((ro_pmtu->ro_rt->rt_flags & RTF_UP)
== 0 ||
!IN6_ARE_ADDR_EQUAL(&sa6_dst->sin6_addr,
dst))) {
RTFREE(ro_pmtu->ro_rt);
ro_pmtu->ro_rt = (struct rtentry *)NULL;
}
if (ro_pmtu->ro_rt == NULL) {
bzero(sa6_dst, sizeof(*sa6_dst));
sa6_dst->sin6_family = AF_INET6;
sa6_dst->sin6_len = sizeof(struct sockaddr_in6);
sa6_dst->sin6_addr = *dst;
#ifdef __bsdi__ /* bsdi needs rtcalloc to clone a route. */
rtcalloc((struct route *)ro_pmtu);
#else
rtalloc((struct route *)ro_pmtu);
#endif
}
}
if (ro_pmtu->ro_rt) {
u_int32_t ifmtu;
if (ifp == NULL)
ifp = ro_pmtu->ro_rt->rt_ifp;
ifmtu = nd_ifinfo[ifp->if_index].linkmtu;
mtu = ro_pmtu->ro_rt->rt_rmx.rmx_mtu;
if (mtu > ifmtu || mtu == 0) {
/*
* The MTU on the route is larger than the MTU on
* the interface! This shouldn't happen, unless the
* MTU of the interface has been changed after the
* interface was brought up. Change the MTU in the
* route to match the interface MTU (as long as the
* field isn't locked).
*
* if MTU on the route is 0, we need to fix the MTU.
* this case happens with path MTU discovery timeouts.
*/
mtu = ifmtu;
if ((ro_pmtu->ro_rt->rt_rmx.rmx_locks & RTV_MTU) == 0)
ro_pmtu->ro_rt->rt_rmx.rmx_mtu = mtu; /* XXX */
}
} else if (ifp) {
mtu = nd_ifinfo[ifp->if_index].linkmtu;
} else
error = EHOSTUNREACH; /* XXX */
*mtup = mtu;
return(error);
}
/*
* IP6 socket option processing.
*/
#if defined(__FreeBSD__) && __FreeBSD__ >= 3
int
ip6_ctloutput(so, sopt)
struct socket *so;
struct sockopt *sopt;
#else
int
ip6_ctloutput(op, so, level, optname, mp)
int op;
struct socket *so;
int level, optname;
struct mbuf **mp;
#endif
{
int privileged, optdatalen;
void *optdata;
struct ip6_recvpktopts *rcvopts;
#if defined(IPSEC) && defined(__OpenBSD__)
struct proc *p = curproc; /* XXX */
struct tdb *tdb;
struct tdb_ident *tdbip, tdbi;
int s;
#endif
#if defined(__FreeBSD__) && __FreeBSD__ >= 3
struct inpcb *in6p = sotoinpcb(so);
int error, optval;
int level, op, optname;
int optlen;
struct proc *p;
if (!sopt) {
panic("ip6_ctloutput: arg soopt is NULL");
}
level = sopt->sopt_level;
op = sopt->sopt_dir;
optname = sopt->sopt_name;
optlen = sopt->sopt_valsize;
p = sopt->sopt_p;
#else
#ifdef HAVE_NRL_INPCB
struct inpcb *inp = sotoinpcb(so);
#define in6p inp
#else /* !NRL */
struct in6pcb *in6p = sotoin6pcb(so);
#endif /* HAVE_NRL_INPCB */
struct mbuf *m = *mp;
int error, optval;
int optlen;
#if defined(__NetBSD__) || (defined(__FreeBSD__) && __FreeBSD__ >= 3)
struct proc *p = curproc; /* XXX */
#endif
optlen = m ? m->m_len : 0;
#endif /* FreeBSD >= 3 */
error = optval = 0;
privileged = 1;
rcvopts = in6p->in6p_inputopts;
if (level == IPPROTO_IPV6) {
switch (op) {
#if defined(__FreeBSD__) && __FreeBSD__ >= 3
case SOPT_SET:
#else
case PRCO_SETOPT:
#endif
switch (optname) {
case IPV6_2292PKTOPTIONS:
#ifdef IPV6_PKTOPTIONS
case IPV6_PKTOPTIONS:
#endif
{
#if defined(__FreeBSD__) && __FreeBSD__ >= 3
struct mbuf *m;
error = soopt_getm(sopt, &m); /* XXX */
if (error)
break;
error = soopt_mcopyin(sopt, m); /* XXX */
if (error)
break;
error = ip6_pcbopts(&in6p->in6p_outputopts,
m, so, sopt);
m_freem(m); /* XXX */
#else
error = ip6_pcbopts(&in6p->in6p_outputopts,
m, so);
#endif /* FreeBSD >= 3 */
break;
}
/*
* Use of some Hop-by-Hop options or some
* Destination options, might require special
* privilege. That is, normal applications
* (without special privilege) might be forbidden
* from setting certain options in outgoing packets,
* and might never see certain options in received
* packets. [RFC 2292 Section 6]
* KAME specific note:
* KAME prevents non-privileged users from sending or
* receiving ANY hbh/dst options in order to avoid
* overhead of parsing options in the kernel.
*/
case IPV6_RECVHOPOPTS:
case IPV6_RECVDSTOPTS:
case IPV6_RECVRTHDRDSTOPTS:
/* fall through */
case IPV6_UNICAST_HOPS:
case IPV6_HOPLIMIT:
case IPV6_FAITH:
case IPV6_RECVPKTINFO:
case IPV6_RECVHOPLIMIT:
case IPV6_RECVRTHDR:
case IPV6_RECVPATHMTU:
case IPV6_RECVTCLASS:
case IPV6_V6ONLY:
case IPV6_AUTOFLOWLABEL:
if (optlen != sizeof(int)) {
error = EINVAL;
break;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -