ip6_output.c
来自「eCos操作系统源码」· C语言 代码 · 共 2,527 行 · 第 1/5 页
C
2,527 行
#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 intip6_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 intip6_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 intip6_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 intip6_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__ >= 3intip6_ctloutput(so, sopt) struct socket *so; struct sockopt *sopt;#elseintip6_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; }#if defined(__FreeBSD__) && __FreeBSD__ >= 3 error = sooptcopyin(sopt, &optval, sizeof optval, sizeof optval); if (error) break;#else optval = *mtod(m, int *);#endif switch (optname) { case IPV6_UNICAST_HOPS: if (optval < -1 || optval >= 256) error = EINVAL; else { /* -1 = kernel default */ in6p->in6p_hops = optval;#if defined(__FreeBSD__) && __FreeBSD__ >= 3 if ((in6p->in6p_vflag & INP_IPV4) != 0) in6p->inp_ip_ttl = optval;#endif } break;#define OPTSET(bit) \do { \ if (optval) \ in6p->in6p_flags |= (bit); \ else \ in6p->in6p_flags &= ~(bit); \} while (0)#define OPTSET2292(bit) \do { \ in6p->in6p_flags |= IN6P_RFC2292; \ if (optval) \ in6p->in6p_flags |= (bit); \ else \ in6p->in6p_flags &= ~(bit); \} while (0)#define OPTBIT(bit) (in6p->in6p_flags & (bit) ? 1 : 0) case IPV6_RECVPKTINFO: /* cannot mix with RFC2292 */ if (OPTBIT(IN6P_RFC2292)) { error = EINVAL; break; } OPTSET(IN6P_PKTINFO); if (OPTBIT(IN6P_PKTINFO) == 0) ip6_reset_rcvopt(rcvopts, IPV6_RECVPKTINFO); break; case IPV6_HOPLIMIT: { struct ip6_pktopts **optp; /* cannot mix with RFC2292 */ if (OPTBIT(IN6P_RFC2292)) { error = EINVAL; break; } optp = &in6p->in6p_outputopts; error = ip6_pcbopt(IPV6_HOPLIMIT, (u_char *)&optval, sizeof(optval), optp, privileged); break; } case IPV6_RECVHOPLIMIT: /* cannot mix with RFC2292 */ if (OPTBIT(IN6P_RFC2292)) { error = EINVAL; break; } OPTSET(IN6P_HOPLIMIT); if (OPTBIT(IN6P_HOPLIMIT) == 0) ip6_reset_rcvopt(rcvopts, IPV6_RECVHOPLIMIT); break; case IPV6_RECVHOPOPTS: /* cannot mix with RFC2292 */ if (OPTBIT(IN6P_RFC2292)) { error = EINVAL; break; } OPTSET(IN6P_HOPOPTS); if (OPTBIT(IN6P_HOPOPTS) == 0) ip6_reset_rcvopt(rcvopts, IPV6_RECVHOPOPTS); break; case IPV6_RECVDSTOPTS:
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?