[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Skipping interfaces in pf [was: pf filtering on loopback?]



Hi,

in an earlier thread Daniel suggested that it might be a good idea to skip pf 
processing on lo0:
http://www.monkey.org/openbsd/archive/tech/0407/msg00061.html

Another thread on freebsd-net:
http://lists.freebsd.org/pipermail/freebsd-net/2004-December/005906.html
is discussing the possible overhead with filtering interfaces that do not 
transport any IP-level data or in setups with huge LAN pipes that you don't 
want to filter on.

As a solution I'd like to propose a new option "skip on <interface>" that 
disables filtering on the listed interface(s). I realize this as a flag in 
the already existing pf internal interface list. See attachment. The cost is 
a simple compare and the gain should be obvious.

To give some more motivation think of a setup where you have some GigE 
interfaces facing your LAN/DMZ and you don't want to filter them (which is 
very common). You will write a ruleset like:

block all
pass on { $if0, $if1, ... $ifN }
#ruleset goes here

The optimizer will also put these rules in front of the processing as there 
are a lot of matches. Still every packet on $ifN will have to go through N 
rules (skip-steps don't help) and every packet that really is filtered has 
that O(N) overhead in front as well.

It might be worthwhile to extend this to be able to skip only certain 
processing. e.g.:

	set skip all on <interface>
		Will skip all (surprise)
	set skip from scrub on <interface>
		Will evaluate scrub rules and end processing afterwards.
	set skip from nat on <interface>
		Will evaluate scrub and nat rules and stop afterwards.

I am open to changes regarding syntax and implementation, but think that the 
idea itself is good.

Comments?

-- 
/"\  Best regards,                      | mlaier_(_at_)_freebsd_(_dot_)_org
\ /  Max Laier                          | ICQ #67774661
 X   http://pf4freebsd.love2party.net/  | mlaier_(_at_)_EFnet
/ \  ASCII Ribbon Campaign              | Against HTML Mail and News
Index: sbin/pfctl/parse.y
===================================================================
RCS file: /usr/store/mlaier/ocvs/src/sbin/pfctl/parse.y,v
retrieving revision 1.469
diff -u -r1.469 parse.y
--- sbin/pfctl/parse.y	10 Dec 2004 22:13:26 -0000	1.469
+++ sbin/pfctl/parse.y	14 Dec 2004 17:37:42 -0000
@@ -281,6 +281,7 @@
 	    struct node_queue_bw bwspec, struct node_queue_opt *);
 int	expand_queue(struct pf_altq *, struct node_if *, struct node_queue *,
 	    struct node_queue_bw, struct node_queue_opt *);
+int	expand_skip_interface(struct node_if *);
 
 int	 check_rulestate(int);
 int	 kw_cmp(const void *, const void *);
@@ -398,7 +399,7 @@
 %token	NOROUTE FRAGMENT USER GROUP MAXMSS MAXIMUM TTL TOS DROP TABLE
 %token	REASSEMBLE FRAGDROP FRAGCROP ANCHOR NATANCHOR RDRANCHOR BINATANCHOR
 %token	SET OPTIMIZATION TIMEOUT LIMIT LOGINTERFACE BLOCKPOLICY RANDOMID
-%token	REQUIREORDER SYNPROXY FINGERPRINTS NOSYNC DEBUG HOSTID
+%token	REQUIREORDER SYNPROXY FINGERPRINTS NOSYNC DEBUG SKIP HOSTID
 %token	ANTISPOOF FOR
 %token	BITMASK RANDOM SOURCEHASH ROUNDROBIN STATICPORT PROBABILITY
 %token	ALTQ CBQ PRIQ HFSC BANDWIDTH TBRSIZE LINKSHARE REALTIME UPPERLIMIT
@@ -570,6 +571,12 @@
 			}
 			free($3);
 		}
+		| SET SKIP interface {
+			if (expand_skip_interface($3) != 0) {
+				yyerror("error setting skip interface(s)");
+				YYERROR;
+			}
+		}
 		;
 
 string		: string STRING				{
@@ -4446,6 +4453,35 @@
 		yyerror("rule expands to no valid combination");
 }
 
