diff options
Diffstat (limited to 'sys/netinet/ip_output.c')
| -rw-r--r-- | sys/netinet/ip_output.c | 349 |
1 files changed, 67 insertions, 282 deletions
diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c index 49461536b762..c40c682c2a50 100644 --- a/sys/netinet/ip_output.c +++ b/sys/netinet/ip_output.c @@ -31,9 +31,6 @@ */ #include "opt_ipfw.h" -#include "opt_ipdn.h" -#include "opt_ipdivert.h" -#include "opt_ipfilter.h" #include "opt_ipsec.h" #include "opt_mac.h" #include "opt_pfil_hooks.h" @@ -51,6 +48,7 @@ #include <sys/sysctl.h> #include <net/if.h> +#include <net/netisr.h> #include <net/route.h> #include <netinet/in.h> @@ -84,10 +82,6 @@ static MALLOC_DEFINE(M_IPMOPTS, "ip_moptions", "internet multicast options"); #include <netipsec/key.h> #endif /*FAST_IPSEC*/ -#include <netinet/ip_fw.h> -#include <netinet/ip_divert.h> -#include <netinet/ip_dummynet.h> - #define print_ip(x, a, y) printf("%s %d.%d.%d.%d%s",\ x, (ntohl(a.s_addr)>>24)&0xFF,\ (ntohl(a.s_addr)>>16)&0xFF,\ @@ -133,49 +127,27 @@ ip_output(struct mbuf *m, struct mbuf *opt, struct route *ro, struct ifnet *ifp = NULL; /* keep compiler happy */ struct mbuf *m0; int hlen = sizeof (struct ip); - int len, off, error = 0; + int len, error = 0; struct sockaddr_in *dst = NULL; /* keep compiler happy */ struct in_ifaddr *ia = NULL; int isbroadcast, sw_csum; - struct in_addr pkt_dst; struct route iproute; - struct m_tag *mtag, *dummytag; + struct in_addr odst; +#ifdef IPFIREWALL_FORWARD + struct m_tag *fwd_tag = NULL; +#endif #ifdef IPSEC struct secpolicy *sp = NULL; #endif #ifdef FAST_IPSEC struct secpolicy *sp = NULL; struct tdb_ident *tdbi; + struct m_tag *mtag; int s; #endif /* FAST_IPSEC */ - struct ip_fw_args args; - int src_was_INADDR_ANY = 0; /* as the name says... */ - - args.eh = NULL; - args.rule = NULL; M_ASSERTPKTHDR(m); - args.next_hop = m_claim_next(m, PACKET_TAG_IPFORWARD); - dummytag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL); - if (dummytag != NULL) { - struct dn_pkt_tag *dt = (struct dn_pkt_tag *)(dummytag+1); - /* - * Prevent lower layers from finding the tag - * Cleanup and free is done below - */ - m_tag_unlink(m, dummytag); - /* - * the packet was already tagged, so part of the - * processing was already done, and we need to go down. - * Get parameters from the header. - */ - args.rule = dt->rule; - ro = &(dt->ro); - dst = dt->dn_dst; - ifp = dt->ifp; - } - if (ro == NULL) { ro = &iproute; bzero(ro, sizeof (*ro)); @@ -184,14 +156,6 @@ ip_output(struct mbuf *m, struct mbuf *opt, struct route *ro, if (inp != NULL) INP_LOCK_ASSERT(inp); - if (args.rule != NULL) { /* dummynet already saw us */ - ip = mtod(m, struct ip *); - hlen = ip->ip_hl << 2 ; - if (ro->ro_rt) - ia = ifatoia(ro->ro_rt->rt_ifa); - goto sendit; - } - if (opt) { len = 0; m = ip_insertoptions(m, opt, &len); @@ -199,7 +163,6 @@ ip_output(struct mbuf *m, struct mbuf *opt, struct route *ro, hlen = len; } ip = mtod(m, struct ip *); - pkt_dst = args.next_hop ? args.next_hop->sin_addr : ip->ip_dst; /* * Fill in IP header. If we are not allowing fragmentation, @@ -222,6 +185,7 @@ ip_output(struct mbuf *m, struct mbuf *opt, struct route *ro, } dst = (struct sockaddr_in *)&ro->ro_dst; +again: /* * If there is a cached route, * check that it is to the same destination @@ -231,15 +195,19 @@ ip_output(struct mbuf *m, struct mbuf *opt, struct route *ro, */ if (ro->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 || dst->sin_family != AF_INET || - dst->sin_addr.s_addr != pkt_dst.s_addr)) { + dst->sin_addr.s_addr != ip->ip_dst.s_addr)) { RTFREE(ro->ro_rt); ro->ro_rt = (struct rtentry *)0; } +#ifdef IPFIREWALL_FORWARD + if (ro->ro_rt == NULL && fwd_tag == NULL) { +#else if (ro->ro_rt == NULL) { +#endif bzero(dst, sizeof(*dst)); dst->sin_family = AF_INET; dst->sin_len = sizeof(*dst); - dst->sin_addr = pkt_dst; + dst->sin_addr = ip->ip_dst; } /* * If routing to interface only, @@ -287,7 +255,7 @@ ip_output(struct mbuf *m, struct mbuf *opt, struct route *ro, else isbroadcast = in_broadcast(dst->sin_addr, ifp); } - if (IN_MULTICAST(ntohl(pkt_dst.s_addr))) { + if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) { struct in_multi *inm; m->m_flags |= M_MCAST; @@ -329,7 +297,7 @@ ip_output(struct mbuf *m, struct mbuf *opt, struct route *ro, ip->ip_src = IA_SIN(ia)->sin_addr; } - IN_LOOKUP_MULTI(pkt_dst, ifp, inm); + IN_LOOKUP_MULTI(ip->ip_dst, ifp, inm); if (inm != NULL && (imo == NULL || imo->imo_multicast_loop)) { /* @@ -395,7 +363,6 @@ ip_output(struct mbuf *m, struct mbuf *opt, struct route *ro, /* Interface may have no addresses. */ if (ia != NULL) { ip->ip_src = IA_SIN(ia)->sin_addr; - src_was_INADDR_ANY = 1; } } #endif /* notdef */ @@ -693,250 +660,75 @@ skip_ipsec: spd_done: #endif /* FAST_IPSEC */ - /* - * 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.> - */ #ifdef PFIL_HOOKS /* * Run through list of hooks for output packets. */ + odst.s_addr = ip->ip_dst.s_addr; error = pfil_run_hooks(&inet_pfil_hook, &m, ifp, PFIL_OUT); if (error != 0 || m == NULL) goto done; - ip = mtod(m, struct ip *); -#endif /* PFIL_HOOKS */ - - /* - * Check with the firewall... - * but not if we are already being fwd'd from a firewall. - */ - if (fw_enable && IPFW_LOADED && !args.next_hop) { - struct sockaddr_in *old = dst; - - args.m = m; - args.next_hop = dst; - args.oif = ifp; - off = ip_fw_chk_ptr(&args); - m = args.m; - dst = args.next_hop; - - /* - * On return we must do the following: - * m == NULL -> drop the pkt (old interface, deprecated) - * (off & IP_FW_PORT_DENY_FLAG) -> drop the pkt (new interface) - * 1<=off<= 0xffff -> DIVERT - * (off & IP_FW_PORT_DYNT_FLAG) -> send to a DUMMYNET pipe - * (off & IP_FW_PORT_TEE_FLAG) -> TEE the packet - * dst != old -> IPFIREWALL_FORWARD - * off==0, dst==old -> accept - * If some of the above modules are 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 ( (off & IP_FW_PORT_DENY_FLAG) || m == NULL) { - if (m) - m_freem(m); - error = EACCES; - goto done; - } - ip = mtod(m, struct ip *); - if (off == 0 && dst == old) /* common case */ - goto pass; - if (DUMMYNET_LOADED && (off & IP_FW_PORT_DYNT_FLAG) != 0) { - /* - * 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! - */ - args.ro = ro; - args.dst = dst; - args.flags = flags; - - error = ip_dn_io_ptr(m, off & 0xffff, DN_TO_IP_OUT, - &args); - goto done; - } -#ifdef IPDIVERT - if (off != 0 && (off & IP_FW_PORT_DYNT_FLAG) == 0) { - struct mbuf *clone; - - /* Clone packet if we're doing a 'tee' */ - if ((off & IP_FW_PORT_TEE_FLAG) != 0) - clone = divert_clone(m); - else - clone = NULL; - - /* Restore packet header fields to original values */ - ip->ip_len = htons(ip->ip_len); - ip->ip_off = htons(ip->ip_off); - - /* Deliver packet to divert input routine */ - divert_packet(m, 0); - - /* If 'tee', continue with original packet */ - if (clone != NULL) { - m = clone; - ip = mtod(m, struct ip *); - goto pass; - } - goto done; - } -#endif - - /* IPFIREWALL_FORWARD */ - /* - * Check dst to make sure it is 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) { /* FORWARD, dst has changed */ -#if 0 - /* - * XXX To improve readability, this block should be - * changed into a function call as below: - */ - error = ip_ipforward(&m, &dst, &ifp); - if (error) - goto bad; - if (m == NULL) /* ip_input consumed the mbuf */ - goto done; -#else - struct in_ifaddr *ia; - /* - * XXX sro_fwd below is static, and a pointer - * to it gets passed to routines downstream. - * This could have surprisingly bad results in - * practice, because its content is overwritten - * by subsequent packets. - * XXX: Breaks on SMP and possibly preemption! - */ - /* There must be a better way to do this next line... */ - static struct route sro_fwd; - struct route *ro_fwd = &sro_fwd; - -#if 0 - print_ip("IPFIREWALL_FORWARD: New dst ip: ", - dst->sin_addr, "\n"); -#endif + ip = mtod(m, struct ip *); - /* - * 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 an ISR. - */ - LIST_FOREACH(ia, - INADDR_HASH(dst->sin_addr.s_addr), ia_hash) { - /* - * 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 == - dst->sin_addr.s_addr) - break; - } - if (ia) { /* tell ip_input "dont filter" */ - mtag = m_tag_get( - PACKET_TAG_IPFORWARD, - sizeof(struct sockaddr_in *), M_NOWAIT); - if (mtag == NULL) { - error = ENOBUFS; - goto bad; - } - *(struct sockaddr_in **)(mtag+1) = - args.next_hop; - m_tag_prepend(m, mtag); - - if (m->m_pkthdr.rcvif == NULL) - m->m_pkthdr.rcvif = ifunit("lo0"); - if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { - m->m_pkthdr.csum_flags |= - CSUM_DATA_VALID | CSUM_PSEUDO_HDR; - m->m_pkthdr.csum_data = 0xffff; - } + /* See if destination IP address was changed by packet filter. */ + if (odst.s_addr != ip->ip_dst.s_addr) { + m->m_flags |= M_SKIP_FIREWALL; + if (in_localip(ip->ip_dst)) { + m->m_flags |= M_FASTFWD_OURS; + if (m->m_pkthdr.rcvif == NULL) + m->m_pkthdr.rcvif = ifunit("lo0"); + if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { m->m_pkthdr.csum_flags |= - CSUM_IP_CHECKED | CSUM_IP_VALID; - ip->ip_len = htons(ip->ip_len); - ip->ip_off = htons(ip->ip_off); - ip_input(m); - goto done; + CSUM_DATA_VALID | CSUM_PSEUDO_HDR; + m->m_pkthdr.csum_data = 0xffff; } - /* - * Some of the logic for this was - * nicked from above. - */ - bcopy(dst, &ro_fwd->ro_dst, sizeof(*dst)); - - ro_fwd->ro_rt = 0; - rtalloc_ign(ro_fwd, RTF_CLONING); + m->m_pkthdr.csum_flags |= + CSUM_IP_CHECKED | CSUM_IP_VALID; - if (ro_fwd->ro_rt == NULL) { - ipstat.ips_noroute++; - error = EHOSTUNREACH; - goto bad; - } - - ia = ifatoia(ro_fwd->ro_rt->rt_ifa); - ifp = ro_fwd->ro_rt->rt_ifp; - ro_fwd->ro_rt->rt_rmx.rmx_pksent++; - if (ro_fwd->ro_rt->rt_flags & RTF_GATEWAY) - dst = (struct sockaddr_in *) - ro_fwd->ro_rt->rt_gateway; - if (ro_fwd->ro_rt->rt_flags & RTF_HOST) - isbroadcast = - (ro_fwd->ro_rt->rt_flags & RTF_BROADCAST); - else - isbroadcast = in_broadcast(dst->sin_addr, ifp); - if (ro->ro_rt) - RTFREE(ro->ro_rt); - ro->ro_rt = ro_fwd->ro_rt; - dst = (struct sockaddr_in *)&ro_fwd->ro_dst; + error = netisr_queue(NETISR_IP, m); + goto done; + } else + goto again; + } -#endif /* ... block to be put into a function */ - /* - * If we added a default src ip earlier, - * which would have been gotten from the-then - * interface, do it again, from the new one. - */ - if (src_was_INADDR_ANY) - ip->ip_src = IA_SIN(ia)->sin_addr; - goto pass ; +#ifdef IPFIREWALL_FORWARD + /* See if local, if yes, send it to netisr with IP_FASTFWD_OURS. */ + if (m->m_flags & M_FASTFWD_OURS) { + if (m->m_pkthdr.rcvif == NULL) + m->m_pkthdr.rcvif = ifunit("lo0"); + if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) { + m->m_pkthdr.csum_flags |= + CSUM_DATA_VALID | CSUM_PSEUDO_HDR; + m->m_pkthdr.csum_data = 0xffff; } + m->m_pkthdr.csum_flags |= + CSUM_IP_CHECKED | CSUM_IP_VALID; - /* - * if we get here, none of the above matches, and - * we have to drop the pkt - */ - m_freem(m); - error = EACCES; /* not sure this is the right error msg */ - goto done; + error = netisr_queue(NETISR_IP, m); + goto done; } + /* Or forward to some other address? */ + fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL); + if (fwd_tag) { + if (!in_localip(ip->ip_src) && !in_localaddr(ip->ip_dst)) { + dst = (struct sockaddr_in *)&ro->ro_dst; + bcopy((fwd_tag+1), dst, sizeof(struct sockaddr_in)); + m->m_flags |= M_SKIP_FIREWALL; + m_tag_delete(m, fwd_tag); + goto again; + } else { + m_tag_delete(m, fwd_tag); + /* Continue. */ + } + } +#endif +#endif /* PFIL_HOOKS */ + +#if 0 pass: +#endif /* 127/8 must not appear on wire - RFC1122. */ if ((ntohl(ip->ip_dst.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET || (ntohl(ip->ip_src.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET) { @@ -1037,13 +829,6 @@ pass: done: if (ro == &iproute && ro->ro_rt) { RTFREE(ro->ro_rt); - ro->ro_rt = NULL; - } - if (dummytag) { - struct dn_pkt_tag *dt = (struct dn_pkt_tag *)(dummytag+1); - if (dt->ro.ro_rt) - RTFREE(dt->ro.ro_rt); - m_tag_free(dummytag); } #ifdef IPSEC if (sp != NULL) { |
