ip_output.c
来自「eCos操作系统源码」· C语言 代码 · 共 1,761 行 · 第 1/3 页
C
1,761 行
//==========================================================================//// sys/netinet/ip_output.c//// ////==========================================================================//####BSDCOPYRIGHTBEGIN####//// -------------------------------------------//// Portions of this software may have been derived from OpenBSD or other sources,// and are covered by the appropriate copyright disclaimers included herein.//// -------------------------------------------////####BSDCOPYRIGHTEND####//==========================================================================//#####DESCRIPTIONBEGIN####//// Author(s): gthomas// Contributors: gthomas// Date: 2000-01-10// Purpose: // Description: // ////####DESCRIPTIONEND####////==========================================================================/* $OpenBSD: ip_output.c,v 1.57 1999/12/10 08:55:23 angelos Exp $ *//* $NetBSD: ip_output.c,v 1.28 1996/02/13 23:43:07 christos Exp $ *//* * Copyright (c) 1982, 1986, 1988, 1990, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)ip_output.c 8.3 (Berkeley) 1/21/94 */#include <sys/param.h>#include <sys/malloc.h>#include <sys/mbuf.h>#include <sys/errno.h>#include <sys/protosw.h>#include <sys/socket.h>#include <sys/socketvar.h>#ifndef __ECOS#include <sys/systm.h>#endif#include <sys/kernel.h>#ifndef __ECOS#include <sys/proc.h>#include <vm/vm.h>#include <sys/proc.h>#endif#include <net/if.h>#include <net/route.h>#include <netinet/in.h>#include <netinet/in_systm.h>#include <netinet/ip.h>#include <netinet/in_pcb.h>#include <netinet/in_var.h>#include <netinet/ip_var.h>#ifdef vax#include <machine/mtpr.h>#endif#include <machine/stdarg.h>#ifdef IPSEC#include <netinet/ip_ah.h>#include <netinet/ip_esp.h>#include <netinet/udp.h>#include <netinet/tcp.h>#include <net/pfkeyv2.h>#ifdef ENCDEBUG#define DPRINTF(x) do { if (encdebug) printf x ; } while (0)#else#define DPRINTF(x)#endif#ifndef offsetof#define offsetof(s, e) ((int)&((s *)0)->e)#endifextern u_int8_t get_sa_require __P((struct inpcb *));#endif /* IPSEC */static struct mbuf *ip_insertoptions __P((struct mbuf *, struct mbuf *, int *));static void ip_mloopback __P((struct ifnet *, struct mbuf *, struct sockaddr_in *));#if defined(IPFILTER) || defined(IPFILTER_LKM)int (*fr_checkp) __P((struct ip *, int, struct ifnet *, int, struct mbuf **));#endif#ifdef IPSECextern int ipsec_auth_default_level;extern int ipsec_esp_trans_default_level;extern int ipsec_esp_network_default_level;extern int pfkeyv2_acquire(struct tdb *, int);#endif#ifndef RAMDOM_IP_IDu_short ip_id;#endif/* * IP output. The packet in mbuf chain m contains a skeletal IP * header (with len, off, ttl, proto, tos, src, dst). * The mbuf chain containing the packet will be freed. * The mbuf opt, if present, will not be freed. */int#if __STDC__ip_output(struct mbuf *m0, ...)#elseip_output(m0, va_alist) struct mbuf *m0; va_dcl#endif{ register struct ip *ip, *mhip; register struct ifnet *ifp; register struct mbuf *m = m0; register int hlen = sizeof (struct ip); int len, off, error = 0; struct route iproute; struct sockaddr_in *dst; struct in_ifaddr *ia; struct mbuf *opt; struct route *ro; int flags; struct ip_moptions *imo; va_list ap;#ifdef IPSEC union sockaddr_union sunion; struct mbuf *mp; struct udphdr *udp; struct tcphdr *tcp; struct inpcb *inp; struct route_enc re0, *re = &re0; struct sockaddr_encap *ddst, *gw; u_int8_t sa_require, sa_have = 0; struct tdb *tdb, *t; int s, ip6flag;#ifdef INET6 struct ip6_hdr *ip6;#endif /* INET6 */#endif /* IPSEC */ va_start(ap, m0); opt = va_arg(ap, struct mbuf *); ro = va_arg(ap, struct route *); flags = va_arg(ap, int); imo = va_arg(ap, struct ip_moptions *);#ifdef IPSEC inp = va_arg(ap, struct inpcb *);#endif /* IPSEC */ va_end(ap);#ifdef DIAGNOSTIC if ((m->m_flags & M_PKTHDR) == 0) panic("ip_output no HDR");#endif if (opt) { m = ip_insertoptions(m, opt, &len); hlen = len; } ip = mtod(m, struct ip *); /* * Fill in IP header. */ if ((flags & (IP_FORWARDING|IP_RAWOUTPUT)) == 0) { ip->ip_v = IPVERSION; ip->ip_off &= IP_DF;#ifdef RANDOM_IP_ID ip->ip_id = ip_randomid();#else ip->ip_id = htons(ip_id++);#endif ip->ip_hl = hlen >> 2; ipstat.ips_localout++; } else { hlen = ip->ip_hl << 2; } /* * Route packet. */ if (ro == 0) { ro = &iproute; bzero((caddr_t)ro, sizeof (*ro)); } dst = satosin(&ro->ro_dst); /* * If there is a cached route, * check that it is to the same destination * and is still up. If not, free it and try again. */ if (ro->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 || dst->sin_addr.s_addr != ip->ip_dst.s_addr)) { RTFREE(ro->ro_rt); ro->ro_rt = (struct rtentry *)0; } if (ro->ro_rt == 0) { dst->sin_family = AF_INET; dst->sin_len = sizeof(*dst); dst->sin_addr = ip->ip_dst; } /* * If routing to interface only, * short circuit routing lookup. */ if (flags & IP_ROUTETOIF) { if ((ia = ifatoia(ifa_ifwithdstaddr(sintosa(dst)))) == 0 && (ia = ifatoia(ifa_ifwithnet(sintosa(dst)))) == 0) { ipstat.ips_noroute++; error = ENETUNREACH; goto bad; } ifp = ia->ia_ifp; ip->ip_ttl = 1; } else { if (ro->ro_rt == 0) rtalloc(ro); if (ro->ro_rt == 0) { ipstat.ips_noroute++; error = EHOSTUNREACH; goto bad; } ia = ifatoia(ro->ro_rt->rt_ifa); ifp = ro->ro_rt->rt_ifp; ro->ro_rt->rt_use++; if (ro->ro_rt->rt_flags & RTF_GATEWAY) dst = satosin(ro->ro_rt->rt_gateway); } if (IN_MULTICAST(ip->ip_dst.s_addr)) { struct in_multi *inm; m->m_flags |= M_MCAST; /* * IP destination address is multicast. Make sure "dst" * still points to the address in "ro". (It may have been * changed to point to a gateway address, above.) */ dst = satosin(&ro->ro_dst); /* * See if the caller provided any multicast options */ if (imo != NULL) { ip->ip_ttl = imo->imo_multicast_ttl; if (imo->imo_multicast_ifp != NULL) ifp = imo->imo_multicast_ifp; } else ip->ip_ttl = IP_DEFAULT_MULTICAST_TTL; /* * Confirm that the outgoing interface supports multicast. */ if ((ifp->if_flags & IFF_MULTICAST) == 0) { ipstat.ips_noroute++; error = ENETUNREACH; goto bad; } /* * If source address not specified yet, use address * of outgoing interface. */ if (ip->ip_src.s_addr == INADDR_ANY) { register struct in_ifaddr *ia; for (ia = in_ifaddr.tqh_first; ia; ia = ia->ia_list.tqe_next) if (ia->ia_ifp == ifp) { ip->ip_src = ia->ia_addr.sin_addr; break; } } IN_LOOKUP_MULTI(ip->ip_dst, ifp, inm); if (inm != NULL && (imo == NULL || imo->imo_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. */ ip_mloopback(ifp, m, dst); }#ifdef MROUTING 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 * IP_FORWARDING flag to prevent infinite recursion. * * Multicasts that are looped back by ip_mloopback(), * above, will be forwarded by the ip_input() routine, * if necessary. */ extern struct socket *ip_mrouter; if (ip_mrouter && (flags & IP_FORWARDING) == 0) { if (ip_mforward(m, ifp) != 0) { m_freem(m); goto done; } } }#endif /* * Multicasts with a time-to-live 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 ip_mloopback() will * loop back a copy if this host actually belongs to the * destination group on the loopback interface. */ if (ip->ip_ttl == 0 || (ifp->if_flags & IFF_LOOPBACK) != 0) { m_freem(m); goto done; } goto sendit; }#ifndef notdef /* * If source address not specified yet, use address * of outgoing interface. */ if (ip->ip_src.s_addr == INADDR_ANY) ip->ip_src = ia->ia_addr.sin_addr;#endif /* * Look for broadcast address and * and verify user is allowed to send * such a packet. */ if (in_broadcast(dst->sin_addr, ifp)) { if ((ifp->if_flags & IFF_BROADCAST) == 0) { error = EADDRNOTAVAIL; goto bad; } if ((flags & IP_ALLOWBROADCAST) == 0) { error = EACCES; goto bad; } /* don't allow broadcast messages to be fragmented */ if ((u_int16_t)ip->ip_len > ifp->if_mtu) { error = EMSGSIZE; goto bad; } m->m_flags |= M_BCAST; } else m->m_flags &= ~M_BCAST;sendit:#ifdef IPSEC /* * Check if the packet needs encapsulation. */ if (!(flags & IP_ENCAPSULATED) && (inp == NULL || inp->inp_seclevel[SL_AUTH] != IPSEC_LEVEL_BYPASS || inp->inp_seclevel[SL_ESP_TRANS] != IPSEC_LEVEL_BYPASS || inp->inp_seclevel[SL_ESP_NETWORK] != IPSEC_LEVEL_BYPASS)) { if (inp == NULL) sa_require = get_sa_require(inp); else sa_require = inp->inp_secrequire; bzero((caddr_t) re, sizeof(*re)); /* * splnet is chosen over spltdb because we are not allowed to * lower the level, and udp_output calls us in splnet(). */ s = splnet(); /* * Check if there was an outgoing SA bound to the flow * from a transport protocol. */ if (inp && inp->inp_tdb && (inp->inp_tdb->tdb_dst.sin.sin_addr.s_addr == INADDR_ANY || !bcmp(&inp->inp_tdb->tdb_dst.sin.sin_addr, &ip->ip_dst, sizeof(ip->ip_dst)))) { tdb = inp->inp_tdb; goto have_tdb; } if (!ipsec_in_use) { splx(s); goto no_encap; } ddst = (struct sockaddr_encap *) &re->re_dst; ddst->sen_family = PF_KEY; ddst->sen_len = SENT_IP4_LEN; ddst->sen_type = SENT_IP4; ddst->sen_ip_src = ip->ip_src; ddst->sen_ip_dst = ip->ip_dst; ddst->sen_proto = ip->ip_p; switch (ip->ip_p) { case IPPROTO_UDP: if (m->m_len < hlen + 2 * sizeof(u_int16_t)) { if ((m = m_pullup(m, hlen + 2 * sizeof(u_int16_t))) == 0) return ENOBUFS; ip = mtod(m, struct ip *); } udp = (struct udphdr *) (mtod(m, u_char *) + hlen); ddst->sen_sport = ntohs(udp->uh_sport); ddst->sen_dport = ntohs(udp->uh_dport); break; case IPPROTO_TCP: if (m->m_len < hlen + 2 * sizeof(u_int16_t)) { if ((m = m_pullup(m, hlen + 2 * sizeof(u_int16_t))) == 0) return ENOBUFS; ip = mtod(m, struct ip *); } tcp = (struct tcphdr *) (mtod(m, u_char *) + hlen); ddst->sen_sport = ntohs(tcp->th_sport); ddst->sen_dport = ntohs(tcp->th_dport); break; default: ddst->sen_sport = 0; ddst->sen_dport = 0; } rtalloc((struct route *) re); if (re->re_rt == NULL) { splx(s); goto no_encap; } gw = (struct sockaddr_encap *) (re->re_rt->rt_gateway); /* Sanity check */ if (gw == NULL || ((gw->sen_type != SENT_IPSP) && (gw->sen_type != SENT_IPSP6))) { splx(s); DPRINTF(("ip_output(): no gw or gw data not IPSP\n")); if (re->re_rt) RTFREE(re->re_rt); error = EHOSTUNREACH; m_freem(m); goto done; } /* * There might be a specific route, that tells us to avoid * doing IPsec; this is useful for specific routes that we * don't want to have IPsec applied on, like the key * management ports. */ if ((gw != NULL) && (gw->sen_ipsp_sproto == 0) && (gw->sen_ipsp_spi == 0)) { if ((gw->sen_family == AF_INET) && (gw->sen_ipsp_dst.s_addr == 0)) { splx(s); goto no_encap; }#ifdef INET6 if ((gw->sen_family == AF_INET6) && IN6_IS_ADDR_UNSPECIFIED(&gw->sen_ipsp6_dst)) { splx(s); goto no_encap; }#endif /* INET6 */ } /* * At this point we have an IPSP "gateway" (tunnel) spec. * Use the destination of the tunnel and the SPI to * look up the necessary Tunnel Control Block. Look it up, * and then pass it, along with the packet and the gw, * to the appropriate transformation. */ bzero(&sunion, sizeof(sunion)); if (gw->sen_type == SENT_IPSP) { sunion.sin.sin_family = AF_INET; sunion.sin.sin_len = sizeof(struct sockaddr_in); sunion.sin.sin_addr = gw->sen_ipsp_dst; }#ifdef INET6 if (gw->sen_type == SENT_IPSP6) { sunion.sin6.sin6_family = AF_INET6; sunion.sin6.sin6_len = sizeof(struct sockaddr_in6); sunion.sin6.sin6_addr = gw->sen_ipsp6_dst; }#endif /* INET6 */ tdb = (struct tdb *) gettdb(gw->sen_ipsp_spi, &sunion, gw->sen_ipsp_sproto); /* * For VPNs a route with a reserved SPI is used to * indicate the need for an SA when none is established. */ if (((ntohl(gw->sen_ipsp_spi) == SPI_LOCAL_USE) && (gw->sen_type == SENT_IPSP)) || ((ntohl(gw->sen_ipsp6_spi) == SPI_LOCAL_USE) && (gw->sen_type == SENT_IPSP6))) { if (tdb == NULL) { /* * XXX We should construct a TDB from system * default (which should be tunable via sysctl). * For now, drop packet and ignore SPD entry. */ splx(s); goto no_encap; } else { if (tdb->tdb_authalgxform) sa_require = NOTIFY_SATYPE_AUTH; if (tdb->tdb_encalgxform) sa_require |= NOTIFY_SATYPE_CONF; if (tdb->tdb_flags & TDBF_TUNNELING) sa_require |= NOTIFY_SATYPE_TUNNEL; } /* PF_KEYv2 notification message */ if (tdb && tdb->tdb_satype != SADB_X_SATYPE_BYPASS) if ((error = pfkeyv2_acquire(tdb, 0)) != 0) return error; splx(s); /* * When sa_require is set, the packet will be dropped * at no_encap. */ goto no_encap; } have_tdb:
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?