+int
+expand_skip_interface(struct node_if *interfaces)
+{
+	int errs = 0;
+
+	if ((!interfaces) || 
+	    ((!interfaces->next) && (!interfaces->not) &&
+	     (strcmp(interfaces->ifname, "none") == 0))) {
+		errs = pfctl_set_interface_flags(pf, "", PFI_IFLAG_SKIP, 0);
+		return (errs);
+	}
+
+	LOOP_THROUGH(struct node_if, interface, interfaces,
+		if (interface->not) {
+			yyerror("skip on ! <interface> is not supported");
+			errs++;
+		} else
+			errs += pfctl_set_interface_flags(pf,
+			     interface->ifname, PFI_IFLAG_SKIP, 1);
+	);
+
+	FREE_LIST(struct node_if, interfaces);
+
+	if (errs)
+		return (1);
+	else
+		return (0);
+}
+
 #undef FREE_LIST
 #undef LOOP_THROUGH
 
@@ -4565,6 +4601,7 @@
 		{ "rule",		RULE},
 		{ "scrub",		SCRUB},
 		{ "set",		SET},
+		{ "skip",		SKIP},
 		{ "source-hash",	SOURCEHASH},
 		{ "source-track",	SOURCETRACK},
 		{ "state",		STATE},
Index: sbin/pfctl/pfctl.c
===================================================================
RCS file: /usr/store/mlaier/ocvs/src/sbin/pfctl/pfctl.c,v
retrieving revision 1.223
diff -u -r1.223 pfctl.c
--- sbin/pfctl/pfctl.c	21 Sep 2004 16:59:11 -0000	1.223
+++ sbin/pfctl/pfctl.c	14 Dec 2004 17:37:42 -0000
@@ -1270,6 +1270,39 @@
 }
 
 int
