npfctl: trunk
authorrmind <rmind@NetBSD.org>
Thu, 19 Jan 2017 20:18:17 +0000
branchtrunk
changeset 247994 d1707065190a
parent 247993 b8656f676a15
child 247995 f69bd2f79601
npfctl: - Add protocol filter option for "map". - Print user-friendly error if table contains an entry with invalid netmask. - Add support for inline ports.
usr.sbin/npf/npfctl/npf.conf.5
usr.sbin/npf/npfctl/npf_build.c
usr.sbin/npf/npfctl/npf_data.c
usr.sbin/npf/npfctl/npf_parse.y
usr.sbin/npf/npfctl/npfctl.h
--- a/usr.sbin/npf/npfctl/npf.conf.5	Thu Jan 19 19:09:06 2017 +0000
+++ b/usr.sbin/npf/npfctl/npf.conf.5	Thu Jan 19 20:18:17 2017 +0000
@@ -1,4 +1,4 @@
-.\"    $NetBSD: npf.conf.5,v 1.46 2017/01/03 01:29:49 rmind Exp $
+.\"    $NetBSD: npf.conf.5,v 1.47 2017/01/19 20:18:17 rmind Exp $
 .\"
 .\" Copyright (c) 2009-2017 The NetBSD Foundation, Inc.
 .\" All rights reserved.
@@ -228,11 +228,15 @@
 .Bd -literal
 procedure "someproc" {
 	log: npflog0
-	normalize: "random-id", "min-ttl" 64
+	normalize: "random-id", "min-ttl" 64, "max-mss" 1432
 }
 .Ed
 .Pp
 In this case, the procedure calls the logging and normalisation modules.
+Traffic normalisation has a set of different mechanisms.
+In the example above, the normalisation procedure has arguments which
+apply the following mechanisms: IPv4 ID randomisation, Don't Fragment (DF)
+flag cleansing, minimum TTL enforcement and TCP MSS "clamping".
 .Ss Misc
 Text after a hash
 .Pq Sq #
@@ -275,9 +279,9 @@
 ; Mapping for address translation.
 
 map		= "map" interface
-		  ( "static" [ "algo" algorithm ] | "dynamic" )
+		  ( "static" [ "algo" algorithm ] | "dynamic" ) [ proto ]
 		  net-seg ( "->" | "<-" | "<->" ) net-seg
-		  [ "pass" filt-opts ]
+		  [ "pass" [ proto ] filt-opts ]
 
 ; Rule procedure definition.  The name should be in the double quotes.
 ;
@@ -295,8 +299,7 @@
 group-opts	= name-string [ "in" | "out" ] [ "on" interface ]
 rule-list	= [ rule new-line ] rule-list
 
-npf-filter	= [ "family" family-opt ] [ "proto" protocol [ proto-opts ] ]
-		  ( "all" | filt-opts )
+npf-filter	= [ "family" family-opt ] [ proto ] ( "all" | filt-opts )
 static-rule	= ( "block" [ block-opts ] | "pass" )
 		  [ "stateful" | "stateful-ends" ]
 		  [ "in" | out" ] [ "final" ] [ "on" interface ]
@@ -306,6 +309,7 @@
 dynamic-ruleset	= "ruleset" group-opts
 rule		= static-rule | dynamic-ruleset
 
+proto		= "proto" protocol [ proto-opts ]
 block-opts	= "return-rst" | "return-icmp" | "return"
 family-opt	= "inet4" | "inet6"
 proto-opts	= "flags" tcp-flags [ "/" tcp-flag-mask ] |
@@ -345,7 +349,7 @@
 # Note: if $ext_if has multiple IP address (e.g. IPv6 as well),
 # then the translation address has to be specified explicitly.
 map $ext_if dynamic 10.1.1.0/24 -> $ext_if
-map $ext_if dynamic 10.1.1.2 port 22 <- $ext_if port 9022
+map $ext_if dynamic proto tcp 10.1.1.2 port 22 <- $ext_if port 9022
 
 procedure "log" {
 	# Note: npf_ext_log kernel module should be loaded, if not built-in.
--- a/usr.sbin/npf/npfctl/npf_build.c	Thu Jan 19 19:09:06 2017 +0000
+++ b/usr.sbin/npf/npfctl/npf_build.c	Thu Jan 19 20:18:17 2017 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_build.c,v 1.43 2017/01/03 01:29:49 rmind Exp $	*/
+/*	$NetBSD: npf_build.c,v 1.44 2017/01/19 20:18:17 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2011-2017 The NetBSD Foundation, Inc.
@@ -34,7 +34,7 @@
  */
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: npf_build.c,v 1.43 2017/01/03 01:29:49 rmind Exp $");
+__RCSID("$NetBSD: npf_build.c,v 1.44 2017/01/19 20:18:17 rmind Exp $");
 
 #include <sys/types.h>
 #include <sys/mman.h>
@@ -586,9 +586,9 @@
  */
 static nl_nat_t *
 npfctl_build_nat(int type, const char *ifname, const addr_port_t *ap,
-    const filt_opts_t *fopts, u_int flags)
+    const opt_proto_t *op, const filt_opts_t *fopts, u_int flags)
 {
-	const opt_proto_t op = { .op_proto = -1, .op_opts = NULL };
+	const opt_proto_t def_op = { .op_proto = -1, .op_opts = NULL };
 	fam_addr_mask_t *am = npfctl_get_singlefam(ap->ap_netaddr);
 	in_port_t port;
 	nl_nat_t *nat;
@@ -600,10 +600,13 @@
 	} else {
 		port = 0;
 	}
+	if (!op) {
+		op = &def_op;
+	}
 
 	nat = npf_nat_create(type, flags, ifname, am->fam_family,
 	    &am->fam_addr, am->fam_mask, port);
-	npfctl_build_code(nat, am->fam_family, &op, fopts);
+	npfctl_build_code(nat, am->fam_family, op, fopts);
 	npf_nat_insert(npf_conf, nat, NPF_PRI_LAST);
 	return nat;
 }
