netfilter-devel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v4 1/4] meta: Introduce new conditions 'time', 'day' and 'hour'
@ 2019-07-01 20:16 Ander Juaristi
  2019-07-01 20:16 ` [PATCH v4 2/4] meta: Parse 'time' type with timegm() Ander Juaristi
                   ` (4 more replies)
  0 siblings, 5 replies; 9+ messages in thread
From: Ander Juaristi @ 2019-07-01 20:16 UTC (permalink / raw)
  To: netfilter-devel

These keywords introduce new checks for a timestamp, an absolute date (which is converted to a timestamp),
an hour in the day (which is converted to the number of seconds since midnight) and a day of week.

When converting an ISO date (eg. 2019-06-06 17:00) to a timestamp,
we need to substract it the GMT difference in seconds, that is, the value
of the 'tm_gmtoff' field in the tm structure. This is because the kernel
doesn't know about time zones. And hence the kernel manages different timestamps
than those that are advertised in userspace when running, for instance, date +%s.

The same conversion needs to be done when converting hours (e.g 17:00) to seconds since midnight
as well.

The result needs to be computed module 86400 in case GMT offset (difference in seconds from UTC)
is negative.

We also introduce a new command line option (-t, --seconds) to show the actual
timestamps when printing the values, rather than the ISO dates, or the hour.

Some usage examples:

	time < "2019-06-06 17:00" drop;
	time < "2019-06-06 17:20:20" drop;
	time < 12341234 drop;
	day "Sat" drop;
	day 6 drop;
	hour >= 17:00 drop;
	hour >= "17:00:01" drop;
	hour >= 63000 drop;

Signed-off-by: Ander Juaristi <a@juaristi.eus>
---
 include/datatype.h                  |   3 +
 include/linux/netfilter/nf_tables.h |   6 +
 include/meta.h                      |   3 +
 include/nftables.h                  |   5 +
 include/nftables/libnftables.h      |   1 +
 src/datatype.c                      |   3 +
 src/main.c                          |  11 +-
 src/meta.c                          | 292 ++++++++++++++++++++++++++++
 src/parser_bison.y                  |   4 +
 src/scanner.l                       |   4 +-
 10 files changed, 330 insertions(+), 2 deletions(-)

diff --git a/include/datatype.h b/include/datatype.h
index 63617eb..1f46eb0 100644
--- a/include/datatype.h
+++ b/include/datatype.h
@@ -90,6 +90,9 @@ enum datatypes {
 	TYPE_CT_EVENTBIT,
 	TYPE_IFNAME,
 	TYPE_IGMP_TYPE,
+	TYPE_TIME_DATE,
+	TYPE_TIME_HOUR,
+	TYPE_TIME_DAY,
 	__TYPE_MAX
 };
 #define TYPE_MAX		(__TYPE_MAX - 1)
diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h
index 7bdb234..ce621ed 100644
--- a/include/linux/netfilter/nf_tables.h
+++ b/include/linux/netfilter/nf_tables.h
@@ -793,6 +793,9 @@ enum nft_exthdr_attributes {
  * @NFT_META_SECPATH: boolean, secpath_exists (!!skb->sp)
  * @NFT_META_IIFKIND: packet input interface kind name (dev->rtnl_link_ops->kind)
  * @NFT_META_OIFKIND: packet output interface kind name (dev->rtnl_link_ops->kind)
+ * @NFT_META_TIME: a UNIX timestamp
+ * @NFT_META_TIME_DAY: day of week
+ * @NFT_META_TIME_HOUR: hour of day
  */
 enum nft_meta_keys {
 	NFT_META_LEN,
@@ -823,6 +826,9 @@ enum nft_meta_keys {
 	NFT_META_SECPATH,
 	NFT_META_IIFKIND,
 	NFT_META_OIFKIND,
+	NFT_META_TIME,
+	NFT_META_TIME_DAY,
+	NFT_META_TIME_HOUR,
 };
 
 /**
diff --git a/include/meta.h b/include/meta.h
index a49b4ff..a62a130 100644
--- a/include/meta.h
+++ b/include/meta.h
@@ -41,6 +41,9 @@ extern const struct datatype uid_type;
 extern const struct datatype devgroup_type;
 extern const struct datatype pkttype_type;
 extern const struct datatype ifname_type;
+extern const struct datatype date_type;
+extern const struct datatype hour_type;
+extern const struct datatype day_type;
 
 extern struct symbol_table *devgroup_tbl;
 
diff --git a/include/nftables.h b/include/nftables.h
index ed446e2..52aff12 100644
--- a/include/nftables.h
+++ b/include/nftables.h
@@ -62,6 +62,11 @@ static inline bool nft_output_guid(const struct output_ctx *octx)
 	return octx->flags & NFT_CTX_OUTPUT_GUID;
 }
 
+static inline bool nft_output_seconds(const struct output_ctx *octx)
+{
+	return octx->flags & NFT_CTX_OUTPUT_SECONDS;
+}
+
 static inline bool nft_output_numeric_proto(const struct output_ctx *octx)
 {
 	return octx->flags & NFT_CTX_OUTPUT_NUMERIC_PROTO;
diff --git a/include/nftables/libnftables.h b/include/nftables/libnftables.h
index e39c588..87d4ff5 100644
--- a/include/nftables/libnftables.h
+++ b/include/nftables/libnftables.h
@@ -52,6 +52,7 @@ enum {
 	NFT_CTX_OUTPUT_NUMERIC_PROTO	= (1 << 7),
 	NFT_CTX_OUTPUT_NUMERIC_PRIO     = (1 << 8),
 	NFT_CTX_OUTPUT_NUMERIC_SYMBOL	= (1 << 9),
+	NFT_CTX_OUTPUT_SECONDS          = (1 << 10),
 	NFT_CTX_OUTPUT_NUMERIC_ALL	= (NFT_CTX_OUTPUT_NUMERIC_PROTO |
 					   NFT_CTX_OUTPUT_NUMERIC_PRIO |
 					   NFT_CTX_OUTPUT_NUMERIC_SYMBOL),
diff --git a/src/datatype.c b/src/datatype.c
index 6d6826e..0a00535 100644
--- a/src/datatype.c
+++ b/src/datatype.c
@@ -71,6 +71,9 @@ static const struct datatype *datatypes[TYPE_MAX + 1] = {
 	[TYPE_BOOLEAN]		= &boolean_type,
 	[TYPE_IFNAME]		= &ifname_type,
 	[TYPE_IGMP_TYPE]	= &igmp_type_type,
+	[TYPE_TIME_DATE]	= &date_type,
+	[TYPE_TIME_HOUR]	= &hour_type,
+	[TYPE_TIME_DAY]		= &day_type,
 };
 
 const struct datatype *datatype_lookup(enum datatypes type)
diff --git a/src/main.c b/src/main.c
index 9a50f30..dc8ad91 100644
--- a/src/main.c
+++ b/src/main.c
@@ -43,8 +43,9 @@ enum opt_vals {
 	OPT_NUMERIC_PRIO	= 'y',
 	OPT_NUMERIC_PROTO	= 'p',
 	OPT_INVALID		= '?',
+	OPT_SECONDS		= 't',
 };
-#define OPTSTRING	"hvcf:iI:jvnsNaeSupyp"
+#define OPTSTRING	"hvcf:iI:jvnsNaeSupypt"
 
 static const struct option options[] = {
 	{
@@ -114,6 +115,10 @@ static const struct option options[] = {
 		.name		= "numeric-priority",
 		.val		= OPT_NUMERIC_PRIO,
 	},
+	{
+		.name		= "seconds",
+		.val		= OPT_SECONDS,
+	},
 	{
 		.name		= NULL
 	}
@@ -143,6 +148,7 @@ static void show_help(const char *name)
 "  -a, --handle			Output rule handle.\n"
 "  -e, --echo			Echo what has been added, inserted or replaced.\n"
 "  -I, --includepath <directory>	Add <directory> to the paths searched for include files. Default is: %s\n"
+"  -t, --seconds                Show hour values in seconds since midnight.\n"
 "  --debug <level [,level...]>	Specify debugging level (scanner, parser, eval, netlink, mnl, proto-ctx, segtree, all)\n"
 "\n",
 	name, DEFAULT_INCLUDE_PATH);
@@ -282,6 +288,9 @@ int main(int argc, char * const *argv)
 		case OPT_GUID:
 			output_flags |= NFT_CTX_OUTPUT_GUID;
 			break;
+		case OPT_SECONDS:
+			output_flags |= NFT_CTX_OUTPUT_SECONDS;
+			break;
 		case OPT_NUMERIC_PRIO:
 			output_flags |= NFT_CTX_OUTPUT_NUMERIC_PRIO;
 			break;
diff --git a/src/meta.c b/src/meta.c
index 1e8964e..41405f1 100644
--- a/src/meta.c
+++ b/src/meta.c
@@ -37,6 +37,10 @@
 #include <iface.h>
 #include <json.h>
 
+#define _XOPEN_SOURCE
+#define __USE_XOPEN
+#include <time.h>
+
 static struct symbol_table *realm_tbl;
 void realm_table_meta_init(void)
 {
@@ -383,6 +387,285 @@ const struct datatype ifname_type = {
 	.basetype	= &string_type,
 };
 
+static void date_type_print(const struct expr *expr, struct output_ctx *octx)
+{
+	char timestr[21];
+	struct tm *tm;
+	uint64_t tstamp = mpz_get_uint64(expr->value);
+
+	if (!nft_output_seconds(octx)) {
+		if ((tm = localtime((time_t *) &tstamp)) == NULL ||
+			!strftime(timestr, sizeof(timestr) - 1, "%F %T", tm))
+			nft_print(octx, "Error converting timestamp to printed time");
+
+		nft_print(octx, "\"%s\"", timestr);
+		return;
+	}
+
+	expr_basetype(expr)->print(expr, octx);
+}
+
+static time_t parse_iso_date(const char *sym)
+{
+	time_t ts = time(NULL);
+	long int gmtoff;
+	struct tm tm, *cur_tm;
+
+	memset(&tm, 0, sizeof(struct tm));
+
+	/* Obtain current tm as well, so that we can substract tm_gmtoff */
+	cur_tm = localtime(&ts);
+
+	if (strptime(sym, "%F %T", &tm))
+		goto success;
+	if (strptime(sym, "%F %R", &tm))
+		goto success;
+	if (strptime(sym, "%F", &tm))
+		goto success;
+
+	return -1;
+
+success:
+	setenv("TZ", "UTC", true);
+	tzset();
+
+	ts = mktime(&tm);
+	if (ts == (time_t) -1 || cur_tm == NULL)
+		return ts;
+
+	/* Substract tm_gmtoff to get the current time */
+	gmtoff = cur_tm->tm_gmtoff;
+	if (cur_tm->tm_isdst == 1)
+		gmtoff -= 3600;
+	return ts - gmtoff;
+}
+
+static struct error_record *date_type_parse(const struct expr *sym,
+					    struct expr **res)
+{
+	time_t tstamp;
+	const char *endptr = sym->identifier;
+
+	if ((tstamp = parse_iso_date(sym->identifier)) != -1)
+		goto success;
+
+	tstamp = strtoul(sym->identifier, (char **) &endptr, 10);
+	if (*endptr == '\0' && endptr != sym->identifier)
+		goto success;
+
+	return error(&sym->location, "Cannot parse date");
+
+success:
+	*res = constant_expr_alloc(&sym->location, sym->dtype,
+				   BYTEORDER_HOST_ENDIAN,
+				   8 * BITS_PER_BYTE,
+				   &tstamp);
+	return NULL;
+}
+
+static void day_type_print(const struct expr *expr, struct output_ctx *octx)
+{
+	const char *days[] = {
+		"Sunday",
+		"Monday",
+		"Tuesday",
+		"Wednesday",
+		"Thursday",
+		"Friday",
+		"Saturday"
+	};
+	uint8_t daynum = mpz_get_uint8(expr->value),
+		 numdays = array_size(days);
+
+	if (daynum >= 0 && daynum < numdays)
+		nft_print(octx, "\"%s\"", days[daynum]);
+	else
+		nft_print(octx, "Unknown day");
+}
+
+static int parse_day_type_numeric(const char *sym)
+{
+	char c = *sym;
+	return (c >= '0' && c <= '6') ?
+		(c - '0') :
+		-1;
+}
+
+static struct error_record *day_type_parse(const struct expr *sym,
+					   struct expr **res)
+{
+	const char *days[] = {
+		"Sunday",
+		"Monday",
+		"Tuesday",
+		"Wednesday",
+		"Thursday",
+		"Friday",
+		"Saturday"
+	};
+	int daynum = -1, numdays = array_size(days);
+	int symlen = strlen(sym->identifier), daylen;
+
+	if (symlen < 3) {
+		if (symlen == 1)
+			daynum = parse_day_type_numeric(sym->identifier);
+
+		if (daynum >= 0)
+			goto success;
+
+		return error(&sym->location, "Day name must be at least three characters long");
+	}
+
+	for (int i = 0; i < numdays && daynum == -1; i++) {
+		daylen = strlen(days[i]);
+
+		if (strncasecmp(sym->identifier,
+				days[i],
+				min(symlen, daylen)) == 0)
+			daynum = i;
+	}
+
+	if (daynum == -1)
+		return error(&sym->location, "Cannot parse day");
+
+success:
+	*res = constant_expr_alloc(&sym->location, sym->dtype,
+				   BYTEORDER_HOST_ENDIAN,
+				   1 * BITS_PER_BYTE,
+				   &daynum);
+	return NULL;
+}
+
+static void __hour_type_print_r(int hours, int minutes, int seconds, char *out, size_t buflen)
+{
+	if (minutes == 60)
+		return __hour_type_print_r(++hours, 0, seconds, out, buflen);
+	else if (minutes > 60)
+		return __hour_type_print_r((int) (minutes / 60), minutes % 60, seconds, out, buflen);
+
+	if (seconds == 60)
+		return __hour_type_print_r(hours, ++minutes, 0, out, buflen);
+	else if (seconds > 60)
+		return __hour_type_print_r(hours, (int) (seconds / 60), seconds % 60, out, buflen);
+
+	if (seconds == 0)
+		snprintf(out, buflen, "%02d:%02d", hours, minutes);
+	else
+		snprintf(out, buflen, "%02d:%02d:%02d", hours, minutes, seconds);
+}
+
+static void hour_type_print(const struct expr *expr, struct output_ctx *octx)
+{
+	char out[9];
+	time_t ts;
+	struct tm *cur_tm;
+	uint64_t seconds = mpz_get_uint64(expr->value);
+
+	if (!nft_output_seconds(octx)) {
+		/* Obtain current tm, so that we can add tm_gmtoff */
+		ts = time(NULL);
+		cur_tm = localtime(&ts);
+
+		if (cur_tm)
+			seconds = (seconds + cur_tm->tm_gmtoff) % 86400;
+
+		__hour_type_print_r(0, 0, seconds, out, sizeof(out));
+		nft_print(octx, "\"%s\"", out);
+
+		return;
+	}
+
+	expr_basetype(expr)->print(expr, octx);
+}
+
+static struct error_record *hour_type_parse(const struct expr *sym,
+					    struct expr **res)
+{
+	time_t ts;
+	char *endptr;
+	struct tm tm, *cur_tm;
+	uint64_t result = 0;
+	struct error_record *er;
+
+	memset(&tm, 0, sizeof(struct tm));
+
+	/* First, try to parse it as a number */
+	result = strtoul(sym->identifier, (char **) &endptr, 10);
+	if (*endptr == '\0' && endptr != sym->identifier)
+		goto success;
+
+	result = 0;
+
+	/* Obtain current tm, so that we can substract tm_gmtoff */
+	ts = time(NULL);
+	cur_tm = localtime(&ts);
+
+	if (strptime(sym->identifier, "%T", &tm))
+		goto convert;
+	if (strptime(sym->identifier, "%R", &tm))
+		goto convert;
+
+	if ((er = time_parse(&sym->location, sym->identifier, &result)) == NULL) {
+		result /= 1000;
+		goto convert;
+	}
+
+	return er;
+
+convert:
+	/* Convert the hour to the number of seconds since midnight */
+	if (result == 0)
+		result = tm.tm_hour * 3600 + tm.tm_min * 60 + tm.tm_sec;
+
+	/* Substract tm_gmtoff to get the current time */
+	if (cur_tm) {
+		if ((long int) result >= cur_tm->tm_gmtoff)
+			result = (result - cur_tm->tm_gmtoff) % 86400;
+		else
+			result = 86400 - cur_tm->tm_gmtoff + result;
+	}
+
+success:
+	*res = constant_expr_alloc(&sym->location, sym->dtype,
+				   BYTEORDER_HOST_ENDIAN,
+				   8 * BITS_PER_BYTE,
+				   &result);
+	return NULL;
+}
+
+const struct datatype date_type = {
+	.type = TYPE_TIME_DATE,
+	.name = "time",
+	.desc = "Relative time of packet reception",
+	.byteorder = BYTEORDER_HOST_ENDIAN,
+	.size = sizeof(uint64_t) * BITS_PER_BYTE,
+	.basetype = &integer_type,
+	.print = date_type_print,
+	.parse = date_type_parse,
+};
+
+const struct datatype day_type = {
+	.type = TYPE_TIME_DAY,
+	.name = "day",
+	.desc = "Day of week of packet reception",
+	.byteorder = BYTEORDER_HOST_ENDIAN,
+	.size = 1 * BITS_PER_BYTE,
+	.basetype = &integer_type,
+	.print = day_type_print,
+	.parse = day_type_parse,
+};
+
+const struct datatype hour_type = {
+	.type = TYPE_TIME_HOUR,
+	.name = "hour",
+	.desc = "Hour of day of packet reception",
+	.byteorder = BYTEORDER_HOST_ENDIAN,
+	.size = sizeof(uint64_t) * BITS_PER_BYTE,
+	.basetype = &integer_type,
+	.print = hour_type_print,
+	.parse = hour_type_parse,
+};
+
 const struct meta_template meta_templates[] = {
 	[NFT_META_LEN]		= META_TEMPLATE("length",    &integer_type,
 						4 * 8, BYTEORDER_HOST_ENDIAN),
@@ -450,6 +733,15 @@ const struct meta_template meta_templates[] = {
 	[NFT_META_OIFKIND]	= META_TEMPLATE("oifkind",   &ifname_type,
 						IFNAMSIZ * BITS_PER_BYTE,
 						BYTEORDER_HOST_ENDIAN),
+	[NFT_META_TIME]		= META_TEMPLATE("time",   &date_type,
+						8 * BITS_PER_BYTE,
+						BYTEORDER_HOST_ENDIAN),
+	[NFT_META_TIME_DAY]	= META_TEMPLATE("day", &day_type,
+						1 * BITS_PER_BYTE,
+						BYTEORDER_HOST_ENDIAN),
+	[NFT_META_TIME_HOUR]	= META_TEMPLATE("hour", &hour_type,
+						8 * BITS_PER_BYTE,
+						BYTEORDER_HOST_ENDIAN),
 };
 
 static bool meta_key_is_unqualified(enum nft_meta_keys key)
diff --git a/src/parser_bison.y b/src/parser_bison.y
index 670e91f..26b64da 100644
--- a/src/parser_bison.y
+++ b/src/parser_bison.y
@@ -415,6 +415,7 @@ int nft_lex(void *, void *, void *);
 %token IIFGROUP			"iifgroup"
 %token OIFGROUP			"oifgroup"
 %token CGROUP			"cgroup"
+%token TIME			"time"
 
 %token CLASSID			"classid"
 %token NEXTHOP			"nexthop"
@@ -3886,6 +3887,9 @@ meta_key_unqualified	:	MARK		{ $$ = NFT_META_MARK; }
 			|       OIFGROUP	{ $$ = NFT_META_OIFGROUP; }
 			|       CGROUP		{ $$ = NFT_META_CGROUP; }
 			|       IPSEC		{ $$ = NFT_META_SECPATH; }
+			|       TIME		{ $$ = NFT_META_TIME; }
+			|       DAY		{ $$ = NFT_META_TIME_DAY; }
+			|       HOUR		{ $$ = NFT_META_TIME_HOUR; }
 			;
 
 meta_stmt		:	META	meta_key	SET	stmt_expr
diff --git a/src/scanner.l b/src/scanner.l
index d1f6e87..bd28141 100644
--- a/src/scanner.l
+++ b/src/scanner.l
@@ -411,7 +411,9 @@ addrstring	({macaddr}|{ip4addr}|{ip6addr})
 "sack2"			{ return SACK2; }
 "sack3"			{ return SACK3; }
 "sack-permitted"	{ return SACK_PERMITTED; }
-"timestamp"		{ return TIMESTAMP; }
+"time"			{ return TIME; }
+"day"			{ return DAY; }
+"hour"			{ return HOUR; }
 
 "kind"			{ return KIND; }
 "count"			{ return COUNT; }
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v4 2/4] meta: Parse 'time' type with timegm()
  2019-07-01 20:16 [PATCH v4 1/4] meta: Introduce new conditions 'time', 'day' and 'hour' Ander Juaristi
@ 2019-07-01 20:16 ` Ander Juaristi
  2019-07-02 23:19   ` Pablo Neira Ayuso
  2019-07-01 20:16 ` [PATCH v4 3/4] tests/py: Add tests for 'time', 'day' and 'hour' Ander Juaristi
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 9+ messages in thread
From: Ander Juaristi @ 2019-07-01 20:16 UTC (permalink / raw)
  To: netfilter-devel

Use timegm() to parse the 'time' time. The timegm() function is only
available on Linux, but overwriting the TZ environment variable seems to
be a much worse solution.

The problem is that we need to convert an ISO date to a timestamp
without taking into account the time zone offset, since comparison will
be done in kernel space and there is no time zone information there.

Overwriting TZ is portable, but will cause problems when parsing a
ruleset that has 'time' and 'hour' rules. Parsing an 'hour' type must
not do time zone conversion, but that will be automatically done if TZ has
been overwritten to UTC.

We could record the initial setting of the
TZ variable on start, but there's no reliable way to do that (we'd have
to store the initial TZ in a global variable at program start and re-set
it every time we parse an 'hour' value).

Hence it's better idea to use timegm(), even though it's non-portable.
Netfilter is a Linux program after all.

Signed-off-by: Ander Juaristi <a@juaristi.eus>
---
 src/meta.c | 33 +++++++++++++++++++++++++--------
 1 file changed, 25 insertions(+), 8 deletions(-)

diff --git a/src/meta.c b/src/meta.c
index 41405f1..6f8b842 100644
--- a/src/meta.c
+++ b/src/meta.c
@@ -390,15 +390,30 @@ const struct datatype ifname_type = {
 static void date_type_print(const struct expr *expr, struct output_ctx *octx)
 {
 	char timestr[21];
-	struct tm *tm;
+	struct tm *tm, *cur_tm;
+	long int gmtoff;
+	time_t cur_ts = time(NULL);
 	uint64_t tstamp = mpz_get_uint64(expr->value);
 
 	if (!nft_output_seconds(octx)) {
-		if ((tm = localtime((time_t *) &tstamp)) == NULL ||
-			!strftime(timestr, sizeof(timestr) - 1, "%F %T", tm))
+		/* Obtain current tm, to add tm_gmtoff to the timestamp */
+		cur_tm = localtime(&cur_ts);
+
+		/* Adjust the GMT offset */
+		if (cur_tm) {
+			gmtoff = cur_tm->tm_gmtoff;
+			if (cur_tm->tm_isdst == 1)
+				gmtoff -= 3600;
+
+			tstamp += gmtoff;
+		}
+
+		if ((tm = gmtime((time_t *) &tstamp)) != NULL &&
+			strftime(timestr, sizeof(timestr) - 1, "%F %T", tm))
+			nft_print(octx, "\"%s\"", timestr);
+		else
 			nft_print(octx, "Error converting timestamp to printed time");
 
-		nft_print(octx, "\"%s\"", timestr);
 		return;
 	}
 
@@ -426,10 +441,12 @@ static time_t parse_iso_date(const char *sym)
 	return -1;
 
 success:
-	setenv("TZ", "UTC", true);
-	tzset();
-
-	ts = mktime(&tm);
+	/*
+	 * Overwriting TZ is problematic if we're parsing hour types in this same process,
+	 * hence I'd rather use timegm() which doesn't take into account the TZ env variable,
+	 * even though it's Linux-specific.
+	 */
+	ts = timegm(&tm);
 	if (ts == (time_t) -1 || cur_tm == NULL)
 		return ts;
 
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v4 3/4] tests/py: Add tests for 'time', 'day' and 'hour'
  2019-07-01 20:16 [PATCH v4 1/4] meta: Introduce new conditions 'time', 'day' and 'hour' Ander Juaristi
  2019-07-01 20:16 ` [PATCH v4 2/4] meta: Parse 'time' type with timegm() Ander Juaristi
@ 2019-07-01 20:16 ` Ander Juaristi
  2019-07-02 23:20   ` Pablo Neira Ayuso
  2019-07-01 20:16 ` [PATCH v4 4/4] nft: Update documentation Ander Juaristi
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 9+ messages in thread
From: Ander Juaristi @ 2019-07-01 20:16 UTC (permalink / raw)
  To: netfilter-devel

Signed-off-by: Ander Juaristi <a@juaristi.eus>
---
 tests/py/ip/meta.t         |  9 +++++++
 tests/py/ip/meta.t.payload | 54 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 63 insertions(+)

diff --git a/tests/py/ip/meta.t b/tests/py/ip/meta.t
index 4db8835..b4c3ce9 100644
--- a/tests/py/ip/meta.t
+++ b/tests/py/ip/meta.t
@@ -3,6 +3,15 @@
 *ip;test-ip4;input
 
 icmp type echo-request;ok
+meta time "1970-05-23 21:07:14" drop;ok
+meta time "2019-06-21 17:00:00" drop;ok
+meta time "2019-07-01 00:00:00" drop;ok
+meta time "2019-07-01 00:01:00" drop;ok
+meta time "2019-07-01 00:00:01" drop;ok
+meta day "Saturday" drop;ok;meta day "Saturday" drop
+meta hour "17:00" drop;ok;meta hour "17:00" drop
+meta hour "00:00" drop;ok
+meta hour "00:01" drop;ok
 meta l4proto icmp icmp type echo-request;ok;icmp type echo-request
 meta l4proto ipv6-icmp icmpv6 type nd-router-advert;ok;icmpv6 type nd-router-advert
 meta l4proto 58 icmpv6 type nd-router-advert;ok;icmpv6 type nd-router-advert
diff --git a/tests/py/ip/meta.t.payload b/tests/py/ip/meta.t.payload
index 322c087..fdf81d2 100644
--- a/tests/py/ip/meta.t.payload
+++ b/tests/py/ip/meta.t.payload
@@ -1,3 +1,57 @@
+# meta time "1970-05-23 21:07:14" drop
+ip meta-test
+  [ meta load unknown => reg 1 ]
+  [ cmp eq reg 1 0x00bc4ff2 0x00000000 ]
+  [ immediate reg 0 drop ]
+
+# meta time "2019-06-21 17:00:00" drop
+ip meta-test input
+  [ meta load unknown => reg 1 ]
+  [ cmp eq reg 1 0x5d0cff00 0x00000000 ]
+  [ immediate reg 0 drop ]
+
+# meta time "2019-07-01 00:00:00" drop
+ip meta-test input
+  [ meta load unknown => reg 1 ]
+  [ cmp eq reg 1 0x5d193ef0 0x00000000 ]
+  [ immediate reg 0 drop ]
+
+# meta time "2019-07-01 00:01:00" drop
+ip meta-test input
+  [ meta load unknown => reg 1 ]
+  [ cmp eq reg 1 0x5d193f2c 0x00000000 ]
+  [ immediate reg 0 drop ]
+
+# meta time "2019-07-01 00:00:01" drop
+ip meta-test input
+  [ meta load unknown => reg 1 ]
+  [ cmp eq reg 1 0x5d193ef1 0x00000000 ]
+  [ immediate reg 0 drop ]
+
+# meta day "Saturday" drop
+ip test-ip4 input
+  [ meta load unknown => reg 1 ]
+  [ cmp eq reg 1 0x00000006 ]
+  [ immediate reg 0 drop ]
+
+# meta hour "17:00" drop
+ip test-ip4 input
+  [ meta load unknown => reg 1 ]
+  [ cmp eq reg 1 0x0000d2f0 0x00000000 ]
+  [ immediate reg 0 drop ]
+
+# meta hour "00:00" drop
+ip meta-test input
+  [ meta load unknown => reg 1 ]
+  [ cmp eq reg 1 0x00013560 0x00000000 ]
+  [ immediate reg 0 drop ]
+
+# meta hour "00:01" drop
+ip meta-test input
+  [ meta load unknown => reg 1 ]
+  [ cmp eq reg 1 0x0001359c 0x00000000 ]
+  [ immediate reg 0 drop ]
+
 # icmp type echo-request
 ip test-ip4 input
   [ meta load l4proto => reg 1 ]
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v4 4/4] nft: Update documentation
  2019-07-01 20:16 [PATCH v4 1/4] meta: Introduce new conditions 'time', 'day' and 'hour' Ander Juaristi
  2019-07-01 20:16 ` [PATCH v4 2/4] meta: Parse 'time' type with timegm() Ander Juaristi
  2019-07-01 20:16 ` [PATCH v4 3/4] tests/py: Add tests for 'time', 'day' and 'hour' Ander Juaristi
@ 2019-07-01 20:16 ` Ander Juaristi
  2019-07-02 23:18 ` [PATCH v4 1/4] meta: Introduce new conditions 'time', 'day' and 'hour' Pablo Neira Ayuso
  2019-07-02 23:26 ` Pablo Neira Ayuso
  4 siblings, 0 replies; 9+ messages in thread
From: Ander Juaristi @ 2019-07-01 20:16 UTC (permalink / raw)
  To: netfilter-devel

Signed-off-by: Ander Juaristi <a@juaristi.eus>
---
 doc/nft.txt                |  6 +++++-
 doc/primary-expression.txt | 27 +++++++++++++++++++++++++--
 src/main.c                 |  2 +-
 3 files changed, 31 insertions(+), 4 deletions(-)

diff --git a/doc/nft.txt b/doc/nft.txt
index 3f1074b..b7a8ee8 100644
--- a/doc/nft.txt
+++ b/doc/nft.txt
@@ -9,7 +9,7 @@ nft - Administration tool of the nftables framework for packet filtering and cla
 SYNOPSIS
 --------
 [verse]
-*nft* [ *-nNscaeSupyj* ] [ *-I* 'directory' ] [ *-f* 'filename' | *-i* | 'cmd' ...]
+*nft* [ *-nNscaeSupyjt* ] [ *-I* 'directory' ] [ *-f* 'filename' | *-i* | 'cmd' ...]
 *nft* *-h*
 *nft* *-v*
 
@@ -93,6 +93,10 @@ For a full summary of options, run *nft --help*.
 	Read input from an interactive readline CLI. You can use quit to exit, or use the EOF marker,
 	normally this is CTRL-D.
 
+*-t*::
+*--seconds*::
+	Show time, day and hour values in seconds.
+
 INPUT FILE FORMATS
 ------------------
 LEXICAL CONVENTIONS
diff --git a/doc/primary-expression.txt b/doc/primary-expression.txt
index 6eb9583..cef2afc 100644
--- a/doc/primary-expression.txt
+++ b/doc/primary-expression.txt
@@ -2,7 +2,7 @@ META EXPRESSIONS
 ~~~~~~~~~~~~~~~~
 [verse]
 *meta* {*length* | *nfproto* | *l4proto* | *protocol* | *priority*}
-[*meta*] {*mark* | *iif* | *iifname* | *iiftype* | *oif* | *oifname* | *oiftype* | *skuid* | *skgid* | *nftrace* | *rtclassid* | *ibrname* | *obrname* | *pkttype* | *cpu* | *iifgroup* | *oifgroup* | *cgroup* | *random* | *ipsec* | *iifkind* | *oifkind*}
+[*meta*] {*mark* | *iif* | *iifname* | *iiftype* | *oif* | *oifname* | *oiftype* | *skuid* | *skgid* | *nftrace* | *rtclassid* | *ibrname* | *obrname* | *pkttype* | *cpu* | *iifgroup* | *oifgroup* | *cgroup* | *random* | *ipsec* | *iifkind* | *oifkind* | *time* | *hour* | *day* }
 
 A meta expression refers to meta data associated with a packet.
 
@@ -115,7 +115,16 @@ boolean (1 bit)
 |iifkind|
 Input interface kind |
 |oifkind|
-Output interface kind
+Output interface kind|
+|time|
+Absolute time of packet reception|
+Integer (32 bit) or string
+|day|
+Day of week|
+Integer (8 bit) or string
+|hour|
+Hour of day|
+String
 |====================
 
 .Meta expression specific types
@@ -141,6 +150,20 @@ Packet type: *host* (addressed to local host), *broadcast* (to all),
 *multicast* (to group), *other* (addressed to another host).
 |ifkind|
 Interface kind (16 byte string). Does not have to exist.
+|time|
+Either an integer or a date in ISO format. For example: "2019-06-06 17:00".
+Hour and seconds are optional and can be omitted if desired. If omitted,
+midnight will be assumed.
+The following three would be equivalent: "2019-06-06", "2019-06-06 00:00"
+and "2019-06-06 00:00:00".
+When an integer is given, it is assumed to be a UNIX timestamp.
+|day|
+Either a day of week ("Monday", "Tuesday", etc.), or an integer between 0 and 6.
+Strings are matched case-insensitively, and a full match is not expected (e.g. "Mon" would match "Monday").
+When an integer is given, 0 is Sunday and 6 is Saturday.
+|hour|
+A string representing an hour in 24-hour format. Seconds can optionally be specified.
+For example, 17:00 and 17:00:00 would be equivalent.
 |=============================
 
 .Using meta expressions
diff --git a/src/main.c b/src/main.c
index dc8ad91..5bffa9a 100644
--- a/src/main.c
+++ b/src/main.c
@@ -148,7 +148,7 @@ static void show_help(const char *name)
 "  -a, --handle			Output rule handle.\n"
 "  -e, --echo			Echo what has been added, inserted or replaced.\n"
 "  -I, --includepath <directory>	Add <directory> to the paths searched for include files. Default is: %s\n"
-"  -t, --seconds                Show hour values in seconds since midnight.\n"
+"  -t, --seconds                Show time, day and hour values in seconds.\n"
 "  --debug <level [,level...]>	Specify debugging level (scanner, parser, eval, netlink, mnl, proto-ctx, segtree, all)\n"
 "\n",
 	name, DEFAULT_INCLUDE_PATH);
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH v4 1/4] meta: Introduce new conditions 'time', 'day' and 'hour'
  2019-07-01 20:16 [PATCH v4 1/4] meta: Introduce new conditions 'time', 'day' and 'hour' Ander Juaristi
                   ` (2 preceding siblings ...)
  2019-07-01 20:16 ` [PATCH v4 4/4] nft: Update documentation Ander Juaristi