+pfctl_set_interface_flags(struct pfctl *pf, char *ifname, int flags, int how)
+{
+	struct pfioc_iface pi;
+
+	if ((loadopt & PFCTL_FLAG_OPTION) == 0)
+		return (0);
+
+	bzero(&pi, sizeof(pi));
+
+	pi.pfiio_flags = flags;
+
+	if (strlcpy(pi.pfiio_name, ifname, sizeof(pi.pfiio_name)) >=
+	    sizeof(pi.pfiio_name))
+		errx(1, "pfctl_set_interface_flags: strlcpy");
+
+	if ((pf->opts & PF_OPT_NOACTION) == 0) {
+		if (how == 0) {
+			if (ioctl(pf->dev, DIOCCLRIFFLAG, &pi))
+				err(1, "DIOCCLRIFFLAG");
+		} else {
+			if (ioctl(pf->dev, DIOCSETIFFLAG, &pi))
+				err(1, "DIOCSETIFFLAG");
+		}
+	}
+
+	if (pf->opts & PF_OPT_VERBOSE)
+		printf("%s %s:0x%x flags\n", how?"set":"clear", pi.pfiio_name,
+		    pi.pfiio_flags);
+
+	return (0);
+}
+
+int
 pfctl_debug(int dev, u_int32_t level, int opts)
 {
 	if (ioctl(dev, DIOCSETDEBUG, &level))
Index: sbin/pfctl/pfctl_parser.h
===================================================================
RCS file: /usr/store/mlaier/ocvs/src/sbin/pfctl/pfctl_parser.h,v
retrieving revision 1.77
diff -u -r1.77 pfctl_parser.h
--- sbin/pfctl/pfctl_parser.h	16 Jul 2004 23:44:25 -0000	1.77
+++ sbin/pfctl/pfctl_parser.h	14 Dec 2004 17:37:42 -0000
@@ -187,6 +187,7 @@
 int	pfctl_set_logif(struct pfctl *, char *);
 int	pfctl_set_hostid(struct pfctl *, u_int32_t);
 int	pfctl_set_debug(struct pfctl *, char *);
+int	pfctl_set_interface_flags(struct pfctl *, char *, int, int);
 
 int	parse_rules(FILE *, struct pfctl *);
 int	parse_flags(char *);
Index: sbin/pfctl/pfctl_table.c
===================================================================
RCS file: /usr/store/mlaier/ocvs/src/sbin/pfctl/pfctl_table.c,v
retrieving revision 1.61
diff -u -r1.61 pfctl_table.c
--- sbin/pfctl/pfctl_table.c	12 Jun 2004 22:22:44 -0000	1.61
+++ sbin/pfctl/pfctl_table.c	14 Dec 2004 17:37:42 -0000
@@ -577,7 +577,8 @@
 	oprintf(flags, PFI_IFLAG_GROUP, "group", &first, 0);
 	oprintf(flags, PFI_IFLAG_CLONABLE, "clonable", &first, 0);
 	oprintf(flags, PFI_IFLAG_DYNAMIC, "dynamic", &first, 0);
-	oprintf(flags, PFI_IFLAG_ATTACHED, "attached", &first, 1);
+	oprintf(flags, PFI_IFLAG_ATTACHED, "attached", &first, 0);
+	oprintf(flags, PFI_IFLAG_SKIP, "skipped", &first, 1);
 	printf("\n");
 
 	if (!(opts & PF_OPT_VERBOSE2))
Index: share/man/man4/pf.4
===================================================================
RCS file: /usr/store/mlaier/ocvs/src/share/man/man4/pf.4,v
retrieving revision 1.53
diff -u -r1.53 pf.4
--- share/man/man4/pf.4	10 Dec 2004 03:29:02 -0000	1.53
+++ share/man/man4/pf.4	14 Dec 2004 17:48:38 -0000
@@ -1013,6 +1013,19 @@
 .Va pfiio_nzero
 will be set by the kernel to the number of interfaces and drivers
 that have been cleared.
+.It Dv DIOCSETIFFLAG Fa "struct pfioc_iface *io"
+Set the user setable flags (described below) of the pf internal interface
+description.
+The filtering process is the same as for
+.Dv DIOCIGETIFACES .
+.Bd -literal
+#define PFI_IFLAG_SKIP		0x0100	/* skip progressing */
+#define PFI_IFLAG_SETABLE_MASK	0x0100	/* setable via DIOC{SET,CLR}IFFLAG */
+.Ed
+.It Dv DIOCCLRIFFLAG Fa "struct pfioc_iface *io"
+works as
+.DV DIOCSETIFFLAG
+above but clears the flags.
 .El
 .Sh FILES
 .Bl -tag -width /dev/pf -compact
Index: share/man/man5/pf.conf.5
===================================================================
RCS file: /usr/store/mlaier/ocvs/src/share/man/man5/pf.conf.5,v
retrieving revision 1.314
diff -u -r1.314 pf.conf.5
--- share/man/man5/pf.conf.5	12 Dec 2004 17:41:55 -0000	1.314
+++ share/man/man5/pf.conf.5	14 Dec 2004 17:33:04 -0000
@@ -483,6 +483,16 @@
 .Pp
 .Dl set fingerprints \&"/etc/pf.os.devel\&"
 .Pp
+.It Ar set skip on <interface>
+List interfaces that should not be included in the ruleset progressing.
+This can be helpful for lo0 and interfaces that only see encapsulated
+traffic (e.g. pppoe).
+If skipping is enabled on a given interface can be determined with
+.Xr pfctl 8 .
+To disable skipping on all interfaces (default) use:
+.Pp
+.Dl set skip on none
+.Pp
 .It Ar set debug
 Set the debug
 .Ar level
Index: sys/net/pf.c
===================================================================
RCS file: /usr/store/mlaier/ocvs/src/sys/net/pf.c,v
retrieving revision 1.474
diff -u -r1.474 pf.c
--- sys/net/pf.c	14 Dec 2004 03:49:06 -0000	1.474
+++ sys/net/pf.c	14 Dec 2004 16:27:10 -0000
@@ -5627,6 +5627,8 @@
 	kif = pfi_index2kif[ifp->if_index];
 	if (kif == NULL)
 		return (PF_DROP);
+	else if (kif->pfik_flags & PFI_IFLAG_SKIP)	/* later? */
+		return (PF_PASS);
 
 #ifdef DIAGNOSTIC
 	if ((m->m_flags & M_PKTHDR) == 0)
@@ -5934,6 +5936,8 @@
 	kif = pfi_index2kif[ifp->if_index];
 	if (kif == NULL)
 		return (PF_DROP);
+	else if (kif->pfik_flags & PFI_IFLAG_SKIP)	/* later? */
+		return (PF_PASS);
 
 #ifdef DIAGNOSTIC
 	if ((m->m_flags & M_PKTHDR) == 0)
Index: sys/net/pf_if.c
===================================================================
RCS file: /usr/store/mlaier/ocvs/src/sys/net/pf_if.c,v
retrieving revision 1.22
diff -u -r1.22 pf_if.c
--- sys/net/pf_if.c	13 Dec 2004 23:51:22 -0000	1.22
+++ sys/net/pf_if.c	14 Dec 2004 16:25:31 -0000
@@ -689,6 +689,44 @@
 }
 
 int