@@ -613,7 +616,7 @@
  */
 void
 npfctl_build_natseg(int sd, int type, const char *ifname,
-    const addr_port_t *ap1, const addr_port_t *ap2,
+    const addr_port_t *ap1, const addr_port_t *ap2, const opt_proto_t *op,
     const filt_opts_t *fopts, u_int algo)
 {
 	fam_addr_mask_t *am1 = NULL, *am2 = NULL;
@@ -692,12 +695,12 @@
 	if (type & NPF_NATIN) {
 		memset(&imfopts, 0, sizeof(filt_opts_t));
 		memcpy(&imfopts.fo_to, ap2, sizeof(addr_port_t));
-		nt1 = npfctl_build_nat(NPF_NATIN, ifname, ap1, fopts, flags);
+		nt1 = npfctl_build_nat(NPF_NATIN, ifname, ap1, op, fopts, flags);
 	}
 	if (type & NPF_NATOUT) {
 		memset(&imfopts, 0, sizeof(filt_opts_t));
 		memcpy(&imfopts.fo_from, ap1, sizeof(addr_port_t));
-		nt2 = npfctl_build_nat(NPF_NATOUT, ifname, ap2, fopts, flags);
+		nt2 = npfctl_build_nat(NPF_NATOUT, ifname, ap2, op, fopts, flags);
 	}
 
 	if (algo == NPF_ALGO_NPT66) {
--- a/usr.sbin/npf/npfctl/npf_data.c	Thu Jan 19 19:09:06 2017 +0000
+++ b/usr.sbin/npf/npfctl/npf_data.c	Thu Jan 19 20:18:17 2017 +0000
@@ -1,7 +1,7 @@
-/*	$NetBSD: npf_data.c,v 1.27 2016/12/27 22:35:33 rmind Exp $	*/
+/*	$NetBSD: npf_data.c,v 1.28 2017/01/19 20:18:17 rmind Exp $	*/
 
 /*-
- * Copyright (c) 2009-2014 The NetBSD Foundation, Inc.
+ * Copyright (c) 2009-2017 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -31,7 +31,7 @@
  */
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: npf_data.c,v 1.27 2016/12/27 22:35:33 rmind Exp $");
+__RCSID("$NetBSD: npf_data.c,v 1.28 2017/01/19 20:18:17 rmind Exp $");
 
 #include <stdlib.h>
 #include <stddef.h>
@@ -129,6 +129,12 @@
 	}
 }
 
+/*
+ * npfctl_parse_fam_addr: parse a given a string and return the address
+ * family with the actual address as npf_addr_t.
+ *
+ * => Return true on success; false otherwise.
+ */
 static bool
 npfctl_parse_fam_addr(const char *name, sa_family_t *fam, npf_addr_t *addr)
 {
@@ -154,41 +160,65 @@
 	return true;
 }
 
