summaryrefslogtreecommitdiffstats
path: root/sys/netinet/ipfw/ip_fw_pfil.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netinet/ipfw/ip_fw_pfil.c')
-rw-r--r--sys/netinet/ipfw/ip_fw_pfil.c164
1 files changed, 146 insertions, 18 deletions
diff --git a/sys/netinet/ipfw/ip_fw_pfil.c b/sys/netinet/ipfw/ip_fw_pfil.c
index 695613d..8d429e7 100644
--- a/sys/netinet/ipfw/ip_fw_pfil.c
+++ b/sys/netinet/ipfw/ip_fw_pfil.c
@@ -47,6 +47,7 @@ __FBSDID("$FreeBSD$");
#include <net/if.h>
#include <net/route.h>
+#include <net/ethernet.h>
#include <net/pfil.h>
#include <net/vnet.h>
@@ -72,10 +73,17 @@ static VNET_DEFINE(int, fw6_enable) = 1;
#define V_fw6_enable VNET(fw6_enable)
#endif
+static VNET_DEFINE(int, fwlink_enable) = 0;
+#define V_fwlink_enable VNET(fwlink_enable)
+
int ipfw_chg_hook(SYSCTL_HANDLER_ARGS);
/* Forward declarations. */
static int ipfw_divert(struct mbuf **, int, struct ipfw_rule_ref *, int);
+static int ipfw_check_packet(void *, struct mbuf **, struct ifnet *, int,
+ struct inpcb *);
+static int ipfw_check_frame(void *, struct mbuf **, struct ifnet *, int,
+ struct inpcb *);
#ifdef SYSCTL_NODE
@@ -92,6 +100,11 @@ SYSCTL_VNET_PROC(_net_inet6_ip6_fw, OID_AUTO, enable,
ipfw_chg_hook, "I", "Enable ipfw+6");
#endif /* INET6 */
+SYSCTL_DECL(_net_link_ether);
+SYSCTL_VNET_PROC(_net_link_ether, OID_AUTO, ipfw,
+ CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_SECURE3, &VNET_NAME(fwlink_enable), 0,
+ ipfw_chg_hook, "I", "Pass ether pkts through firewall");
+
SYSEND
#endif /* SYSCTL_NODE */
@@ -101,8 +114,8 @@ SYSEND
* dummynet, divert, netgraph or other modules.
* The packet may be consumed.
*/
-int
-ipfw_check_hook(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir,
+static int
+ipfw_check_packet(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir,
struct inpcb *inp)
{
struct ip_fw_args args;
@@ -278,6 +291,111 @@ again:
return ret;
}
+/*
+ * ipfw processing for ethernet packets (in and out).
+ * Inteface is NULL from ether_demux, and ifp from
+ * ether_output_frame.
+ */
+static int
+ipfw_check_frame(void *arg, struct mbuf **m0, struct ifnet *dst, int dir,
+ struct inpcb *inp)
+{
+ struct ether_header *eh;
+ struct ether_header save_eh;
+ struct mbuf *m;
+ int i, ret;
+ struct ip_fw_args args;
+ struct m_tag *mtag;
+
+ /* fetch start point from rule, if any */
+ mtag = m_tag_locate(*m0, MTAG_IPFW_RULE, 0, NULL);
+ if (mtag == NULL) {
+ args.rule.slot = 0;
+ } else {
+ /* dummynet packet, already partially processed */
+ struct ipfw_rule_ref *r;
+
+ /* XXX can we free it after use ? */
+ mtag->m_tag_id = PACKET_TAG_NONE;
+ r = (struct ipfw_rule_ref *)(mtag + 1);
+ if (r->info & IPFW_ONEPASS)
+ return (0);
+ args.rule = *r;
+ }
+
+ /* I need some amt of data to be contiguous */
+ m = *m0;
+ i = min(m->m_pkthdr.len, max_protohdr);
+ if (m->m_len < i) {
+ m = m_pullup(m, i);
+ if (m == NULL) {
+ *m0 = m;
+ return (0);
+ }
+ }
+ eh = mtod(m, struct ether_header *);
+ save_eh = *eh; /* save copy for restore below */
+ m_adj(m, ETHER_HDR_LEN); /* strip ethernet header */
+
+ args.m = m; /* the packet we are looking at */
+ args.oif = dst; /* destination, if any */
+ args.next_hop = NULL; /* we do not support forward yet */
+ args.next_hop6 = NULL; /* we do not support forward yet */
+ args.eh = &save_eh; /* MAC header for bridged/MAC packets */
+ args.inp = NULL; /* used by ipfw uid/gid/jail rules */
+ i = ipfw_chk(&args);
+ m = args.m;
+ if (m != NULL) {
+ /*
+ * Restore Ethernet header, as needed, in case the
+ * mbuf chain was replaced by ipfw.
+ */
+ M_PREPEND(m, ETHER_HDR_LEN, M_DONTWAIT);
+ if (m == NULL) {
+ *m0 = NULL;
+ return (0);
+ }
+ if (eh != mtod(m, struct ether_header *))
+ bcopy(&save_eh, mtod(m, struct ether_header *),
+ ETHER_HDR_LEN);
+ }
+ *m0 = m;
+
+ ret = 0;
+ /* Check result of ipfw_chk() */
+ switch (i) {
+ case IP_FW_PASS:
+ break;
+
+ case IP_FW_DENY:
+ ret = EACCES;
+ break; /* i.e. drop */
+
+ case IP_FW_DUMMYNET:
+ ret = EACCES;
+ int dir;
+
+ if (ip_dn_io_ptr == NULL)
+ break; /* i.e. drop */
+
+ *m0 = NULL;
+ dir = PROTO_LAYER2 | (dst ? DIR_OUT : DIR_IN);
+ ip_dn_io_ptr(&m, dir, &args);
+ return 0;
+
+ default:
+ KASSERT(0, ("%s: unknown retval", __func__));
+ }
+
+ if (ret != 0) {
+ if (*m0)
+ FREE_PKT(*m0);
+ *m0 = NULL;
+ }
+
+ return ret;
+}
+
/* do the divert, return 1 on error 0 on success */
static int
ipfw_divert(struct mbuf **m0, int incoming, struct ipfw_rule_ref *rule,
@@ -379,13 +497,16 @@ static int
ipfw_hook(int onoff, int pf)
{
struct pfil_head *pfh;
+ void *hook_func;
pfh = pfil_head_get(PFIL_TYPE_AF, pf);
if (pfh == NULL)
return ENOENT;
+ hook_func = (pf == AF_LINK) ? ipfw_check_frame : ipfw_check_packet;
+
(void) (onoff ? pfil_add_hook : pfil_remove_hook)
- (ipfw_check_hook, NULL, PFIL_IN | PFIL_OUT | PFIL_WAITOK, pfh);
+ (hook_func, NULL, PFIL_IN | PFIL_OUT | PFIL_WAITOK, pfh);
return 0;
}
@@ -409,51 +530,58 @@ ipfw_attach_hooks(int arg)
printf("ipfw6_hook() error\n");
}
#endif
+ if (arg == 0) /* detach */
+ ipfw_hook(0, AF_LINK);
+ else if (V_fwlink_enable && ipfw_hook(1, AF_LINK) != 0) {
+ error = ENOENT;
+ printf("ipfw_link_hook() error\n");
+ }
return error;
}
int
ipfw_chg_hook(SYSCTL_HANDLER_ARGS)
{
- int enable;
- int oldenable;
+ int *enable;
+ int newval;
int error;
int af;
if (arg1 == &VNET_NAME(fw_enable)) {
- enable = V_fw_enable;
+ enable = &V_fw_enable;
af = AF_INET;
}
#ifdef INET6
else if (arg1 == &VNET_NAME(fw6_enable)) {
- enable = V_fw6_enable;
+ enable = &V_fw6_enable;
af = AF_INET6;
}
#endif
+ else if (arg1 == &VNET_NAME(fwlink_enable)) {
+ enable = &V_fwlink_enable;
+ af = AF_LINK;
+ }
else
return (EINVAL);
- oldenable = enable;
+ newval = *enable;
- error = sysctl_handle_int(oidp, &enable, 0, req);
+ /* Handle sysctl change */
+ error = sysctl_handle_int(oidp, &newval, 0, req);
if (error)
return (error);
- enable = (enable) ? 1 : 0;
+ /* Formalize new value */
+ newval = (newval) ? 1 : 0;
- if (enable == oldenable)
+ if (*enable == newval)
return (0);
- error = ipfw_hook(enable, af);
+ error = ipfw_hook(newval, af);
if (error)
return (error);
- if (af == AF_INET)
- V_fw_enable = enable;
-#ifdef INET6
- else if (af == AF_INET6)
- V_fw6_enable = enable;
-#endif
+ *enable = newval;
return (0);
}
OpenPOWER on IntegriCloud