+pfi_set_flags(const char *name, int flags)
+{
+	struct pfi_kif	*p;
+
+	if (flags & ~PFI_IFLAG_SETABLE_MASK)
+		return (EINVAL);
+
+	s = splsoftnet();
+	RB_FOREACH(p, pfi_ifhead, &pfi_ifs) {
+		if (pfi_skip_if(name, p, PFI_FLAG_GROUP|PFI_FLAG_INSTANCE))
+			continue;
+		p->pfik_flags |= flags;
+	}
+	splx(s);
+
+	return (0);
+}
+
+int
+pfi_clear_flags(const char *name, int flags)
+{
+	struct pfi_kif	*p;
+
+	if (flags & ~PFI_IFLAG_SETABLE_MASK)
+		return (EINVAL);
+
+	s = splsoftnet();
+	RB_FOREACH(p, pfi_ifhead, &pfi_ifs) {
+		if (pfi_skip_if(name, p, PFI_FLAG_GROUP|PFI_FLAG_INSTANCE))
+			continue;
+		p->pfik_flags &= ~flags;
+	}
+	splx(s);
+
+	return (0);
+}
+
+int
 pfi_get_ifaces(const char *name, struct pfi_if *buf, int *size, int flags)
 {
 	struct pfi_kif	*p;
Index: sys/net/pf_ioctl.c
===================================================================
RCS file: /usr/store/mlaier/ocvs/src/sys/net/pf_ioctl.c,v
retrieving revision 1.136
diff -u -r1.136 pf_ioctl.c
--- sys/net/pf_ioctl.c	10 Dec 2004 22:13:26 -0000	1.136
+++ sys/net/pf_ioctl.c	14 Dec 2004 16:26:17 -0000
@@ -1028,6 +1028,8 @@
 		case DIOCCLRSRCNODES:
 		case DIOCIGETIFACES:
 		case DIOCICLRISTATS:
+		case DIOCSETIFFLAG:
+		case DIOCCLRIFFLAG:
 			break;
 		case DIOCRCLRTABLES:
 		case DIOCRADDTABLES:
@@ -2767,6 +2769,20 @@
 		break;
 	}
 
+	case DIOCSETIFFLAG: {
+		struct pfioc_iface *io = (struct pfioc_iface *)addr;
+
+		error = pfi_set_flags(io->pfiio_name, io->pfiio_flags);
+		break;
+	}
+
+	case DIOCCLRIFFLAG: {
+		struct pfioc_iface *io = (struct pfioc_iface *)addr;
+
+		error = pfi_clear_flags(io->pfiio_name, io->pfiio_flags);
+		break;
+	}
+
 	default:
 		error = ENODEV;
 		break;
Index: sys/net/pfvar.h
===================================================================
RCS file: /usr/store/mlaier/ocvs/src/sys/net/pfvar.h,v
retrieving revision 1.209
diff -u -r1.209 pfvar.h
--- sys/net/pfvar.h	10 Dec 2004 22:13:26 -0000	1.209
+++ sys/net/pfvar.h	14 Dec 2004 16:24:18 -0000
@@ -869,6 +869,8 @@
 #define PFI_IFLAG_CLONABLE	0x0010	/* clonable group */
 #define PFI_IFLAG_DYNAMIC	0x0020	/* dynamic group */
 #define PFI_IFLAG_ATTACHED	0x0040	/* interface attached */
+#define	PFI_IFLAG_SKIP		0x0100	/* skip progressing */
+#define	PFI_IFLAG_SETABLE_MASK	0x0100	/* setable via DIOC{SET,CLR}IFFLAG */
 
 struct pf_pdesc {
 	u_int64_t	 tot_len;	/* Make Mickey money */
@@ -1326,6 +1328,8 @@
 #define DIOCSETHOSTID	_IOWR('D', 86, u_int32_t)
 #define DIOCIGETIFACES	_IOWR('D', 87, struct pfioc_iface)
 #define DIOCICLRISTATS  _IOWR('D', 88, struct pfioc_iface)
+#define	DIOCSETIFFLAG	_IOWR('D', 89, struct pfioc_iface)
+#define	DIOCCLRIFFLAG	_IOWR('D', 90, struct pfioc_iface)
 
 #ifdef _KERNEL
 RB_HEAD(pf_src_tree, pf_src_node);
@@ -1488,6 +1492,8 @@
 void		 pfi_fill_oldstatus(struct pf_status *);
 int		 pfi_clr_istats(const char *, int *, int);
 int		 pfi_get_ifaces(const char *, struct pfi_if *, int *, int);
+int		 pfi_set_flags(const char *, int);
+int		 pfi_clear_flags(const char *, int);
 int		 pfi_match_addr(struct pfi_dynaddr *, struct pf_addr *,
 		    sa_family_t);
 

Attachment: pgp8qnql0WF2m.pgp
Description: PGP signature


Visit your host, monkey.org