+/*
+ * npfctl_parse_mask: parse a given string which represents a mask and
+ * can either be in quad-dot or CIDR block notation; validates the mask
+ * given the family.
+ *
+ * => Returns true if mask is valid (or is NULL); false otherwise.
+ */
 static bool
 npfctl_parse_mask(const char *s, sa_family_t fam, npf_netmask_t *mask)
 {
+	unsigned max_mask = NPF_MAX_NETMASK;
 	char *ep = NULL;
 	npf_addr_t addr;
 	uint8_t *ap;
 
-	if (s) {
-		errno = 0;
-		*mask = (npf_netmask_t)strtol(s, &ep, 0);
-		if (*ep == '\0' && s != ep && errno != ERANGE)
-			return true;
-		if (!npfctl_parse_fam_addr(s, &fam, &addr))
-			return false;
-	}
-
 	assert(fam == AF_INET || fam == AF_INET6);
-	*mask = NPF_NO_NETMASK;
-	if (ep == NULL) {
+	if (!s) {
+		/* No mask. */
+		*mask = NPF_NO_NETMASK;
 		return true;
 	}
 
+	errno = 0;
+	*mask = (npf_netmask_t)strtol(s, &ep, 0);
+	if (*ep == '\0' && s != ep && errno != ERANGE) {
+		/* Just a number -- CIDR notation. */
+		goto check;
+	}
+
+	/* Other characters: try to parse a full address. */
+	if (!npfctl_parse_fam_addr(s, &fam, &addr)) {
+		return false;
+	}
+
+	/* Convert the address to CIDR block number. */
 	ap = addr.word8 + (*mask / 8) - 1;
 	while (ap >= addr.word8) {
 		for (int j = 8; j > 0; j--) {
 			if (*ap & 1)
-				return true;
+				goto check;
 			*ap >>= 1;
 			(*mask)--;
 			if (*mask == 0)
-				return true;
+				goto check;
 		}
 		ap--;
 	}
+	*mask = NPF_NO_NETMASK;
 	return true;
+check:
+	switch (fam) {
+	case AF_INET:
+		max_mask = 32;
+		break;
+	case AF_INET6:
+		max_mask = 128;
+		break;
+	}
+	return *mask <= max_mask;
 }
 
 /*
@@ -202,6 +232,7 @@
     unsigned long *nummask)
 {
 	fam_addr_mask_t fam;
+	char buf[32];
 
 	memset(&fam, 0, sizeof(fam));
 
@@ -209,12 +240,14 @@
 		return NULL;
 
 	/*
-	 * Note: both mask and nummask may be NULL.  In such case,
-	 * npfctl_parse_mask() will handle and will set full mask.
+	 * Mask may be NULL.  In such case, "no mask" value will be set.
 	 */
 	if (nummask) {
-		fam.fam_mask = *nummask;
-	} else if (!npfctl_parse_mask(mask, fam.fam_family, &fam.fam_mask)) {
+		/* Let npfctl_parse_mask() validate the number. */
+		snprintf(buf, sizeof(buf), "%lu", *nummask);
+		mask = buf;
+	}
+	if (!npfctl_parse_mask(mask, fam.fam_family, &fam.fam_mask)) {
 		return NULL;
 	}
 	return npfvar_create_element(NPFVAR_FAM, &fam, sizeof(fam));
@@ -249,17 +282,16 @@
 }
 
 npfvar_t *
