ip_output.c
来自「eCos操作系统源码」· C语言 代码 · 共 1,972 行 · 第 1/4 页
C
1,972 行
//==========================================================================//// src/sys/netinet/ip_output.c////==========================================================================//####BSDCOPYRIGHTBEGIN####//// -------------------------------------------//// Portions of this software may have been derived from OpenBSD, // FreeBSD or other sources, and are covered by the appropriate// copyright disclaimers included herein.//// Portions created by Red Hat are// Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.//// -------------------------------------------////####BSDCOPYRIGHTEND####//==========================================================================/* * 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 * $FreeBSD: src/sys/netinet/ip_output.c,v 1.99.2.16 2001/07/19 06:37:26 kris Exp $ */#define _IP_VHL#include <sys/param.h>#include <sys/malloc.h>#include <sys/mbuf.h>#include <sys/protosw.h>#include <sys/socket.h>#include <sys/socketvar.h>#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 IPSEC#include <netinet6/ipsec.h>#include <netkey/key.h>#ifdef IPSEC_DEBUG#include <netkey/key_debug.h>#else#define KEYDEBUG(lev,arg)#endif#endif /*IPSEC*/#include <netinet/ip_fw.h>#ifdef DUMMYNET#include <netinet/ip_dummynet.h>#endif#ifdef IPFIREWALL_FORWARD_DEBUG#define print_ip(a) printf("%ld.%ld.%ld.%ld",(ntohl(a.s_addr)>>24)&0xFF,\ (ntohl(a.s_addr)>>16)&0xFF,\ (ntohl(a.s_addr)>>8)&0xFF,\ (ntohl(a.s_addr))&0xFF);#endifu_short ip_id;static struct mbuf *ip_insertoptions __P((struct mbuf *, struct mbuf *, int *));static struct ifnet *ip_multicast_if __P((struct in_addr *, int *));static void ip_mloopback __P((struct ifnet *, struct mbuf *, struct sockaddr_in *, int));static int ip_getmoptions __P((struct sockopt *, struct ip_moptions *));static int ip_pcbopts __P((int, struct mbuf **, struct mbuf *));static int ip_setmoptions __P((struct sockopt *, struct ip_moptions **));int ip_optcopy __P((struct ip *, struct ip *));extern int (*fr_checkp) __P((struct ip *, int, struct ifnet *, int, struct mbuf **));extern struct protosw inetsw[];/* * 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. */intip_output(m0, opt, ro, flags, imo) struct mbuf *m0; struct mbuf *opt; struct route *ro; int flags; struct ip_moptions *imo;{ struct ip *ip, *mhip; struct ifnet *ifp; struct mbuf *m = m0; int hlen = sizeof (struct ip); int len, off, error = 0; struct sockaddr_in *dst; struct in_ifaddr *ia = NULL; int isbroadcast, sw_csum;#ifdef IPSEC struct route iproute; struct socket *so = NULL; struct secpolicy *sp = NULL;#endif u_int16_t divert_cookie; /* firewall cookie */#ifdef IPFIREWALL_FORWARD int fwd_rewrite_src = 0;#endif struct ip_fw_chain *rule = NULL; #ifdef IPDIVERT /* Get and reset firewall cookie */ divert_cookie = ip_divert_cookie; ip_divert_cookie = 0;#else divert_cookie = 0;#endif#if defined(IPFIREWALL) && defined(DUMMYNET) /* * dummynet packet are prepended a vestigial mbuf with * m_type = MT_DUMMYNET and m_data pointing to the matching * rule. */ if (m->m_type == MT_DUMMYNET) { /* * the packet was already tagged, so part of the * processing was already done, and we need to go down. * Get parameters from the header. */ rule = (struct ip_fw_chain *)(m->m_data) ; opt = NULL ; ro = & ( ((struct dn_pkt *)m)->ro ) ; imo = NULL ; dst = ((struct dn_pkt *)m)->dn_dst ; ifp = ((struct dn_pkt *)m)->ifp ; flags = ((struct dn_pkt *)m)->flags ; m0 = m = m->m_next ;#ifdef IPSEC so = ipsec_getsocket(m); (void)ipsec_setsocket(m, NULL);#endif ip = mtod(m, struct ip *); hlen = IP_VHL_HL(ip->ip_vhl) << 2 ; if (ro->ro_rt != NULL) ia = (struct in_ifaddr *)ro->ro_rt->rt_ifa; goto sendit; } else rule = NULL ;#endif#ifdef IPSEC so = ipsec_getsocket(m); (void)ipsec_setsocket(m, NULL);#endif#ifdef DIAGNOSTIC if ((m->m_flags & M_PKTHDR) == 0) panic("ip_output no HDR"); if (!ro) panic("ip_output no route, proto = %d", mtod(m, struct ip *)->ip_p);#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_vhl = IP_MAKE_VHL(IPVERSION, hlen >> 2); ip->ip_off &= IP_DF;#ifdef RANDOM_IP_ID ip->ip_id = ip_randomid();#else ip->ip_id = htons(ip_id++);#endif ipstat.ips_localout++; } else { hlen = IP_VHL_HL(ip->ip_vhl) << 2; } dst = (struct sockaddr_in *)&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. */#define ifatoia(ifa) ((struct in_ifaddr *)(ifa))#define sintosa(sin) ((struct sockaddr *)(sin)) 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; isbroadcast = in_broadcast(dst->sin_addr, ifp); } else { /* * If this is the case, we probably don't want to allocate * a protocol-cloned route since we didn't get one from the * ULP. This lets TCP do its thing, while not burdening * forwarding or ICMP with the overhead of cloning a route. * Of course, we still want to do any cloning requested by * the link layer, as this is probably required in all cases * for correct operation (as it is for ARP). */ if (ro->ro_rt == 0) rtalloc_ign(ro, RTF_PRCLONING); 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 = (struct sockaddr_in *)ro->ro_rt->rt_gateway; if (ro->ro_rt->rt_flags & RTF_HOST) isbroadcast = (ro->ro_rt->rt_flags & RTF_BROADCAST); else isbroadcast = in_broadcast(dst->sin_addr, ifp); } if (IN_MULTICAST(ntohl(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 = (struct sockaddr_in *)&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; if (imo->imo_multicast_vif != -1) ip->ip_src.s_addr = ip_mcast_src(imo->imo_multicast_vif); } else ip->ip_ttl = IP_DEFAULT_MULTICAST_TTL; /* * Confirm that the outgoing interface supports multicast. */ if ((imo == NULL) || (imo->imo_multicast_vif == -1)) { 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 *ia1; TAILQ_FOREACH(ia1, &in_ifaddrhead, ia_link) if (ia1->ia_ifp == ifp) { ip->ip_src = IA_SIN(ia1)->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, hlen); } 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. */ if (ip_mrouter && (flags & IP_FORWARDING) == 0) { /* * Check if rsvp daemon is running. If not, don't * set ip_moptions. This ensures that the packet * is multicast and not just sent down one link * as prescribed by rsvpd. */ if (!rsvp_on) imo = NULL; if (ip_mforward(ip, ifp, m, imo) != 0) { m_freem(m); goto done; } } } /* * 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) { 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_SIN(ia)->sin_addr;#ifdef IPFIREWALL_FORWARD /* Keep note that we did this - if the firewall changes * the next-hop, our interface may change, changing the * default source IP. It's a shame so much effort happens * twice. Oh well. */ fwd_rewrite_src++;#endif /* IPFIREWALL_FORWARD */ }#endif /* notdef */#ifdef ALTQ /* * disable packet drop hack. * packetdrop should be done by queueing. */#else /* !ALTQ */ /* * Verify that we have any chance at all of being able to queue * the packet or packet fragments */ if ((ifp->if_snd.ifq_len + ip->ip_len / ifp->if_mtu + 1) >= ifp->if_snd.ifq_maxlen) { error = ENOBUFS; goto bad; }#endif /* !ALTQ */ /* * Look for broadcast address and * and verify user is allowed to send * such a packet. */ if (isbroadcast) { 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_short)ip->ip_len > ifp->if_mtu) { error = EMSGSIZE; goto bad; } m->m_flags |= M_BCAST; } else { m->m_flags &= ~M_BCAST; }sendit:#ifdef IPSEC /* get SP for this packet */ if (so == NULL) sp = ipsec4_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, flags, &error); else sp = ipsec4_getpolicybysock(m, IPSEC_DIR_OUTBOUND, so, &error); if (sp == NULL) { ipsecstat.out_inval++; goto bad; } error = 0; /* check policy */ switch (sp->policy) { case IPSEC_POLICY_DISCARD: /* * This packet is just discarded. */ ipsecstat.out_polvio++; goto bad; case IPSEC_POLICY_BYPASS: case IPSEC_POLICY_NONE: /* no need to do IPsec. */ goto skip_ipsec; case IPSEC_POLICY_IPSEC: if (sp->req == NULL) { /* acquire a policy */ error = key_spdacquire(sp); goto bad; } break; case IPSEC_POLICY_ENTRUST: default: printf("ip_output: Invalid policy found. %d\n", sp->policy); } { struct ipsec_output_state state; bzero(&state, sizeof(state)); state.m = m; if (flags & IP_ROUTETOIF) { state.ro = &iproute; bzero(&iproute, sizeof(iproute)); } else state.ro = ro; state.dst = (struct sockaddr *)dst;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?