diff options
author | luigi <luigi@FreeBSD.org> | 2002-05-13 10:37:19 +0000 |
---|---|---|
committer | luigi <luigi@FreeBSD.org> | 2002-05-13 10:37:19 +0000 |
commit | 2afce45ffcb33c722890e0085af2e0af09472a4e (patch) | |
tree | 3178446df8619c134cc22fc1485f4a05238cc975 /sys | |
parent | f3653f3da85e17701bd60340a6a015bdcacb446f (diff) | |
download | FreeBSD-src-2afce45ffcb33c722890e0085af2e0af09472a4e.zip FreeBSD-src-2afce45ffcb33c722890e0085af2e0af09472a4e.tar.gz |
Add ipfw hooks to ether_demux() and ether_output_frame().
Ipfw processing of frames at layer 2 can be enabled by the sysctl variable
net.link.ether.ipfw=1
Consider this feature experimental, because right now, the firewall
is invoked in the places indicated below, and controlled by the
sysctl variables listed on the right. As a consequence, a packet
can be filtered from 1 to 4 times depending on the path it follows,
which might make a ruleset a bit hard to follow.
I will add an ipfw option to tell if we want a given rule to apply
to ether_demux() and ether_output_frame(), but we have run out of
flags in the struct ip_fw so i need to think a bit on how to implement
this.
to upper layers
| |
+----------->-----------+
^ V
[ip_input] [ip_output] net.inet.ip.fw.enable=1
| |
^ V
[ether_demux] [ether_output_frame] net.link.ether.ipfw=1
| |
+->- [bdg_forward]-->---+ net.link.ether.bridge_ipfw=1
^ V
| |
to devices
Diffstat (limited to 'sys')
-rw-r--r-- | sys/net/if_ethersubr.c | 146 | ||||
-rw-r--r-- | sys/netinet/ip_dummynet.c | 32 | ||||
-rw-r--r-- | sys/netinet/ip_dummynet.h | 2 |
3 files changed, 175 insertions, 5 deletions
diff --git a/sys/net/if_ethersubr.c b/sys/net/if_ethersubr.c index cc8ee89..16fb62f 100644 --- a/sys/net/if_ethersubr.c +++ b/sys/net/if_ethersubr.c @@ -65,6 +65,8 @@ #include <netinet/in.h> #include <netinet/in_var.h> #include <netinet/if_ether.h> +#include <netinet/ip_fw.h> +#include <netinet/ip_dummynet.h> #endif #ifdef INET6 #include <netinet6/nd6.h> @@ -124,6 +126,11 @@ u_char etherbroadcastaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; #define senderr(e) do { error = (e); goto bad;} while (0) #define IFP2AC(IFP) ((struct arpcom *)IFP) +int +ether_ipfw_chk(struct mbuf **m0, struct ifnet *dst, + struct ip_fw **rule, struct ether_header *eh, int shared); +static int ether_ipfw; + /* * Ethernet output routine. * Encapsulate a packet of type family for the local net. @@ -377,6 +384,14 @@ ether_output_frame(ifp, m) { int error = 0; +#if 1 /* XXX ipfw */ + struct ip_fw *rule = NULL; + if (m->m_type == MT_DUMMYNET) { /* extract info from dummynet header */ + rule = (struct ip_fw *)(m->m_data) ; + m = m->m_next ; + goto no_bridge; + } +#endif if (BDG_ACTIVE(ifp) ) { struct ether_header *eh; /* a ptr suffices */ @@ -389,6 +404,35 @@ ether_output_frame(ifp, m) return (0); } +#if 1 /* XXX ipfw */ +no_bridge: + if (IPFW_LOADED && ether_ipfw != 0) { + struct ether_header save_eh, *eh; + + eh = mtod(m, struct ether_header *); + save_eh = *eh; + m_adj(m, ETHER_HDR_LEN); + if (ether_ipfw_chk(&m, ifp, &rule, eh, 0) == 0) { + if (m) { + m_freem(m); + return ENOBUFS; /* pkt dropped */ + } else + return 0; /* consumed e.g. in a pipe */ + } + /* packet was ok, restore the ethernet header */ + if ( (void *)(eh + 1) == (void *)m->m_data) { + m->m_data -= ETHER_HDR_LEN ; + m->m_len += ETHER_HDR_LEN ; + m->m_pkthdr.len += ETHER_HDR_LEN ; + } else { + M_PREPEND(m, ETHER_HDR_LEN, M_DONTWAIT); + if (m == NULL) /* nope... */ + return ENOBUFS; + bcopy(&save_eh, mtod(m, struct ether_header *), + ETHER_HDR_LEN); + } + } +#endif /* XXX ipfw */ /* * Queue message on interface, update output statistics if * successful, and start output if interface not yet active. @@ -399,6 +443,85 @@ ether_output_frame(ifp, m) } /* + * ipfw processing for ethernet packets (in and out). + * The second parameter is NULL from ether_demux, and ifp from + * ether_output_frame. This section of code could be used from + * bridge.c as well as long as we put some extra field (e.g. shared) + * to distinguish that case from ether_output_frame(); + */ +int +ether_ipfw_chk(struct mbuf **m0, struct ifnet *dst, + struct ip_fw **rule, struct ether_header *eh, int shared) +{ + struct ether_header save_eh = *eh; /* could be a ptr in m */ + int i; + + if (*rule != NULL) /* dummynet packet, already partially processed */ + return 1; /* HACK! I should obey the fw_one_pass */ + /* + * i need some amt of data to be contiguous, and in case others need + * the packet (shared==1) also better be in the first mbuf. + */ + i = min( (*m0)->m_pkthdr.len, max_protohdr) ; + if ( shared || (*m0)->m_len < i) { + *m0 = m_pullup(*m0, i); + if (*m0 == NULL) { + printf("-- bdg: pullup failed.\n") ; + return 0; + } + } + + i = ip_fw_chk_ptr(m0, dst, NULL /* cookie */, rule, + (struct sockaddr_in **)&save_eh); + if ( (i & IP_FW_PORT_DENY_FLAG) || *m0 == NULL) /* drop */ + return 0; + + if (i == 0) /* a PASS rule. */ + return 1; + + if (DUMMYNET_LOADED && (i & IP_FW_PORT_DYNT_FLAG)) { + /* + * Pass the pkt to dummynet, which consumes it. + * If shared, make a copy and keep the original. + * Need to prepend the ethernet header, optimize the common + * case of eh pointing already into the original mbuf. + */ + struct mbuf *m ; + + if (shared) { + m = m_copypacket(*m0, M_DONTWAIT); + if (m == NULL) { + printf("bdg_fwd: copy(1) failed\n"); + return 0; + } + } else { + m = *m0 ; /* pass the original to dummynet */ + *m0 = NULL ; /* and nothing back to the caller */ + } + if ( (void *)(eh + 1) == (void *)m->m_data) { + m->m_data -= ETHER_HDR_LEN ; + m->m_len += ETHER_HDR_LEN ; + m->m_pkthdr.len += ETHER_HDR_LEN ; + } else { + M_PREPEND(m, ETHER_HDR_LEN, M_DONTWAIT); + if (m == NULL) /* nope... */ + return 0; + bcopy(&save_eh, mtod(m, struct ether_header *), + ETHER_HDR_LEN); + } + ip_dn_io_ptr((i & 0xffff), dst ? DN_TO_ETH_OUT: DN_TO_ETH_DEMUX, + m, dst, NULL /*route*/, 0 /*dst*/, *rule, 0 /*flags*/); + return 0; + } + /* + * XXX add divert/forward actions... + */ + /* if none of the above matches, we have to drop the pkt */ + printf("ether_ipfw: No rules match, so dropping packet!\n"); + return 0; +} + +/* * Process a received Ethernet packet; * the packet is in the mbuf chain m without * the ether header, which is provided separately. @@ -504,6 +627,16 @@ ether_demux(ifp, eh, m) register struct llc *l; #endif +#if 1 /* XXX ipfw */ + struct ip_fw *rule = NULL; + if (m->m_type == MT_DUMMYNET) { /* extract info from dummynet header */ + rule = (struct ip_fw *)(m->m_data) ; + m = m->m_next ; + ifp = m->m_pkthdr.rcvif; + goto post_stats; + } +#endif + if (! (BDG_ACTIVE(ifp) ) ) /* Discard packet if upper layers shouldn't see it because it was unicast to a different Ethernet address. If the driver is working @@ -532,6 +665,17 @@ ether_demux(ifp, eh, m) if (m->m_flags & (M_BCAST|M_MCAST)) ifp->if_imcasts++; +#if 1 /* XXX ipfw */ +post_stats: + if ( IPFW_LOADED && ether_ipfw != 0) { + if (ether_ipfw_chk(&m, NULL, &rule, eh, 0 ) == 0) { + if (m) + m_freem(m); + return; + } + } +#endif /* XXX ipfw */ + ether_type = ntohs(eh->ether_type); switch (ether_type) { @@ -719,6 +863,8 @@ ether_ifdetach(ifp, bpf) SYSCTL_DECL(_net_link); SYSCTL_NODE(_net_link, IFT_ETHER, ether, CTLFLAG_RW, 0, "Ethernet"); +SYSCTL_INT(_net_link_ether, OID_AUTO, ipfw, CTLFLAG_RW, + ðer_ipfw,0,"Pass ether pkts through firewall"); int ether_ioctl(ifp, command, data) diff --git a/sys/netinet/ip_dummynet.c b/sys/netinet/ip_dummynet.c index 5da5ea8..fa7fd91 100644 --- a/sys/netinet/ip_dummynet.c +++ b/sys/netinet/ip_dummynet.c @@ -161,7 +161,7 @@ static void rt_unref(struct rtentry *); static void dummynet(void *); static void dummynet_flush(void); void dummynet_drain(void); -static int dummynet_io(int pipe, int dir, struct mbuf *m, struct ifnet *ifp, +static int dummynet_io(int pipe_nr, int dir, struct mbuf *m, struct ifnet *ifp, struct route *ro, struct sockaddr_in * dst, struct ip_fw *rule, int flags); static void dn_rule_delete(void *); @@ -445,7 +445,10 @@ transmit_event(struct dn_pipe *pipe) /* somebody unloaded the bridge module. Drop pkt */ printf("-- dropping bridged packet trapped in pipe--\n"); m_freem(pkt->dn_m); - } else { + break; + } /* fallthrough */ + case DN_TO_ETH_DEMUX: + { struct mbuf *m = (struct mbuf *)pkt ; struct ether_header *eh; @@ -465,11 +468,18 @@ transmit_event(struct dn_pipe *pipe) * (originally pkt->dn_m, but could be something else now) if * it has not consumed it. */ - m = bdg_forward_ptr(m, eh, pkt->ifp); - if (m) - m_freem(m); + if (pkt->dn_dir == DN_TO_BDG_FWD) { + m = bdg_forward_ptr(m, eh, pkt->ifp); + if (m) + m_freem(m); + } else + ether_demux(NULL, eh, m); /* which consumes the mbuf */ } break ; + case DN_TO_ETH_OUT: + ether_output_frame(pkt->ifp, (struct mbuf *)pkt); + break; + default: printf("dummynet: bad switch %d!\n", pkt->dn_dir); m_freem(pkt->dn_m); @@ -1036,6 +1046,18 @@ locate_flowset(int pipe_nr, struct ip_fw *rule) /* * dummynet hook for packets. Below 'pipe' is a pipe or a queue * depending on whether WF2Q or fixed bw is used. + * + * pipe_nr pipe or queue the packet is destined for. + * dir where shall we send the packet after dummynet. + * m the mbuf with the packet + * ifp the 'ifp' parameter from the caller. + * NULL in ip_input, destination interface in ip_output, + * real_dst in bdg_forward + * ro route parameter (only used in ip_output, NULL otherwise) + * dst destination address, only used by ip_output + * rule matching rule, in case of multiple passes + * flags flags from the caller, only used in ip_output + * */ int dummynet_io(int pipe_nr, int dir, /* pipe_nr can also be a fs_nr */ diff --git a/sys/netinet/ip_dummynet.h b/sys/netinet/ip_dummynet.h index 9f15ff3..ff482d7 100644 --- a/sys/netinet/ip_dummynet.h +++ b/sys/netinet/ip_dummynet.h @@ -130,6 +130,8 @@ struct dn_pkt { #define DN_TO_IP_OUT 1 #define DN_TO_IP_IN 2 #define DN_TO_BDG_FWD 3 +#define DN_TO_ETH_DEMUX 4 +#define DN_TO_ETH_OUT 5 dn_key output_time; /* when the pkt is due for delivery */ struct ifnet *ifp; /* interface, for ip_output */ |