-npfctl_parse_port_range_variable(const char *v)
+npfctl_parse_port_range_variable(const char *v, npfvar_t *vp)
 {
-	npfvar_t *vp = npfvar_lookup(v);
 	size_t count = npfvar_get_count(vp);
 	npfvar_t *pvp = npfvar_create();
 	port_range_t *pr;
-	in_port_t p;
 
 	for (size_t i = 0; i < count; i++) {
 		int type = npfvar_get_type(vp, i);
 		void *data = npfvar_get_data(vp, type, i);
+		in_port_t p;
 
 		switch (type) {
 		case NPFVAR_IDENTIFIER:
@@ -277,8 +309,13 @@
 			npfvar_add_elements(pvp, npfctl_parse_port_range(p, p));
 			break;
 		default:
-			yyerror("wrong variable '%s' type '%s' for port range",
-			    v, npfvar_type(type));
+			if (v) {
+				yyerror("wrong variable '%s' type '%s' "
+				    "for port range", v, npfvar_type(type));
+			} else {
+				yyerror("wrong element '%s' in the "
+				    "inline list", npfvar_type(type));
+			}
 			npfvar_destroy(pvp);
 			return NULL;
 		}
@@ -322,13 +359,11 @@
 		memset(&fam, 0, sizeof(fam));
 		fam.fam_family = sa->sa_family;
 		fam.fam_ifindex = ifna.ifna_index;
+		fam.fam_mask = NPF_NO_NETMASK;
 
 		if (!npfctl_copy_address(sa->sa_family, &fam.fam_addr, sa))
 			goto out;
 
-		if (!npfctl_parse_mask(NULL, fam.fam_family, &fam.fam_mask))
-			goto out;
-
 		if (!npfvar_add_element(vpa, NPFVAR_FAM, &fam, sizeof(fam)))
 			goto out;
 	}
--- a/usr.sbin/npf/npfctl/npf_parse.y	Thu Jan 19 19:09:06 2017 +0000
+++ b/usr.sbin/npf/npfctl/npf_parse.y	Thu Jan 19 20:18:17 2017 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_parse.y,v 1.41 2017/01/11 02:11:21 christos Exp $	*/
+/*	$NetBSD: npf_parse.y,v 1.42 2017/01/19 20:18:17 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2011-2017 The NetBSD Foundation, Inc.
@@ -36,7 +36,9 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#ifdef __NetBSD__
 #include <vis.h>
+#endif
 
 #include "npfctl.h"
 
@@ -65,11 +67,14 @@
 	fprintf(stderr, "%s:%d:%d: %s", yyfilename,
 	    yylineno - (int)eol, yycolumn, msg);
 	if (!eol) {
+#ifdef __NetBSD__
 		size_t len = strlen(context);
 		char *dst = ecalloc(1, len * 4 + 1);
 
 		strvisx(dst, context, len, VIS_WHITE|VIS_CSTYLE);
-		fprintf(stderr, " near '%s'", dst);
+		context = dst
+#endif
+		fprintf(stderr, " near '%s'", context);
 	}
 	fprintf(stderr, "\n");
 	exit(EXIT_FAILURE);
@@ -347,13 +352,14 @@
 	;
 
 map
-	: MAP ifref map_sd map_algo mapseg map_type mapseg PASS filt_opts
+	: MAP ifref map_sd map_algo mapseg map_type mapseg
+	  PASS opt_proto filt_opts
 	{
-		npfctl_build_natseg($3, $6, $2, &$5, &$7, &$9, $4);
+		npfctl_build_natseg($3, $6, $2, &$5, &$7, &$9, &$10, $4);
 	}
-	| MAP ifref map_sd map_algo mapseg map_type mapseg
+	| MAP ifref map_sd map_algo opt_proto mapseg map_type mapseg
 	{
-		npfctl_build_natseg($3, $6, $2, &$5, &$7, NULL, $4);
+		npfctl_build_natseg($3, $7, $2, &$6, &$8, &$5, NULL, $4);
 	}
 	| MAP RULESET group_opts
 	{
@@ -722,12 +728,14 @@
 	}
 	| PORT VAR_ID
 	{
-		$$ = npfctl_parse_port_range_variable($2);
+		npfvar_t *vp = npfvar_lookup($2);
+		$$ = npfctl_parse_port_range_variable($2, vp);
 	}
-	|
+	| PORT list
 	{
-		$$ = NULL;
+		$$ = npfctl_parse_port_range_variable(NULL, $2);
 	}
+	|			{ $$ = NULL; }
 	;
 
 port
--- a/usr.sbin/npf/npfctl/npfctl.h	Thu Jan 19 19:09:06 2017 +0000
+++ b/usr.sbin/npf/npfctl/npfctl.h	Thu Jan 19 20:18:17 2017 +0000
@@ -1,7 +1,7 @@
-/*	$NetBSD: npfctl.h,v 1.43 2017/01/03 01:29:49 rmind Exp $	*/
+/*	$NetBSD: npfctl.h,v 1.44 2017/01/19 20:18:17 rmind Exp $	*/
 
 /*-
- * Copyright (c) 2009-2013 The NetBSD Foundation, Inc.
+ * Copyright (c) 2009-2017 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
  * This material is based upon work partially supported by The
@@ -127,7 +127,7 @@
 npfvar_t *	npfctl_parse_table_id(const char *);
 npfvar_t * 	npfctl_parse_icmp(int, int, int);
 npfvar_t *	npfctl_parse_port_range(in_port_t, in_port_t);
-npfvar_t *	npfctl_parse_port_range_variable(const char *);
+npfvar_t *	npfctl_parse_port_range_variable(const char *, npfvar_t *);
 npfvar_t *	npfctl_parse_fam_addr_mask(const char *, const char *,
 		    unsigned long *);
 bool		npfctl_parse_cidr(char *, fam_addr_mask_t *, int *);
@@ -204,7 +204,7 @@
 		    const char *, const char *);
 void		npfctl_build_natseg(int, int, const char *,
 		    const addr_port_t *, const addr_port_t *,
-		    const filt_opts_t *, unsigned);
+		    const opt_proto_t *, const filt_opts_t *, unsigned);
 void		npfctl_build_maprset(const char *, int, const char *);
 void		npfctl_build_table(const char *, u_int, const char *);