@ 2019-07-02 23:18 ` Pablo Neira Ayuso
  2019-07-02 23:26 ` Pablo Neira Ayuso
  4 siblings, 0 replies; 9+ messages in thread
From: Pablo Neira Ayuso @ 2019-07-02 23:18 UTC (permalink / raw)
  To: Ander Juaristi; +Cc: netfilter-devel

On Mon, Jul 01, 2019 at 10:16:43PM +0200, Ander Juaristi wrote:
[...]
>  include/datatype.h                  |   3 +
>  include/linux/netfilter/nf_tables.h |   6 +
>  include/meta.h                      |   3 +
>  include/nftables.h                  |   5 +
>  include/nftables/libnftables.h      |   1 +
>  src/datatype.c                      |   3 +
>  src/main.c                          |  11 +-
>  src/meta.c                          | 292 ++++++++++++++++++++++++++++
>  src/parser_bison.y                  |   4 +
>  src/scanner.l                       |   4 +-
>  10 files changed, 330 insertions(+), 2 deletions(-)

I don't see any update on the json bits in this diffstat.

Please make sure you run:

        ./configure ... --with-json

then, also run json tests:

        tests/py# python nft-tests.py -j

Thanks.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v4 2/4] meta: Parse 'time' type with timegm()
  2019-07-01 20:16 ` [PATCH v4 2/4] meta: Parse 'time' type with timegm() Ander Juaristi
@ 2019-07-02 23:19   ` Pablo Neira Ayuso
  0 siblings, 0 replies; 9+ messages in thread
