summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/netpfil/ipfw/ip_fw_pfil.c
diff options
context:
space:
mode:
Diffstat (limited to 'freebsd/sys/netpfil/ipfw/ip_fw_pfil.c')
-rw-r--r--freebsd/sys/netpfil/ipfw/ip_fw_pfil.c210
1 files changed, 167 insertions, 43 deletions
diff --git a/freebsd/sys/netpfil/ipfw/ip_fw_pfil.c b/freebsd/sys/netpfil/ipfw/ip_fw_pfil.c
index d2e1b448..59c13aa5 100644
--- a/freebsd/sys/netpfil/ipfw/ip_fw_pfil.c
+++ b/freebsd/sys/netpfil/ipfw/ip_fw_pfil.c
@@ -49,6 +49,7 @@ __FBSDID("$FreeBSD$");
#include <net/if.h>
#include <net/route.h>
+#include <net/ethernet.h>
#include <net/pfil.h>
#include <net/vnet.h>
@@ -60,6 +61,7 @@ __FBSDID("$FreeBSD$");
#ifdef INET6
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
+#include <netinet6/scope6_var.h>
#endif
#include <netgraph/ng_ipfw.h>
@@ -76,26 +78,39 @@ 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);
+int ipfw_check_packet(void *, struct mbuf **, struct ifnet *, int,
+ struct inpcb *);
+int ipfw_check_frame(void *, struct mbuf **, struct ifnet *, int,
+ struct inpcb *);
#ifdef SYSCTL_NODE
SYSBEGIN(f1)
SYSCTL_DECL(_net_inet_ip_fw);
-SYSCTL_VNET_PROC(_net_inet_ip_fw, OID_AUTO, enable,
- CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_SECURE3, &VNET_NAME(fw_enable), 0,
- ipfw_chg_hook, "I", "Enable ipfw");
+SYSCTL_PROC(_net_inet_ip_fw, OID_AUTO, enable,
+ CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_SECURE3,
+ &VNET_NAME(fw_enable), 0, ipfw_chg_hook, "I", "Enable ipfw");
#ifdef INET6
SYSCTL_DECL(_net_inet6_ip6_fw);
-SYSCTL_VNET_PROC(_net_inet6_ip6_fw, OID_AUTO, enable,
- CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_SECURE3, &VNET_NAME(fw6_enable), 0,
- ipfw_chg_hook, "I", "Enable ipfw+6");
+SYSCTL_PROC(_net_inet6_ip6_fw, OID_AUTO, enable,
+ CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_SECURE3,
+ &VNET_NAME(fw6_enable), 0, ipfw_chg_hook, "I", "Enable ipfw+6");
#endif /* INET6 */
+SYSCTL_DECL(_net_link_ether);
+SYSCTL_PROC(_net_link_ether, OID_AUTO, ipfw,
+ CTLFLAG_VNET | CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_SECURE3,
+ &VNET_NAME(fwlink_enable), 0, ipfw_chg_hook, "I",
+ "Pass ether pkts through firewall");
+
SYSEND
#endif /* SYSCTL_NODE */
@@ -106,7 +121,7 @@ SYSEND
* The packet may be consumed.
*/
int
-ipfw_check_hook(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir,
+ipfw_check_packet(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir,
struct inpcb *inp)
{
struct ip_fw_args args;
@@ -114,10 +129,6 @@ ipfw_check_hook(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir,
int ipfw;
int ret;
- /* all the processing now uses ip_len in net format */
- if (mtod(*m0, struct ip *)->ip_v == 4)
- SET_NET_IPLEN(mtod(*m0, struct ip *));
-
/* convert dir to IPFW values */
dir = (dir == PFIL_IN) ? DIR_IN : DIR_OUT;
bzero(&args, sizeof(args));
@@ -131,11 +142,8 @@ again:
if (tag != NULL) {
args.rule = *((struct ipfw_rule_ref *)(tag+1));
m_tag_delete(*m0, tag);
- if (args.rule.info & IPFW_ONEPASS) {
- if (mtod(*m0, struct ip *)->ip_v == 4)
- SET_HOST_IPLEN(mtod(*m0, struct ip *));
+ if (args.rule.info & IPFW_ONEPASS)
return (0);
- }
}
args.m = *m0;
@@ -192,8 +200,20 @@ again:
}
#ifdef INET6
if (args.next_hop6 != NULL) {
- bcopy(args.next_hop6, (fwd_tag+1), len);
- if (in6_localip(&args.next_hop6->sin6_addr))
+ struct sockaddr_in6 *sa6;
+
+ sa6 = (struct sockaddr_in6 *)(fwd_tag + 1);
+ bcopy(args.next_hop6, sa6, len);
+ /*
+ * If nh6 address is link-local we should convert
+ * it to kernel internal form before doing any
+ * comparisons.
+ */
+ if (sa6_embedscope(sa6, V_ip6_use_defzone) != 0) {
+ ret = EACCES;
+ break;
+ }
+ if (in6_localip(&sa6->sin6_addr))
(*m0)->m_flags |= M_FASTFWD_OURS;
(*m0)->m_flags |= M_IP6_NEXTHOP;
}
@@ -279,8 +299,112 @@ again:
FREE_PKT(*m0);
*m0 = NULL;
}
- if (*m0 && mtod(*m0, struct ip *)->ip_v == 4)
- SET_HOST_IPLEN(mtod(*m0, struct ip *));
+
+ return ret;
+}
+
+/*
+ * ipfw processing for ethernet packets (in and out).
+ * Inteface is NULL from ether_demux, and ifp from
+ * ether_output_frame.
+ */
+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 = dir == PFIL_OUT ? dst: NULL; /* 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_NOWAIT);
+ 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;
}
@@ -303,7 +427,7 @@ ipfw_divert(struct mbuf **m0, int incoming, struct ipfw_rule_ref *rule,
clone = *m0; /* use the original mbuf */
*m0 = NULL;
} else {
- clone = m_dup(*m0, M_DONTWAIT);
+ clone = m_dup(*m0, M_NOWAIT);
/* If we cannot duplicate the mbuf, we sacrifice the divert
* chain and continue with the tee-ed packet.
*/
@@ -325,7 +449,6 @@ ipfw_divert(struct mbuf **m0, int incoming, struct ipfw_rule_ref *rule,
int hlen;
struct mbuf *reass;
- SET_HOST_IPLEN(ip); /* ip_reass wants host order */
reass = ip_reass(clone); /* Reassemble packet. */
if (reass == NULL)
return 0; /* not an error */
@@ -336,7 +459,6 @@ ipfw_divert(struct mbuf **m0, int incoming, struct ipfw_rule_ref *rule,
*/
ip = mtod(reass, struct ip *);
hlen = ip->ip_hl << 2;
- SET_NET_IPLEN(ip);
ip->ip_sum = 0;
if (hlen == sizeof(struct ip))
ip->ip_sum = in_cksum_hdr(ip);
@@ -385,13 +507,16 @@ static int
ipfw_hook(int onoff, int pf)
{
struct pfil_head *pfh;
+ pfil_func_t 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;
}
@@ -415,51 +540,50 @@ 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 newval;
int error;
int af;
- if (arg1 == &VNET_NAME(fw_enable)) {
- enable = V_fw_enable;
+ if (arg1 == &V_fw_enable)
af = AF_INET;
- }
#ifdef INET6
- else if (arg1 == &VNET_NAME(fw6_enable)) {
- enable = V_fw6_enable;
+ else if (arg1 == &V_fw6_enable)
af = AF_INET6;
- }
#endif
+ else if (arg1 == &V_fwlink_enable)
+ af = AF_LINK;
else
return (EINVAL);
- oldenable = enable;
-
- error = sysctl_handle_int(oidp, &enable, 0, req);
+ newval = *(int *)arg1;
+ /* 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 (*(int *)arg1 == 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
+ *(int *)arg1 = newval;
return (0);
}