mirror of
https://git.FreeBSD.org/src.git
synced 2025-01-17 15:27:36 +00:00
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
This commit is contained in:
parent
8a0ba81859
commit
4b9840932d
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=96511
@ -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.
|
||||
@ -398,6 +442,85 @@ ether_output_frame(ifp, m)
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
@ -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)
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user