From: Pablo Neira Ayuso @ 2019-07-02 23:19 UTC (permalink / raw)
  To: Ander Juaristi; +Cc: netfilter-devel

On Mon, Jul 01, 2019 at 10:16:44PM +0200, Ander Juaristi wrote:
> Use timegm() to parse the 'time' time. The timegm() function is only
> available on Linux, but overwriting the TZ environment variable seems to
> be a much worse solution.
> 
> The problem is that we need to convert an ISO date to a timestamp
> without taking into account the time zone offset, since comparison will
> be done in kernel space and there is no time zone information there.
> 
> Overwriting TZ is portable, but will cause problems when parsing a
> ruleset that has 'time' and 'hour' rules. Parsing an 'hour' type must
> not do time zone conversion, but that will be automatically done if TZ has
> been overwritten to UTC.
> 
> We could record the initial setting of the
> TZ variable on start, but there's no reliable way to do that (we'd have
> to store the initial TZ in a global variable at program start and re-set
> it every time we parse an 'hour' value).
> 
> Hence it's better idea to use timegm(), even though it's non-portable.
> Netfilter is a Linux program after all.

Please, squash this patch into 1/4 in your next batch. Thanks.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v4 3/4] tests/py: Add tests for 'time', 'day' and 'hour'
  2019-07-01 20:16 ` [PATCH v4 3/4] tests/py: Add tests for 'time', 'day' and 'hour' Ander Juaristi
@ 2019-07-02 23:20   ` Pablo Neira Ayuso
  0 siblings, 0 replies; 9+ messages in thread
