ip_output.c
来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 1,566 行 · 第 1/3 页
C
1,566 行
/* * 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 * $Id: ip_output.c,v 1.85.2.3 1999/05/04 16:24:00 luigi Exp $ */#define _IP_VHL#include "opt_ipfw.h"#include "opt_ipdn.h"#include "opt_ipdivert.h"#include "opt_ipfilter.h"#include <sys/param.h>#include <sys/systm.h>#include <sys/kernel.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 vax#include <machine/mtpr.h>#endif#include <machine/in_cksum.h>static MALLOC_DEFINE(M_IPMOPTS, "ip_moptions", "internet multicast options");#if !defined(COMPAT_IPFW) || COMPAT_IPFW == 1#undef COMPAT_IPFW#define COMPAT_IPFW 1#else#undef COMPAT_IPFW#endif#ifdef COMPAT_IPFW#include <netinet/ip_fw.h>#endif#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 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 **));#if defined(IPFILTER_LKM) || defined(IPFILTER)int ip_optcopy __P((struct ip *, struct ip *));extern int (*fr_checkp) __P((struct ip *, int, struct ifnet *, int, struct mbuf **));#elsestatic int ip_optcopy __P((struct ip *, struct ip *));#endifextern 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; int isbroadcast;#ifdef IPFIREWALL_FORWARD int fwd_rewrite_src = 0;#endif#ifndef IPDIVERT /* dummy variable for the firewall code to play with */ u_short ip_divert_cookie = 0 ;#endif#ifdef COMPAT_IPFW struct ip_fw_chain *rule = NULL ;#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. * opt, flags and imo have already been used, and now * they are used to hold ifp, dst and NULL, respectively. */ rule = (struct ip_fw_chain *)(m->m_data) ; m0 = m = m->m_next ; ip = mtod(m, struct ip *); dst = (struct sockaddr_in *)flags; ifp = (struct ifnet *)opt; hlen = IP_VHL_HL(ip->ip_vhl) << 2 ; opt = NULL ; flags = 0 ; /* XXX is this correct ? */ goto sendit; } else rule = 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; ip->ip_id = htons(ip_id++); 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; for (ia1 = in_ifaddrhead.tqh_first; ia1; ia1 = ia1->ia_link.tqe_next) 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 */ /* * 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; } /* * 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: /* * IpHack's section. * - Xlate: translate packet's addr/port (NAT). * - Firewall: deny/allow/etc. * - Wrap: fake packet's addr/port <unimpl.> * - Encapsulate: put it in another IP and send out. <unimp.> */ #if defined(IPFILTER) || defined(IPFILTER_LKM) if (fr_checkp) { struct mbuf *m1 = m; if ((error = (*fr_checkp)(ip, hlen, ifp, 1, &m1)) || !m1) goto done; ip = mtod(m = m1, struct ip *); }#endif#ifdef COMPAT_IPFW if (ip_nat_ptr && !(*ip_nat_ptr)(&ip, &m, ifp, IP_NAT_OUT)) { error = EACCES; goto done; } /* * Check with the firewall... */ if (ip_fw_chk_ptr) { struct sockaddr_in *old = dst; off = (*ip_fw_chk_ptr)(&ip, hlen, ifp, &ip_divert_cookie, &m, &rule, &dst); /* * On return we must do the following: * m == NULL -> drop the pkt * 1<=off<= 0xffff -> DIVERT * (off & 0x10000) -> send to a DUMMYNET pipe * dst != old -> IPFIREWALL_FORWARD * off==0, dst==old -> accept * If some of the above modules is not compiled in, then * we should't have to check the corresponding condition * (because the ipfw control socket should not accept * unsupported rules), but better play safe and drop * packets in case of doubt. */ if (!m) { /* firewall said to reject */ error = EACCES; goto done; } if (off == 0 && dst == old) /* common case */ goto pass ;#ifdef DUMMYNET if (off & 0x10000) { /* * pass the pkt to dummynet. Need to include * pipe number, m, ifp, ro, dst because these are * not recomputed in the next pass. * All other parameters have been already used and * so they are not needed anymore. * XXX note: if the ifp or ro entry are deleted * while a pkt is in dummynet, we are in trouble! */ dummynet_io(off & 0xffff, DN_TO_IP_OUT, m,ifp,ro,dst,rule); goto done; }#endif #ifdef IPDIVERT if (off > 0 && off < 0x10000) { /* Divert packet */ ip_divert_port = off & 0xffff ; (*inetsw[ip_protox[IPPROTO_DIVERT]].pr_input)(m, 0); goto done; }#endif#ifdef IPFIREWALL_FORWARD /* Here we check dst to make sure it's directly reachable on the * interface we previously thought it was. * If it isn't (which may be likely in some situations) we have * to re-route it (ie, find a route for the next-hop and the * associated interface) and set them here. This is nested * forwarding which in most cases is undesirable, except where * such control is nigh impossible. So we do it here. * And I'm babbling. */ if (off == 0 && old != dst) { struct in_ifaddr *ia; /* It's changed... */ /* There must be a better way to do this next line... */ static struct route sro_fwd, *ro_fwd = &sro_fwd;#ifdef IPFIREWALL_FORWARD_DEBUG printf("IPFIREWALL_FORWARD: New dst ip: "); print_ip(dst->sin_addr); printf("\n");#endif /* * We need to figure out if we have been forwarded * to a local socket. If so then we should somehow * "loop back" to ip_input, and get directed to the * PCB as if we had received this packet. This is * because it may be dificult to identify the packets * you want to forward until they are being output * and have selected an interface. (e.g. locally * initiated packets) If we used the loopback inteface, * we would not be able to control what happens * as the packet runs through ip_input() as * it is done through a ISR. */ for (ia = TAILQ_FIRST(&in_ifaddrhead); ia; ia = TAILQ_NEXT(ia, ia_link)) { /* * If the addr to forward to is one * of ours, we pretend to * be the destination for this packet. */ if (IA_SIN(ia)->sin_addr.s_addr ==
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?