From: Pablo Neira Ayuso @ 2019-07-02 23:20 UTC (permalink / raw)
  To: Ander Juaristi; +Cc: netfilter-devel

On Mon, Jul 01, 2019 at 10:16:45PM +0200, Ander Juaristi wrote:
> Signed-off-by: Ander Juaristi <a@juaristi.eus>
> ---
>  tests/py/ip/meta.t         |  9 +++++++
>  tests/py/ip/meta.t.payload | 54 ++++++++++++++++++++++++++++++++++++++
>  2 files changed, 63 insertions(+)
> 
> diff --git a/tests/py/ip/meta.t b/tests/py/ip/meta.t
> index 4db8835..b4c3ce9 100644
> --- a/tests/py/ip/meta.t
> +++ b/tests/py/ip/meta.t
> @@ -3,6 +3,15 @@
>  *ip;test-ip4;input
>  
>  icmp type echo-request;ok
> +meta time "1970-05-23 21:07:14" drop;ok
> +meta time "2019-06-21 17:00:00" drop;ok
> +meta time "2019-07-01 00:00:00" drop;ok
> +meta time "2019-07-01 00:01:00" drop;ok
> +meta time "2019-07-01 00:00:01" drop;ok
> +meta day "Saturday" drop;ok;meta day "Saturday" drop

meta day "Saturday" drop;ok

should be fine, right?

> +meta hour "17:00" drop;ok;meta hour "17:00" drop

same here:

meta hour "17:00" drop;ok

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v4 1/4] meta: Introduce new conditions 'time', 'day' and 'hour'
  2019-07-01 20:16 [PATCH v4 1/4] meta: Introduce new conditions 'time', 'day' and 'hour' Ander Juaristi
                   ` (3 preceding siblings ...)
  2019-07-02 23:18 ` [PATCH v4 1/4] meta: Introduce new conditions 'time', 'day' and 'hour' Pablo Neira Ayuso
@ 2019-07-02 23:26 ` Pablo Neira Ayuso
  2019-07-03  0:59   ` Pablo Neira Ayuso
  4 siblings, 1 reply; 9+ messages in thread
From: Pablo Neira Ayuso @ 2019-07-02 23:26 UTC (permalink / raw)
  To: Ander Juaristi; +Cc: netfilter-devel

On Mon, Jul 01, 2019 at 10:16:43PM +0200, Ander Juaristi wrote:
> diff --git a/include/nftables/libnftables.h b/include/nftables/libnftables.h
> index e39c588..87d4ff5 100644
> --- a/include/nftables/libnftables.h
> +++ b/include/nftables/libnftables.h
> @@ -52,6 +52,7 @@ enum {
>  	NFT_CTX_OUTPUT_NUMERIC_PROTO	= (1 << 7),
>  	NFT_CTX_OUTPUT_NUMERIC_PRIO     = (1 << 8),
>  	NFT_CTX_OUTPUT_NUMERIC_SYMBOL	= (1 << 9),
> +	NFT_CTX_OUTPUT_SECONDS          = (1 << 10),

I'd rename this to:

        NFT_CTX_OUTPUT_NUMERIC_TIME

>  	NFT_CTX_OUTPUT_NUMERIC_ALL	= (NFT_CTX_OUTPUT_NUMERIC_PROTO |
>  					   NFT_CTX_OUTPUT_NUMERIC_PRIO |
>  					   NFT_CTX_OUTPUT_NUMERIC_SYMBOL),

You have to update NFT_CTX_OUTPUT_NUMERIC_ALL.

> +static void day_type_print(const struct expr *expr, struct output_ctx *octx)
> +{
> +	const char *days[] = {
> +		"Sunday",
> +		"Monday",
> +		"Tuesday",
> +		"Wednesday",
> +		"Thursday",
> +		"Friday",
> +		"Saturday"

Probably print in short format?

        Sun
        Mon
        Tue
        Wed
        Thu
        Fri
        Sat

like 'date'.

> +	};
> +	uint8_t daynum = mpz_get_uint8(expr->value),

missing line break between variable definition and code.

> +		 numdays = array_size(days);
        ^^^^^^^^^

Unnecessary indentation.

> +
> +	if (daynum >= 0 && daynum < numdays)
> +		nft_print(octx, "\"%s\"", days[daynum]);
> +	else
> +		nft_print(octx, "Unknown day");
> +}
> +
> +static int parse_day_type_numeric(const char *sym)
> +{
> +	char c = *sym;
> +	return (c >= '0' && c <= '6') ?
> +		(c - '0') :
> +		-1;

Oh, I'd suggest:

        if (c >= '0' && c <= '6')
                return (c - '0');

        return -1;

> +}
> +
> +static struct error_record *day_type_parse(const struct expr *sym,
> +					   struct expr **res)
> +{
> +	const char *days[] = {
> +		"Sunday",
> +		"Monday",
> +		"Tuesday",
> +		"Wednesday",
> +		"Thursday",
> +		"Friday",
> +		"Saturday"
> +	};

Can you add a helper function to translate numeric day to literal? So
you don't need to define this again, probably:

        day_str = nft_day_str(day_num);

> +	int daynum = -1, numdays = array_size(days);
> +	int symlen = strlen(sym->identifier), daylen;
> +
> +	if (symlen < 3) {
> +		if (symlen == 1)
> +			daynum = parse_day_type_numeric(sym->identifier);
> +
> +		if (daynum >= 0)
> +			goto success;

goto for success path is discouraged.

Better if you use 'goto' to deal with error path.

> +
> +		return error(&sym->location, "Day name must be at least three characters long");
> +	}
> +
> +	for (int i = 0; i < numdays && daynum == -1; i++) {
> +		daylen = strlen(days[i]);
> +
> +		if (strncasecmp(sym->identifier,
> +				days[i],
> +				min(symlen, daylen)) == 0)
> +			daynum = i;
> +	}

Introduce a helper function to do this literal to numeric lookup?

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v4 1/4] meta: Introduce new conditions 'time', 'day' and 'hour'
  2019-07-02 23:26 ` Pablo Neira Ayuso
@ 2019-07-03  0:59   ` Pablo Neira Ayuso
  0 siblings, 0 replies; 9+ messages in thread
From: Pablo Neira Ayuso @ 2019-07-03  0:59 UTC (permalink / raw)
  To: Ander Juaristi; +Cc: netfilter-devel

On Wed, Jul 03, 2019 at 01:26:49AM +0200, Pablo Neira Ayuso wrote:
> On Mon, Jul 01, 2019 at 10:16:43PM +0200, Ander Juaristi wrote:
> > diff --git a/include/nftables/libnftables.h b/include/nftables/libnftables.h
> > index e39c588..87d4ff5 100644
> > --- a/include/nftables/libnftables.h
> > +++ b/include/nftables/libnftables.h
> > @@ -52,6 +52,7 @@ enum {
> >  	NFT_CTX_OUTPUT_NUMERIC_PROTO	= (1 << 7),
> >  	NFT_CTX_OUTPUT_NUMERIC_PRIO     = (1 << 8),
> >  	NFT_CTX_OUTPUT_NUMERIC_SYMBOL	= (1 << 9),
> > +	NFT_CTX_OUTPUT_SECONDS          = (1 << 10),
> 
> I'd rename this to:
> 
>         NFT_CTX_OUTPUT_NUMERIC_TIME

NFT_CTX_OUTPUT_SECONDS is also fine.

I let you choose.

> 
> >  	NFT_CTX_OUTPUT_NUMERIC_ALL	= (NFT_CTX_OUTPUT_NUMERIC_PROTO |
> >  					   NFT_CTX_OUTPUT_NUMERIC_PRIO |
> >  					   NFT_CTX_OUTPUT_NUMERIC_SYMBOL),
> 
> You have to update NFT_CTX_OUTPUT_NUMERIC_ALL.

But you still have to update this :-)

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2019-07-03  0:59 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-07-01 20:16 [PATCH v4 1/4] meta: Introduce new conditions 'time', 'day' and 'hour' Ander Juaristi
2019-07-01 20:16 ` [PATCH v4 2/4] meta: Parse 'time' type with timegm() Ander Juaristi
2019-07-02 23:19   ` Pablo Neira Ayuso
2019-07-01 20:16 ` [PATCH v4 3/4] tests/py: Add tests for 'time', 'day' and 'hour' Ander Juaristi
2019-07-02 23:20   ` Pablo Neira Ayuso
2019-07-01 20:16 ` [PATCH v4 4/4] nft: Update documentation Ander Juaristi
2019-07-02 23:18 ` [PATCH v4 1/4] meta: Introduce new conditions 'time', 'day' and 'hour' Pablo Neira Ayuso
2019-07-02 23:26 ` Pablo Neira Ayuso
2019-07-03  0:59   ` Pablo Neira Ayuso

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).