netfilter-devel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* xt_time 20070915
@ 2007-09-15 16:07 Jan Engelhardt
  2007-09-15 17:41 ` xt_time 20070915 (iptables) Jan Engelhardt
  2007-09-17 13:54 ` xt_time 20070915 Patrick McHardy
  0 siblings, 2 replies; 10+ messages in thread
From: Jan Engelhardt @ 2007-09-15 16:07 UTC (permalink / raw)
  To: Netfilter Developer Mailing List; +Cc: kaber


For 2.6.24. Please comment, thanks!


===


ipt_time from POM-ng augmented by:

 * xtables
 * ipv6 support
 * day-of-month support (for example "match on the 15th of each month")
 * timezone (it was there before though) -
   it does *what the user expects* (which is good).

Signed-off-by: Jan Engelhardt <jengelh@computergmbh.de>

---
 include/linux/netfilter/xt_time.h |   13 ++
 net/netfilter/Kconfig             |   14 ++
 net/netfilter/Makefile            |    1 
 net/netfilter/xt_time.c           |  208 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 236 insertions(+)

Index: linux-2.6.23/include/linux/netfilter/xt_time.h
===================================================================
--- /dev/null
+++ linux-2.6.23/include/linux/netfilter/xt_time.h
@@ -0,0 +1,13 @@
+#ifndef _XT_TIME_H
+#define _XT_TIME_H 1
+
+struct xt_time_info {
+	time_t date_start;
+	time_t date_stop;
+	u_int32_t monthdays_match;
+	u_int16_t time_start;
+	u_int16_t time_stop;
+	u_int8_t weekdays_match;
+};
+
+#endif /* _XT_TIME_H */
Index: linux-2.6.23/net/netfilter/Kconfig
===================================================================
--- linux-2.6.23.orig/net/netfilter/Kconfig
+++ linux-2.6.23/net/netfilter/Kconfig
@@ -665,6 +665,20 @@ config NETFILTER_XT_MATCH_TCPMSS
 
 	  To compile it as a module, choose M here.  If unsure, say N.
 
+config NETFILTER_XT_MATCH_TIME
+	tristate '"time" match support'
+	depends on NETFILTER_XTABLES
+	---help---
+	  This option adds a "time" match, which allows you to match based on
+	  the packet arrival time (at the machine which netfilter is running)
+	  on) or departure time/date (for locally generated packets).
+
+	  If you say Y here, try `iptables -m time --help` for
+	  more information.
+
+	  If you want to compile it as a module, say M here.
+	  If unsure, say N.
+
 config NETFILTER_XT_MATCH_U32
 	tristate '"u32" match support'
 	depends on NETFILTER_XTABLES
Index: linux-2.6.23/net/netfilter/Makefile
===================================================================
--- linux-2.6.23.orig/net/netfilter/Makefile
+++ linux-2.6.23/net/netfilter/Makefile
@@ -73,6 +73,7 @@ obj-$(CONFIG_NETFILTER_XT_MATCH_SCTP) +=
 obj-$(CONFIG_NETFILTER_XT_MATCH_STATE) += xt_state.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_STATISTIC) += xt_statistic.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_STRING) += xt_string.o
+obj-$(CONFIG_NETFILTER_XT_MATCH_TIME) += xt_time.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_TCPMSS) += xt_tcpmss.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_PHYSDEV) += xt_physdev.o
 obj-$(CONFIG_NETFILTER_XT_MATCH_U32) += xt_u32.o
Index: linux-2.6.23/net/netfilter/xt_time.c
===================================================================
--- /dev/null
+++ linux-2.6.23/net/netfilter/xt_time.c
@@ -0,0 +1,208 @@
+/*
+ *	xt_time
+ *	Copyright © Jan Engelhardt <jengelh@computergmbh.de>, 2007
+ *
+ *	based on ipt_time by Fabrice MARIE <fabrice@netfilter.org>
+ *	This is a module which is used for time matching
+ *	It is using some modified code from dietlibc (localtime() function)
+ *	that you can find at http://www.fefe.de/dietlibc/
+ *	This file is distributed under the terms of the GNU General Public
+ *	License (GPL). Copies of the GPL can be obtained from gnu.org/gpl.
+ */
+#include <linux/ktime.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/netfilter/x_tables.h>
+#include "xt_time.h"
+
+struct xtm {
+	unsigned int month;    /* (1-12) */
+	unsigned int monthday; /* (1-31) */
+	unsigned int weekday;  /* (1-7) */
+	unsigned int hour;     /* (0-23) */
+	unsigned int minute;   /* (0-59) */
+};
+
+extern struct timezone sys_tz; /* ouch */
+
+static const u_int16_t days_since_year[] = {
+	0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
+};
+
+static const u_int16_t days_since_leapyear[] = {
+	0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335,
+};
+
+/*
+ * Since time progresses forward, it is best to organize this array in reverse,
+ * to minimize lookup time.
+ */
+enum {
+	DSE_FIRST = 2039,
+};
+static const u_int16_t days_since_epoch[] = {
+	/* 2039 - 2030 */
+	25202, 24837, 24472, 24106, 23741, 23376, 23011, 22645, 22280, 21915,
+	/* 2029 - 2020 */
+	21550, 21184, 20819, 20454, 20089, 19723, 19358, 18993, 18628, 18262,
+	/* 2019 - 2010 */
+	17897, 17532, 17167, 16801, 16436, 16071, 15706, 15340, 14975, 14610,
+	/* 2009 - 2000 */
+	14245, 13879, 13514, 13149, 12784, 12418, 12053, 11688, 11323, 10957,
+	/* 1999 - 1990 */
+	10592, 10227, 9862, 9496, 9131, 8766, 8401, 8035, 7670, 7305,
+	/* 1989 - 1980 */
+	6940, 6574, 6209, 5844, 5479, 5113, 4748, 4383, 4018, 3652,
+	/* 1979 - 1970 */
+	3287, 2922, 2557, 2191, 1826, 1461, 1096, 730, 365, 0,
+};
+
+static inline bool is_leap(unsigned int y)
+{
+	return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0);
+}
+
+static void localtime(struct xtm *r, time_t time)
+{
+	unsigned int year, i, w;
+
+	time -= 60 * sys_tz.tz_minuteswest;
+
+	/* 1d=86400s. Daytime. */
+	w  = time % 86400;
+	w /= 60;
+	r->minute = w % 60;
+	r->hour   = w / 60;
+
+	/* Days since 1970-01-01 */
+	w = time / 86400;
+	r->weekday = (4 + w) % 7;
+
+	/*
+	 * Consider x=21551. Loop will abort on dse[i] <= x, which is with
+	 * dse[i] = 21550 (=> 2029-01-02).
+	 */
+	for (i = 0, year = DSE_FIRST; days_since_epoch[i] > w;
+	    ++i, --year)
+		/* just loop */;
+
+	w -= days_since_epoch[i];
+	/* r->yearday = w; */
+
+	if (is_leap(year)) {
+		for (i = ARRAY_SIZE(days_since_leapyear) - 1;
+		    i > 0 && days_since_year[i] > w; --i)
+			/* just loop */;
+	} else {
+		for (i = ARRAY_SIZE(days_since_year) - 1;
+		    i > 0 && days_since_year[i] > w; --i)
+			/* just loop */;
+	}
+
+	r->month    = i + 1;
+	r->monthday = w - days_since_year[i] + 1;
+	printk(KERN_ERR "m=%u md=%u\n", r->month, r->monthday);
+	return;
+}
+
+static bool xt_time_match(const struct sk_buff *skb,
+                          const struct net_device *in,
+                          const struct net_device *out,
+                          const struct xt_match *match, const void *matchinfo,
+                          int offset, unsigned int protoff, bool *hotdrop)
+{
+	const struct xt_time_info *info = matchinfo;
+	unsigned int packet_time;
+	struct xtm current_time;
+	s64 stamp;
+
+	if (skb->tstamp.tv64 == 0)
+		__net_timestamp((struct sk_buff *)skb);
+
+	stamp = skb->tstamp.tv64;
+	do_div(stamp, NSEC_PER_SEC);
+
+	/*
+	 * xt_time will match when _all_ of the following hold:
+	 *   - 'now' is in the global time range date_start..date_end
+	 *   - 'now' is in the monthday mask
+	 *   - 'now' is in the weekday mask
+	 *   - 'now' is in the daytime range time_start..time_end
+	 * (and by default, libxt_time will set these so as to match)
+	 */
+
+	if (stamp < info->date_start || stamp > info->date_stop)
+		return false;
+
+	localtime(&current_time, stamp);
+
+	if (!(info->monthdays_match & (1 << current_time.monthday)))
+		return false;
+	if (!(info->weekdays_match & (1 << current_time.weekday)))
+		return false;
+
+	packet_time = current_time.hour * 60 + current_time.minute;
+	if (info->time_start < info->time_stop) {
+		if (packet_time < info->time_start ||
+		    packet_time > info->time_stop)
+			return false;
+	} else {
+		if (packet_time < info->time_start &&
+		    packet_time > info->time_stop)
+			return false;
+	}
+
+	return true;
+}
+
+static bool xt_time_check(const char *tablename, const void *ip,
+                          const struct xt_match *match, void *matchinfo,
+                          unsigned int hook_mask)
+{
+	struct xt_time_info *info = matchinfo;
+
+	/* xt_time's granularity is a minute, hence 24*60 = 1day */
+	if (info->time_start > 24 * 60 || info->time_stop >= 24 * 60) {
+		printk(KERN_WARNING "ipt_time: invalid argument - start or stop time greater than 23:59h\n");
+		return false;
+	}
+
+	return true;
+}
+
+static struct xt_match xt_time_reg[] __read_mostly = {
+	{
+		.name       = "time",
+		.family     = AF_INET,
+		.match      = xt_time_match,
+		.matchsize  = sizeof(struct xt_time_info),
+		.checkentry = xt_time_check,
+		.me         = THIS_MODULE,
+	},
+	{
+		.name       = "time",
+		.family     = AF_INET6,
+		.match      = xt_time_match,
+		.matchsize  = sizeof(struct xt_time_info),
+		.checkentry = xt_time_check,
+		.me         = THIS_MODULE,
+	},
+};
+
+static int __init xt_time_init(void)
+{
+	return xt_register_matches(xt_time_reg, ARRAY_SIZE(xt_time_reg));
+}
+
+static void __exit xt_time_exit(void)
+{
+	xt_unregister_matches(xt_time_reg, ARRAY_SIZE(xt_time_reg));
+}
+
+module_init(xt_time_init);
+module_exit(xt_time_exit);
+MODULE_AUTHOR("Jan Engelhardt <jengelh@computergmbh.de>");
+MODULE_DESCRIPTION("netfilter time match");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("ipt_time");
+MODULE_ALIAS("ip6t_time");

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

* xt_time 20070915 (iptables)
  2007-09-15 16:07 xt_time 20070915 Jan Engelhardt
@ 2007-09-15 17:41 ` Jan Engelhardt
  2007-09-17 13:54 ` xt_time 20070915 Patrick McHardy
  1 sibling, 0 replies; 10+ messages in thread
From: Jan Engelhardt @ 2007-09-15 17:41 UTC (permalink / raw)
  To: Netfilter Developer Mailing List; +Cc: kaber


libipt_time from POM-ng, augmented by:

 * day-of-month support (for example "match on the 15th of each month")
 * inversion support for --weekdays and --monthdays
 * a wonderful manpage

Signed-off-by: Jan Engelhardt <jengelh@computergmbh.de>

---
 extensions/Makefile               |    2 
 extensions/libipt_time.man        |   57 ++++
 extensions/libxt_time.c           |  473 ++++++++++++++++++++++++++++++++++++++
 include/linux/netfilter/xt_time.h |   13 +
 4 files changed, 544 insertions(+), 1 deletion(-)

Index: iptables/extensions/Makefile
===================================================================
--- iptables.orig/extensions/Makefile
+++ iptables/extensions/Makefile
@@ -7,7 +7,7 @@
 #
 PF_EXT_SLIB:=ah addrtype conntrack ecn icmp iprange owner policy realm recent tos ttl unclean CLUSTERIP DNAT ECN LOG MASQUERADE MIRROR NETMAP REDIRECT REJECT SAME SNAT TOS TTL ULOG
 PF6_EXT_SLIB:=ah dst eui64 frag hbh hl icmp6 ipv6header mh owner policy rt HL LOG REJECT
-PFX_EXT_SLIB:=connbytes connmark connlimit comment dccp dscp esp hashlimit helper length limit mac mark multiport physdev pkttype quota sctp state statistic standard string tcp tcpmss u32 udp CLASSIFY CONNMARK DSCP MARK NFLOG NFQUEUE NOTRACK TCPMSS TRACE
+PFX_EXT_SLIB:=connbytes connmark connlimit comment dccp dscp esp hashlimit helper length limit mac mark multiport physdev pkttype quota sctp state statistic standard string tcp tcpmss time u32 udp CLASSIFY CONNMARK DSCP MARK NFLOG NFQUEUE NOTRACK TCPMSS TRACE
 
 PF_EXT_SELINUX_SLIB:=
 PF6_EXT_SELINUX_SLIB:=
Index: iptables/extensions/libipt_time.man
===================================================================
--- /dev/null
+++ iptables/extensions/libipt_time.man
@@ -0,0 +1,57 @@
+This matches if the packet arrival time/date is within a given range. All
+options are optional, but are ANDed when specified.
+.TP
+\fB--datestart\fR \fIYYYY\fR[\fB-\fR\fIMM\fR[\fBT\fR\fIDD\fR[\fB-\fR\fIhh\fR[\fB:\fR\fImm\fR[\fB:\fR\fIss\fR]]]]]
+.TP
+\fB--datestop\fR \fIYYYY\fR[\fB-\fR\fIMM\fR[\fBT\fR\fIDD\fR[\fB-\fR\fIhh\fR[\fB:\fR\fImm\fR[\fB:\fR\fIss\fR]]]]]
+Only match during the given time, which must be in ISO 8601 notation. Possible
+values are \fB1970\fR to \fB2038\fR for the year \fIYYYY\fR, \fB1\fR to
+\fB12\fR for the month \fIMM\fR, \fB1\fR to \fB31\fR for the day \fIDD\fR
+(subject to limits of the selected month), \fB0\fR to \fB23\fR for the hour
+\fIHH\fR, and \fB0\fR to \fB59\fR for the minute \fIMM\fR and seconds \fISS\fR.
+The minimum possible date is 1970-01-01, the maximum date is 2038-01-19
+04:17:07.
+.TP
+\fB--timestart\fR \fIHH\fR:\fIMM\fR
+.TP
+\fB--timestop\fR \fIHH\fR:\fIMM\fR
+Only match during the given daytime. Possible values are \fB0\fR to \fB23\fR
+for \fIHH\fR and \fB0\fR to \fB59\fR for \fIMM\fR. Leading zeroes are allowed
+and ignored, i.e. the numbers are always interpreted in base-10.
+.TP
+[\fB!\fR] \fB--monthday\fR \fIday\fR[\fB,\fR\fIday\fR...]
+Only match on the given days of the month. Possible values are \fB1\fR
+to \fB31\fR. Note that specifying \fB31\fR will of course not match
+on months which do not have a 31st day; the same goes for 28- or 29-day
+February.
+.TP
+[\fB!\fR] \fB--weekdays\fR \fIday\fR[\fB,\fR\fIday\fR...]
+Only match on the given weekdays. Possible values are \fBMon\fR, \fBTue\fR,
+\fBWed\fR, \fBThu\fR, \fBFri\fR, \fBSat\fR, \fBSun\fR, or values from \fB1\fR
+to \fB7\fR, respectively. You may also use two-character variants (\fBMo\fR,
+\fBTu\fR, etc.).
+.PP
+EXAMPLES. To match on weekends, use:
+.IP
+-m time --weekdays Sa,Su
+.PP
+Or to match (once) on a national holiday block:
+.IP
+-m time --datestart 2007-12-24 --datestop 2007-12-27
+.PP
+Since the stop time is actually inclusive, you would need this to not match
+the first second of the new day:
+.IP
+-m time --datestart 2007-01-01T17:00 --datestop 2007-01-01T23:59:59
+.PP
+During lunch hour:
+.IP
+-m time --timestart 12:30 --timestop 13:30
+.PP
+The fourth Friday in the month:
+.IP
+-m time --weekdays Fr --monthdays 22,23,24,25,26,27,28
+.PP
+(Note that this exploits a certain mathematical property. It is not possible to
+say "fourth Thursday OR fourth Friday" in one rule. It is possible with
+multiple rules, though.)
Index: iptables/extensions/libxt_time.c
===================================================================
--- /dev/null
+++ iptables/extensions/libxt_time.c
@@ -0,0 +1,473 @@
+/*
+ *	libxt_time - iptables part for xt_time
+ *	Copyright © Jan Engelhardt <jengelh@computergmbh.de>, 2007
+ *
+ *	libxt_time.c is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 or 3 of the License.
+ *
+ *	Based on libipt_time.c.
+ */
+#include <sys/types.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <time.h>
+#include <linux/netfilter/xt_time.h>
+#include <xtables.h>
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x))
+
+enum {
+	F_DATE_START = 1 << 0,
+	F_DATE_STOP  = 1 << 1,
+	F_TIME_START = 1 << 2,
+	F_TIME_STOP  = 1 << 3,
+	F_MONTHDAYS  = 1 << 4,
+	F_WEEKDAYS   = 1 << 5,
+};
+
+static const char *const week_days[] = {
+	NULL, "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun",
+};
+
+static const struct option time_opts[] = {
+	{"datestart", 1, NULL, 'D'},
+	{"datestop",  1, NULL, 'E'},
+	{"timestart", 1, NULL, 'X'},
+	{"timestop",  1, NULL, 'Y'},
+	{"weekdays",  1, NULL, 'w'},
+	{"monthdays", 1, NULL, 'm'},
+	{NULL},
+};
+
+static void time_help(void)
+{
+	printf(
+"TIME v%s options:\n"
+"  --datestart value     YYYY[-MM[-DD[Thh[:mm[:ss]]]]]\n"
+"                        If any of month, day, hour, minute or second is\n"
+"                        not specified, then defaults to their smallest value.\n"
+"                        1970 <= YYYY <= 2038    0 <= hh <= 23\n"
+"                           1 <= MM   <= 12      0 <= mm <= 59\n"
+"                           1 <= DD   <= 31      0 <= ss <= 59\n"
+"  --datestop value      YYYY[-MM[-DD[Thh[:mm[:ss]]]]]\n"
+"                        If the whole option is ommited, default to never stop.\n"
+"                        Unspecified fields default to their largest value.\n"
+"  --timestart value	 HH:MM (default 00:00)\n"
+"  --timestop  value     HH:MM (default 23:59)\n"
+"  --monthdays value     A list of days to apply, separated by comma\n"
+"                        Possible days: 1..31\n"
+"                        Defaults to all days of the months.\n"
+"  --weekdays  value     A list of days to apply, separated by comma\n"
+"                        Possible days: Mon,Tue,Wed,Thu,Fri,Sat,Sun or 1..7\n"
+"                        Defaults to all weekdays.\n",
+IPTABLES_VERSION);
+}
+
+static void time_init(struct xt_entry_match *m)
+{
+	struct xt_time_info *info = (void *)m->data;
+
+	/* By default, we match on every day, every daytime */
+	info->monthdays_match = 0xFFFFFFFE;
+	info->weekdays_match  = 0xFE;
+	info->time_start      = 0;
+	info->time_stop       = 24 * 60 - 1; /* 23:59 */
+
+	/* ...and have no date-begin or date-end boundary */
+	info->date_start = 0;
+	info->date_stop  = LONG_MAX;
+}
+
+static time_t time_parse_date(const char *s, bool end)
+{
+	unsigned int month = 1, day = 1, hour = 0, minute = 0, second = 0;
+	unsigned int year  = end ? 2038 : 1970;
+	const char *os = s;
+	struct tm tm;
+	time_t ret;
+	char *e;
+
+	year = strtoul(s, &e, 10);
+	if ((*e != '-' && *e != '\0') || year < 1970 || year > 2038)
+		goto out;
+	if (*e == '\0')
+		goto eval;
+
+	s = e + 1;
+	month = strtoul(s, &e, 10);
+	if ((*e != '-' && *e != '\0') || month > 12)
+		goto out;
+	if (*e == '\0')
+		goto eval;
+
+	s = e + 1;
+	day = strtoul(s, &e, 10);
+	if ((*e != 'T' && *e != '\0') || day > 31)
+		goto out;
+	if (*e == '\0')
+		goto eval;
+
+	s = e + 1;
+	hour = strtoul(s, &e, 10);
+	if ((*e != ':' && *e != '\0') || hour > 23)
+		goto out;
+	if (*e == '\0')
+		goto eval;
+
+	s = e + 1;
+	minute = strtoul(s, &e, 10);
+	if ((*e != ':' && *e != '\0') || minute > 59)
+		goto out;
+	if (*e == '\0')
+		goto eval;
+
+	s = e + 1;
+	second = strtoul(s, &e, 10);
+	if (*e != '\0' || second > 59)
+		goto out;
+
+ eval:
+	tm.tm_year = year - 1900;
+	tm.tm_mon  = month - 1;
+	tm.tm_mday = day;
+	tm.tm_hour = hour;
+	tm.tm_min  = minute;
+	tm.tm_sec  = second;
+	ret = mktime(&tm);
+	if (ret >= 0)
+		return ret;
+	perror("mktime");
+	exit_error(OTHER_PROBLEM, "mktime returned an error");
+
+ out:
+	exit_error(PARAMETER_PROBLEM, "Invalid date \"%s\" specified. Should "
+	           "be YYYY[-MM[-DD[Thh[:mm[:ss]]]]]", os);
+	return -1;
+}
+
+static unsigned int time_parse_minutes(const char *s)
+{
+	unsigned int hour, minute;
+	char *e;
+
+	hour = strtoul(s, &e, 10);
+	if (*e != ':' || hour > 23)
+		goto out;
+	s = e + 1;
+	minute = strtoul(s, &e, 10);
+	if (*e != '\0' || minute > 59)
+		goto out;
+
+	return hour * 60 + minute;
+
+ out:
+	exit_error(PARAMETER_PROBLEM, "invalid time \"%s\" specified, "
+	           "should be HH:MM format, with 0 <= HH <= 23 and "
+	           "0 <= MM <= 59", s);
+	return -1;
+}
+
+static const char *my_strseg(char *buf, unsigned int buflen,
+    const char **arg, char delim)
+{
+	const char *sep;
+
+	if (*arg == NULL || **arg == '\0')
+		return NULL;
+	sep = strchr(*arg, delim);
+	if (sep == NULL) {
+		snprintf(buf, buflen, "%s", *arg);
+		*arg = NULL;
+		return buf;
+	}
+	snprintf(buf, buflen, "%.*s", (unsigned int)(sep - *arg), *arg);
+	*arg = sep + 1;
+	return buf;
+}
+
+static uint32_t time_parse_monthdays(const char *arg)
+{
+	char day[3], *err = NULL;
+	uint32_t ret = 0;
+	unsigned int i;
+
+	while (my_strseg(day, sizeof(day), &arg, ',') != NULL) {
+		i = strtoul(day, &err, 0);
+		if ((*err != ',' && *err != '\0') || i > 31)
+			exit_error(PARAMETER_PROBLEM,
+			           "%s is not a valid day for --monthdays", day);
+		ret |= 1 << i;
+	}
+
+	return ret;
+}
+
+static unsigned int time_parse_weekdays(const char *arg)
+{
+	char day[4], *err = NULL;
+	unsigned int i, ret = 0;
+	bool valid;
+
+	while (my_strseg(day, sizeof(day), &arg, ',') != NULL) {
+		i = strtoul(day, &err, 0);
+		if (*err == '\0') {
+			if (i == 0)
+				exit_error(PARAMETER_PROBLEM,
+				           "No, the week does not begin with Sunday.");
+			ret |= 1 << i;
+			continue;
+		}
+
+		valid = false;
+		for (i = 1; i < ARRAY_SIZE(week_days); ++i)
+			if (strncmp(day, week_days[i], 2) == 0) {
+				ret |= 1 << i;
+				valid = true;
+			}
+
+		if (!valid)
+			exit_error(PARAMETER_PROBLEM,
+			           "%s is not a valid day specifier", day);
+	}
+
+	return ret;
+}
+
+static int time_parse(int c, char **argv, int invert, unsigned int *flags,
+                      const void *entry, struct xt_entry_match **match)
+{
+	struct xt_time_info *info = (void *)(*match)->data;
+
+	switch (c) {
+	case 'D': /* --datestart */
+		if (*flags & F_DATE_START)
+			exit_error(PARAMETER_PROBLEM,
+			           "Cannot specify --datestart twice");
+		if (invert)
+			exit_error(PARAMETER_PROBLEM,
+			           "Unexpected \"!\" with --datestart");
+		info->date_start = time_parse_date(optarg, false);
+		*flags |= F_DATE_START;
+		break;
+	case 'E': /* --datestop */
+		if (*flags & F_DATE_STOP)
+			exit_error(PARAMETER_PROBLEM,
+			           "Cannot specify --datestop more than once");
+		if (invert)
+			exit_error(PARAMETER_PROBLEM,
+			           "unexpected \"!\" with --datestop");
+		info->date_stop = time_parse_date(optarg, true);
+		*flags |= F_DATE_STOP;
+		break;
+	case 'X': /* --timestart */
+		if (*flags & F_TIME_START)
+			exit_error(PARAMETER_PROBLEM,
+			           "Cannot specify --timestart more than once");
+		if (invert)
+			exit_error(PARAMETER_PROBLEM,
+			           "Unexpected \"!\" with --timestart");
+		info->time_start = time_parse_minutes(optarg);
+		*flags |= F_TIME_START;
+		break;
+	case 'Y': /* --timestop */
+		if (*flags & F_TIME_STOP)
+			exit_error(PARAMETER_PROBLEM,
+			           "Cannot specify --timestop more than once");
+		if (invert)
+			exit_error(PARAMETER_PROBLEM,
+			           "Unexpected \"!\" with --timestop");
+		info->time_stop = time_parse_minutes(optarg);
+		*flags |= F_TIME_STOP;
+		break;
+	case 'm': /* --monthdays */
+		if (*flags & F_MONTHDAYS)
+			exit_error(PARAMETER_PROBLEM,
+			           "Cannot specify --monthdays more than once");
+		info->monthdays_match = time_parse_monthdays(optarg);
+		if (invert)
+			info->monthdays_match = ~info->monthdays_match;
+		*flags |= F_MONTHDAYS;
+		break;
+	case 'w': /* --weekdays */
+		if (*flags & F_WEEKDAYS)
+			exit_error(PARAMETER_PROBLEM,
+			           "Cannot specify --weekdays more than once");
+		info->weekdays_match = time_parse_weekdays(optarg);
+		if (invert)
+			info->weekdays_match = ~info->weekdays_match;
+		*flags |= F_WEEKDAYS;
+		break;
+	default:
+		return 0;
+	}
+	return 1;
+}
+
+static void time_check(unsigned int flags)
+{
+}
+
+static void time_print_date(time_t date, const char *command)
+{
+	struct tm *t;
+
+	/* If it is the default value, do not print it. */
+	if (date == 0 || date == LONG_MAX)
+		return;
+
+	t = localtime(&date);
+	if (command != NULL)
+		/*
+		 * Need a contiguous string (no whitespaces), hence using
+		 * the ISO 8601 variant with "T" in it.
+		 */
+		printf("%s %04u-%02u-%02uT%02u:%02u:%02u ",
+		       command, t->tm_year + 1900, t->tm_mon + 1,
+		       t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
+	else
+		printf("%04u-%02u-%02u %02u:%02u:%02u ",
+		       t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
+		       t->tm_hour, t->tm_min, t->tm_sec);
+}
+
+static void time_print_monthdays(uint32_t mask)
+{
+	unsigned int i, nbdays = 0;
+
+	for (i = 1; i <= 31; ++i)
+		if (mask & (1 << i)) {
+			if (nbdays > 0)
+				printf(",%u", i);
+			else
+				printf("%u", i);
+			++nbdays;
+		}
+	printf(" ");
+}
+
+static void time_print_weekdays(unsigned int mask)
+{
+	unsigned int i, nbdays = 0;
+
+	for (i = 1; i <= 7; ++i)
+		if (mask & (1 << i)) {
+			if (nbdays > 0)
+				printf(",%s", week_days[i]);
+			else
+				printf("%s", week_days[i]);
+			++nbdays;
+		}
+	printf(" of month ");
+}
+
+static inline void divide_time(unsigned int fulltime, unsigned int *hours,
+    unsigned int *minutes)
+{
+	*hours   = fulltime / 60;
+	*minutes = fulltime % 60;
+}
+
+static void time_print(const void *ip, const struct xt_entry_match *match,
+                       int numeric)
+{
+	struct xt_time_info *time = (void *)match->data;
+	unsigned int hour_start, hour_stop, minute_start, minute_stop;
+
+	divide_time(time->time_start, &hour_start, &minute_start);
+	divide_time(time->time_stop, &hour_stop, &minute_stop);
+	printf("TIME ");
+
+	if (time->time_start != 0)
+		printf("from %02u:%02u ", hour_start, minute_start);
+	if (time->time_stop != 24 * 60 - 1)
+		printf("to %02u:%02u ", hour_stop, minute_stop);
+
+	printf("on ");
+	if ((time->weekdays_match & 0xFE) == 0xFE)
+		printf("all weekdays ");
+	else
+		time_print_weekdays(time->weekdays_match);
+
+	printf("on ");
+	if ((time->monthdays_match & 0xFFFFFFFE) == 0xFFFFFFFE)
+		printf("all days of the month ");
+	else
+		time_print_monthdays(time->monthdays_match);
+
+	if (time->date_start != 0) {
+		printf("starting from ");
+		time_print_date(time->date_start, NULL);
+	}
+	if (time->date_stop != LONG_MAX) {
+		printf("until date ");
+		time_print_date(time->date_stop, NULL);
+	}
+}
+
+static void time_save(const void *ip, const struct xt_entry_match *match)
+{
+	const struct xt_time_info *time = (const void *)match->data;
+	unsigned int hour_start, hour_stop, minute_start, minute_stop;
+
+	divide_time(time->time_start, &hour_start, &minute_start);
+	divide_time(time->time_stop, &hour_stop, &minute_stop);
+
+	if (time->time_start != 0)
+		printf("--timestart %02u:%02u ",
+		       hour_start, minute_start);
+	if (time->time_stop != 24 * 60 - 1)
+		printf("--timestop %02u:%02u ",
+		       hour_stop, minute_stop);
+	if ((time->monthdays_match & 0xFFFFFFFE) != 0xFFFFFFFE) {
+		printf("--monthdays ");
+		time_print_monthdays(time->monthdays_match);
+		printf(" ");
+	}
+	if ((time->weekdays_match & 0xFE) != 0xFE) {
+		printf("--weekdays ");
+		time_print_weekdays(time->weekdays_match);
+		printf(" ");
+	}
+	time_print_date(time->date_start, "--datestart");
+	time_print_date(time->date_stop, "--datestop");
+}
+
+static struct xtables_match time_reg = {
+	.name          = "time",
+	.family        = AF_INET,
+	.version       = IPTABLES_VERSION,
+	.size          = XT_ALIGN(sizeof(struct xt_time_info)),
+	.userspacesize = XT_ALIGN(sizeof(struct xt_time_info)),
+	.help          = time_help,
+	.init          = time_init,
+	.parse         = time_parse,
+	.final_check   = time_check,
+	.print         = time_print,
+	.save          = time_save,
+	.extra_opts    = time_opts,
+};
+
+static struct xtables_match time6_reg = {
+	.name          = "time",
+	.family        = AF_INET6,
+	.version       = IPTABLES_VERSION,
+	.size          = XT_ALIGN(sizeof(struct xt_time_info)),
+	.userspacesize = XT_ALIGN(sizeof(struct xt_time_info)),
+	.help          = time_help,
+	.init          = time_init,
+	.parse         = time_parse,
+	.final_check   = time_check,
+	.print         = time_print,
+	.save          = time_save,
+	.extra_opts    = time_opts,
+};
+
+void _init(void)
+{
+	xtables_register_match(&time_reg);
+	xtables_register_match(&time6_reg);
+}
Index: iptables/include/linux/netfilter/xt_time.h
===================================================================
--- /dev/null
+++ iptables/include/linux/netfilter/xt_time.h
@@ -0,0 +1,13 @@
+#ifndef _XT_TIME_H
+#define _XT_TIME_H 1
+
+struct xt_time_info {
+	time_t date_start;
+	time_t date_stop;
+	u_int32_t monthdays_match;
+	u_int16_t time_start;
+	u_int16_t time_stop;
+	u_int8_t weekdays_match;
+};
+
+#endif /* _XT_TIME_H */

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

* Re: xt_time 20070915
  2007-09-15 16:07 xt_time 20070915 Jan Engelhardt
  2007-09-15 17:41 ` xt_time 20070915 (iptables) Jan Engelhardt
@ 2007-09-17 13:54 ` Patrick McHardy
  2007-09-17 15:01   ` Jan Engelhardt
  1 sibling, 1 reply; 10+ messages in thread
From: Patrick McHardy @ 2007-09-17 13:54 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List

Jan Engelhardt wrote:
> ipt_time from POM-ng augmented by:
> 
>  * xtables
>  * ipv6 support


I bet this was tricky :)

>  * day-of-month support (for example "match on the 15th of each month")
>  * timezone (it was there before though) -
>    it does *what the user expects* (which is good).


Indeed. We decided to merge it despite the so-far existing timezone
limitation, but this is even better.

> 
> Signed-off-by: Jan Engelhardt <jengelh@computergmbh.de>
> 
> ---
> +++ linux-2.6.23/include/linux/netfilter/xt_time.h
> @@ -0,0 +1,13 @@
> +#ifndef _XT_TIME_H
> +#define _XT_TIME_H 1
> +
> +struct xt_time_info {
> +	time_t date_start;
> +	time_t date_stop;


This is an arch-dependant type and must not be used within the
iptables ABI.

> +	u_int32_t monthdays_match;
> +	u_int16_t time_start;
> +	u_int16_t time_stop;
> +	u_int8_t weekdays_match;


No inversion?

> +++ linux-2.6.23/net/netfilter/xt_time.c
> @@ -0,0 +1,208 @@
> +/*
> + *	xt_time
> + *	Copyright © Jan Engelhardt <jengelh@computergmbh.de>, 2007
> + *
> + *	based on ipt_time by Fabrice MARIE <fabrice@netfilter.org>
> + *	This is a module which is used for time matching
> + *	It is using some modified code from dietlibc (localtime() function)
> + *	that you can find at http://www.fefe.de/dietlibc/
> + *	This file is distributed under the terms of the GNU General Public
> + *	License (GPL). Copies of the GPL can be obtained from gnu.org/gpl.
> + */
> +#include <linux/ktime.h>
> +#include <linux/module.h>
> +#include <linux/skbuff.h>
> +#include <linux/netfilter/x_tables.h>
> +#include "xt_time.h"


This should be <include/netfilter/xt_time.h> I suppose?

> +
> +struct xtm {
> +	unsigned int month;    /* (1-12) */
> +	unsigned int monthday; /* (1-31) */
> +	unsigned int weekday;  /* (1-7) */
> +	unsigned int hour;     /* (0-23) */
> +	unsigned int minute;   /* (0-59) */
> +};
> +
> +extern struct timezone sys_tz; /* ouch */
> +
> +static const u_int16_t days_since_year[] = {
> +	0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
> +};
> +
> +static const u_int16_t days_since_leapyear[] = {
> +	0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335,
> +};
> +
> +/*
> + * Since time progresses forward, it is best to organize this array in reverse,
> + * to minimize lookup time.
> + */
> +enum {
> +	DSE_FIRST = 2039,
> +};
> +static const u_int16_t days_since_epoch[] = {
> +	/* 2039 - 2030 */
> +	25202, 24837, 24472, 24106, 23741, 23376, 23011, 22645, 22280, 21915,
> +	/* 2029 - 2020 */
> +	21550, 21184, 20819, 20454, 20089, 19723, 19358, 18993, 18628, 18262,
> +	/* 2019 - 2010 */
> +	17897, 17532, 17167, 16801, 16436, 16071, 15706, 15340, 14975, 14610,
> +	/* 2009 - 2000 */
> +	14245, 13879, 13514, 13149, 12784, 12418, 12053, 11688, 11323, 10957,
> +	/* 1999 - 1990 */
> +	10592, 10227, 9862, 9496, 9131, 8766, 8401, 8035, 7670, 7305,
> +	/* 1989 - 1980 */
> +	6940, 6574, 6209, 5844, 5479, 5113, 4748, 4383, 4018, 3652,
> +	/* 1979 - 1970 */
> +	3287, 2922, 2557, 2191, 1826, 1461, 1096, 730, 365, 0,
> +};
> +
> +static inline bool is_leap(unsigned int y)
> +{
> +	return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0);
> +}
> +
> +static void localtime(struct xtm *r, time_t time)
> +{
> +	unsigned int year, i, w;
> +
> +	time -= 60 * sys_tz.tz_minuteswest;
> +
> +	/* 1d=86400s. Daytime. */
> +	w  = time % 86400;
> +	w /= 60;
> +	r->minute = w % 60;
> +	r->hour   = w / 60;
> +
> +	/* Days since 1970-01-01 */
> +	w = time / 86400;
> +	r->weekday = (4 + w) % 7;
> +
> +	/*
> +	 * Consider x=21551. Loop will abort on dse[i] <= x, which is with
> +	 * dse[i] = 21550 (=> 2029-01-02).
> +	 */
> +	for (i = 0, year = DSE_FIRST; days_since_epoch[i] > w;
> +	    ++i, --year)
> +		/* just loop */;
> +
> +	w -= days_since_epoch[i];
> +	/* r->yearday = w; */
> +
> +	if (is_leap(year)) {
> +		for (i = ARRAY_SIZE(days_since_leapyear) - 1;
> +		    i > 0 && days_since_year[i] > w; --i)
> +			/* just loop */;
> +	} else {
> +		for (i = ARRAY_SIZE(days_since_year) - 1;
> +		    i > 0 && days_since_year[i] > w; --i)
> +			/* just loop */;
> +	}
> +
> +	r->month    = i + 1;
> +	r->monthday = w - days_since_year[i] + 1;
> +	printk(KERN_ERR "m=%u md=%u\n", r->month, r->monthday);
> +	return;
> +}


Some explanation what the above is doing would be appreciated.

> +static bool xt_time_match(const struct sk_buff *skb,
> +                          const struct net_device *in,
> +                          const struct net_device *out,
> +                          const struct xt_match *match, const void *matchinfo,
> +                          int offset, unsigned int protoff, bool *hotdrop)
> +{
> +	const struct xt_time_info *info = matchinfo;
> +	unsigned int packet_time;
> +	struct xtm current_time;
> +	s64 stamp;
> +
> +	if (skb->tstamp.tv64 == 0)
> +		__net_timestamp((struct sk_buff *)skb);
> +
> +	stamp = skb->tstamp.tv64;
> +	do_div(stamp, NSEC_PER_SEC);


Would get_seconds() work?

> +
> +	/*
> +	 * xt_time will match when _all_ of the following hold:
> +	 *   - 'now' is in the global time range date_start..date_end
> +	 *   - 'now' is in the monthday mask
> +	 *   - 'now' is in the weekday mask
> +	 *   - 'now' is in the daytime range time_start..time_end
> +	 * (and by default, libxt_time will set these so as to match)
> +	 */
> +
> +	if (stamp < info->date_start || stamp > info->date_stop)
> +		return false;
> +
> +	localtime(&current_time, stamp);
> +
> +	if (!(info->monthdays_match & (1 << current_time.monthday)))
> +		return false;
> +	if (!(info->weekdays_match & (1 << current_time.weekday)))
> +		return false;
> +
> +	packet_time = current_time.hour * 60 + current_time.minute;
> +	if (info->time_start < info->time_stop) {
> +		if (packet_time < info->time_start ||
> +		    packet_time > info->time_stop)
> +			return false;
> +	} else {
> +		if (packet_time < info->time_start &&
> +		    packet_time > info->time_stop)
> +			return false;
> +	}
> +
> +	return true;
> +}
> +
> +static bool xt_time_check(const char *tablename, const void *ip,
> +                          const struct xt_match *match, void *matchinfo,
> +                          unsigned int hook_mask)
> +{
> +	struct xt_time_info *info = matchinfo;
> +
> +	/* xt_time's granularity is a minute, hence 24*60 = 1day */
> +	if (info->time_start > 24 * 60 || info->time_stop >= 24 * 60) {
> +		printk(KERN_WARNING "ipt_time: invalid argument - start or stop time greater than 23:59h\n");
> +		return false;
> +	}


Is there a reason for minute granularity? The data types look large
enough for at least second granularity (more is probably useless).

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

* Re: xt_time 20070915
  2007-09-17 13:54 ` xt_time 20070915 Patrick McHardy
@ 2007-09-17 15:01   ` Jan Engelhardt
  2007-09-17 15:13     ` Jan Engelhardt
  2007-09-18  9:53     ` Patrick McHardy
  0 siblings, 2 replies; 10+ messages in thread
From: Jan Engelhardt @ 2007-09-17 15:01 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: Netfilter Developer Mailing List


On Sep 17 2007 15:54, Patrick McHardy wrote:
>Jan Engelhardt wrote:
>> ipt_time from POM-ng augmented by:
>> 
>>  * xtables
>>  * ipv6 support
>
>I bet this was tricky :)

Yes, indeed. It's too easy to forget .family in the iptables part...

>> +++ linux-2.6.23/include/linux/netfilter/xt_time.h
>> @@ -0,0 +1,13 @@
>> +#ifndef _XT_TIME_H
>> +#define _XT_TIME_H 1
>> +
>> +struct xt_time_info {
>> +	time_t date_start;
>> +	time_t date_stop;
>
>This is an arch-dependant type and must not be used within the
>iptables ABI.

So what would you suggest? time_t will only work for 30 more years,
then we'll have to change it. So what is your pick for ABI-complete
time_t, uint64 or uint32?

>> +	u_int32_t monthdays_match;
>> +	u_int16_t time_start;
>> +	u_int16_t time_stop;
>> +	u_int8_t weekdays_match;
>
>No inversion?

Inversion is a funny thing with xt_time because (almost) all possible
times are _finite_.

Consider xt_connlimit and --connlimit x. The set of possible values
for x is infinite (assuming we were not limited by 32/64-bit CPUs).

Along comes xt_time. The set of possible values for --weekdays
is finite, there are exactly seven days for any given week. Since
this is the case, the negation of any set/bitmap of weekdays can
again be represented as a bitmap. So the inversion is done in
libxt_time.c (iptables).  !(Fri..Sun) == (Mon..Thu)

The same applies to time_start. Got a noon match that matches
from 10:00 to 15:00? Its inversion is (simply put) 15:00 to
10:00.

>> +#include "xt_time.h"
>
>This should be <include/netfilter/xt_time.h> I suppose?

Indeed.

>> +static void localtime(struct xtm *r, time_t time)
>> +{
>> +	unsigned int year, i, w;
>> +
>> +	time -= 60 * sys_tz.tz_minuteswest;
>> +
>> +	/* 1d=86400s. Daytime. */
>> +	w  = time % 86400;
>> +	w /= 60;
>> +	r->minute = w % 60;
>> +	r->hour   = w / 60;
>> +
>> +	/* Days since 1970-01-01 */
>> +	w = time / 86400;
>> +	r->weekday = (4 + w) % 7;
>> +
>> +	/*
>> +	 * Consider x=21551. Loop will abort on dse[i] <= x, which is with
>> +	 * dse[i] = 21550 (=> 2029-01-02).
>> +	 */
>> +	for (i = 0, year = DSE_FIRST; days_since_epoch[i] > w;
>> +	    ++i, --year)
>> +		/* just loop */;
>> +
>> +	w -= days_since_epoch[i];
>> +	/* r->yearday = w; */
>> +
>> +	if (is_leap(year)) {
>> +		for (i = ARRAY_SIZE(days_since_leapyear) - 1;
>> +		    i > 0 && days_since_year[i] > w; --i)
>> +			/* just loop */;
>> +	} else {
>> +		for (i = ARRAY_SIZE(days_since_year) - 1;
>> +		    i > 0 && days_since_year[i] > w; --i)
>> +			/* just loop */;
>> +	}
>> +
>> +	r->month    = i + 1;
>> +	r->monthday = w - days_since_year[i] + 1;
>> +	printk(KERN_ERR "m=%u md=%u\n", r->month, r->monthday);
>> +	return;
>> +}
>
>Some explanation what the above is doing would be appreciated.

/*
 * Each network packet has a (nano)seconds-since-the-epoch (SSTE) timestamp.
 * Since we match against days and daytime, the SSTE value needs to be
 * computed back into human-readable dates.
 */
static void localtime(struct xtm *r, time_t time)
{
	unsigned int year, i, w;

	time -= 60 * sys_tz.tz_minuteswest;

	/* Each day has 86400s, so finding the hour/minute is actually easy. */
	w  = time % 86400;
	w /= 60;
	r->minute = w % 60;
	r->hour   = w / 60;

	/*
	 * Here comes the rest (weekday, monthday). First, divide the SSTE
	 * by seconds-per-day to get the number of _days_ since the epoch.
	 */
	w = time / 86400;

	/* 1970-01-01 (w=0) was a Thursday (4). */
	r->weekday = (4 + w) % 7;

	/*
	 * In each year, a certain number of days-since-the-epoch have passed.
	 * Find the year that is closest to said days.
	 *
	 * Consider, for example, w=21612 (2029-03-04). Loop will abort on
	 * dse[i] <= w, which happens when dse[i] == 21550. This implies
	 * year == 2009. w will then be 62.
	 */
	for (i = 0, year = DSE_FIRST; days_since_epoch[i] > w;
	    ++i, --year)
		/* just loop */;

	w -= days_since_epoch[i];

	/*
	 * By now we have the current year, and the day of the year.
	 * r->yearday = w;
	 *
	 * On to finding the month (like above). In each month, a certain
	 * number of days-since-New Year have passed, and find the closest
	 * one.
	 *
	 * Consider w=62 (in a non-leap year). Loop will abort on
	 * dsy[i] < w, which happens when dsy[i] == 31+28 (i == 2).
	 * Concludes i == 2, i.e. 3rd month => March.
	 *
	 * (A different approach to use would be to subtract a monthlength
	 * from w repeatedly while counting.)
	 */
	if (is_leap(year)) {
		for (i = ARRAY_SIZE(days_since_leapyear) - 1;
		    i > 0 && days_since_year[i] > w; --i)
			/* just loop */;
	} else {
		for (i = ARRAY_SIZE(days_since_year) - 1;
		    i > 0 && days_since_year[i] > w; --i)
			/* just loop */;
	}

	r->month    = i + 1;
	r->monthday = w - days_since_year[i] + 1;
	return;
}

>> +	s64 stamp;
>> +
>> +	if (skb->tstamp.tv64 == 0)
>> +		__net_timestamp((struct sk_buff *)skb);
>> +
>> +	stamp = skb->tstamp.tv64;
>> +	do_div(stamp, NSEC_PER_SEC);
>
>Would get_seconds() work?

One might think so, but there is a race involved, for example:

[16:59:59.100000 || 1190041199.100000]

	network code stamps the packet, with 1190040556 (we will
	ignore the subsecond part since xt_time does not deal with
	it.)

[16:59:59.200000 || 1190041199.200000]

	It may happen that the network code, or some iptables code,
	or whatever other code takes time. Assume the user added an
	xt_complex_match.ko which takes _at least_ that many jiffies
	until the next second.

[17:00:00.000000 || 1190041200.000000]

	Now it's 17:00. But the packet stamp still is 16:59:59.
	Consider the user had a

	-m time --timestart 15:00 --timestop 17:00

Now what? Further consider the following hypothetical case where we
inflate the times to show the problem -- since xt_time has only
minute resolution:

The user has a multi-core (#Cores >= 2) machine, and xt_complex_match
takes "a lot of time", say, 3 minutes per packet. It may sound
unrealistic, but shows the point. Then xt_time entrance would be at
17:03, __way__ after the packet actually arrived at the box, and way
after the 17:00 time boundary that was set.

Conclusion: It is not really a race, it just depends on what you
measure time relative against, i.e. whether xt_time is relative to
the packet timestamp or the actual processing {point in time}.

>> +static bool xt_time_check(const char *tablename, const void *ip,
>> +                          const struct xt_match *match, void *matchinfo,
>> +                          unsigned int hook_mask)
>> +{
>> +	struct xt_time_info *info = matchinfo;
>> +
>> +	/* xt_time's granularity is a minute, hence 24*60 = 1day */
>> +	if (info->time_start > 24 * 60 || info->time_stop >= 24 * 60) {
>> +		printk(KERN_WARNING "ipt_time: invalid argument - start or stop time greater than 23:59h\n");
>> +		return false;
>> +	}
>
>Is there a reason for minute granularity? The data types look large
>enough for at least second granularity (more is probably useless).

Yeah I can do that. Not that I see a real benefit for the user ;^) to
match on sub-minute times. ("Help me, my internet only works for 30
seconds for each minute!") The more I think about it, it might be a
really evil way to shape traffic (ha!)

for $h (0..23) {
for $m (1..60) {
-m time --timestart $h:$m:00 --timestop $h:$m:30 -j MARK --set-mark 1
-m time --timestart $h:$m:00 --timestop $h:$m:30 -j MARK --set-mark 2
}
}

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

* Re: xt_time 20070915
  2007-09-17 15:01   ` Jan Engelhardt
@ 2007-09-17 15:13     ` Jan Engelhardt
  2007-09-18  9:53     ` Patrick McHardy
  1 sibling, 0 replies; 10+ messages in thread
From: Jan Engelhardt @ 2007-09-17 15:13 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: Netfilter Developer Mailing List

netfilter list is kinda slow...

On Sep 17 2007 17:01, Jan Engelhardt wrote:
>>> +	u_int32_t monthdays_match;
>>> +	u_int16_t time_start;
>>> +	u_int16_t time_stop;
>>> +	u_int8_t weekdays_match;
>>
>>No inversion?
>
>[...Length explanation about set theory...]

Actually, there is also a technical limitation. There are three evil
cases the user could do:

	(1)	! --timestart 05:00   --timestop 07:00
	(2)	  --timestart 05:00 ! --timestop 07:00
	(3)	! --timestart 05:00 ! --timestop 07:00

and in all of these, we would have to record that ! was given and
fix up the start and stop times in time_final(). Thay may work,
but cases 2 and 3 look so odd that I am not sure I should do that.

I would really leave it as is now and assume the user is smart
enough to write

	--timestart 07:00 --timestop 05:00

if he wanted to match everything but the morning hours
!(--timestart 05:00 --timestop 07:00)


thanks,
Jan

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

* Re: xt_time 20070915
@ 2007-09-17 17:57 Jan Engelhardt
  0 siblings, 0 replies; 10+ messages in thread
From: Jan Engelhardt @ 2007-09-17 17:57 UTC (permalink / raw)
  To: Netfilter Developer Mailing List


I guess these mails don't make it to the old list anymore :p

---------- Forwarded message ----------
Date: Mon, 17 Sep 2007 17:01:43 +0200 (CEST)
Cc: Netfilter Developer Mailing List <netfilter-devel@lists.netfilter.org>
Subject: Re: xt_time 20070915


On Sep 17 2007 15:54, Patrick McHardy wrote:
>Jan Engelhardt wrote:
>> ipt_time from POM-ng augmented by:
>> 
>>  * xtables
>>  * ipv6 support
>
>I bet this was tricky :)

Yes, indeed. It's too easy to forget .family in the iptables part...

>> +++ linux-2.6.23/include/linux/netfilter/xt_time.h
>> @@ -0,0 +1,13 @@
>> +#ifndef _XT_TIME_H
>> +#define _XT_TIME_H 1
>> +
>> +struct xt_time_info {
>> +	time_t date_start;
>> +	time_t date_stop;
>
>This is an arch-dependant type and must not be used within the
>iptables ABI.

So what would you suggest? time_t will only work for 30 more years,
then we'll have to change it. So what is your pick for ABI-complete
time_t, uint64 or uint32?

>> +	u_int32_t monthdays_match;
>> +	u_int16_t time_start;
>> +	u_int16_t time_stop;
>> +	u_int8_t weekdays_match;
>
>No inversion?

Inversion is a funny thing with xt_time because (almost) all possible
times are _finite_.

Consider xt_connlimit and --connlimit x. The set of possible values
for x is infinite (assuming we were not limited by 32/64-bit CPUs).

Along comes xt_time. The set of possible values for --weekdays
is finite, there are exactly seven days for any given week. Since
this is the case, the negation of any set/bitmap of weekdays can
again be represented as a bitmap. So the inversion is done in
libxt_time.c (iptables).  !(Fri..Sun) == (Mon..Thu)

The same applies to time_start. Got a noon match that matches
from 10:00 to 15:00? Its inversion is (simply put) 15:00 to
10:00.

>> +#include "xt_time.h"
>
>This should be <include/netfilter/xt_time.h> I suppose?

Indeed.

>> +static void localtime(struct xtm *r, time_t time)
>> +{
>> +	unsigned int year, i, w;
>> +
>> +	time -= 60 * sys_tz.tz_minuteswest;
>> +
>> +	/* 1d=86400s. Daytime. */
>> +	w  = time % 86400;
>> +	w /= 60;
>> +	r->minute = w % 60;
>> +	r->hour   = w / 60;
>> +
>> +	/* Days since 1970-01-01 */
>> +	w = time / 86400;
>> +	r->weekday = (4 + w) % 7;
>> +
>> +	/*
>> +	 * Consider x=21551. Loop will abort on dse[i] <= x, which is with
>> +	 * dse[i] = 21550 (=> 2029-01-02).
>> +	 */
>> +	for (i = 0, year = DSE_FIRST; days_since_epoch[i] > w;
>> +	    ++i, --year)
>> +		/* just loop */;
>> +
>> +	w -= days_since_epoch[i];
>> +	/* r->yearday = w; */
>> +
>> +	if (is_leap(year)) {
>> +		for (i = ARRAY_SIZE(days_since_leapyear) - 1;
>> +		    i > 0 && days_since_year[i] > w; --i)
>> +			/* just loop */;
>> +	} else {
>> +		for (i = ARRAY_SIZE(days_since_year) - 1;
>> +		    i > 0 && days_since_year[i] > w; --i)
>> +			/* just loop */;
>> +	}
>> +
>> +	r->month    = i + 1;
>> +	r->monthday = w - days_since_year[i] + 1;
>> +	printk(KERN_ERR "m=%u md=%u\n", r->month, r->monthday);
>> +	return;
>> +}
>
>Some explanation what the above is doing would be appreciated.

/*
 * Each network packet has a (nano)seconds-since-the-epoch (SSTE) timestamp.
 * Since we match against days and daytime, the SSTE value needs to be
 * computed back into human-readable dates.
 */
static void localtime(struct xtm *r, time_t time)
{
	unsigned int year, i, w;

	time -= 60 * sys_tz.tz_minuteswest;

	/* Each day has 86400s, so finding the hour/minute is actually easy. */
	w  = time % 86400;
	w /= 60;
	r->minute = w % 60;
	r->hour   = w / 60;

	/*
	 * Here comes the rest (weekday, monthday). First, divide the SSTE
	 * by seconds-per-day to get the number of _days_ since the epoch.
	 */
	w = time / 86400;

	/* 1970-01-01 (w=0) was a Thursday (4). */
	r->weekday = (4 + w) % 7;

	/*
	 * In each year, a certain number of days-since-the-epoch have passed.
	 * Find the year that is closest to said days.
	 *
	 * Consider, for example, w=21612 (2029-03-04). Loop will abort on
	 * dse[i] <= w, which happens when dse[i] == 21550. This implies
	 * year == 2009. w will then be 62.
	 */
	for (i = 0, year = DSE_FIRST; days_since_epoch[i] > w;
	    ++i, --year)
		/* just loop */;

	w -= days_since_epoch[i];

	/*
	 * By now we have the current year, and the day of the year.
	 * r->yearday = w;
	 *
	 * On to finding the month (like above). In each month, a certain
	 * number of days-since-New Year have passed, and find the closest
	 * one.
	 *
	 * Consider w=62 (in a non-leap year). Loop will abort on
	 * dsy[i] < w, which happens when dsy[i] == 31+28 (i == 2).
	 * Concludes i == 2, i.e. 3rd month => March.
	 *
	 * (A different approach to use would be to subtract a monthlength
	 * from w repeatedly while counting.)
	 */
	if (is_leap(year)) {
		for (i = ARRAY_SIZE(days_since_leapyear) - 1;
		    i > 0 && days_since_year[i] > w; --i)
			/* just loop */;
	} else {
		for (i = ARRAY_SIZE(days_since_year) - 1;
		    i > 0 && days_since_year[i] > w; --i)
			/* just loop */;
	}

	r->month    = i + 1;
	r->monthday = w - days_since_year[i] + 1;
	return;
}

>> +	s64 stamp;
>> +
>> +	if (skb->tstamp.tv64 == 0)
>> +		__net_timestamp((struct sk_buff *)skb);
>> +
>> +	stamp = skb->tstamp.tv64;
>> +	do_div(stamp, NSEC_PER_SEC);
>
>Would get_seconds() work?

One might think so, but there is a race involved, for example:

[16:59:59.100000 || 1190041199.100000]

	network code stamps the packet, with 1190040556 (we will
	ignore the subsecond part since xt_time does not deal with
	it.)

[16:59:59.200000 || 1190041199.200000]

	It may happen that the network code, or some iptables code,
	or whatever other code takes time. Assume the user added an
	xt_complex_match.ko which takes _at least_ that many jiffies
	until the next second.

[17:00:00.000000 || 1190041200.000000]

	Now it's 17:00. But the packet stamp still is 16:59:59.
	Consider the user had a

	-m time --timestart 15:00 --timestop 17:00

Now what? Further consider the following hypothetical case where we
inflate the times to show the problem -- since xt_time has only
minute resolution:

The user has a multi-core (#Cores >= 2) machine, and xt_complex_match
takes "a lot of time", say, 3 minutes per packet. It may sound
unrealistic, but shows the point. Then xt_time entrance would be at
17:03, __way__ after the packet actually arrived at the box, and way
after the 17:00 time boundary that was set.

Conclusion: It is not really a race, it just depends on what you
measure time relative against, i.e. whether xt_time is relative to
the packet timestamp or the actual processing {point in time}.

>> +static bool xt_time_check(const char *tablename, const void *ip,
>> +                          const struct xt_match *match, void *matchinfo,
>> +                          unsigned int hook_mask)
>> +{
>> +	struct xt_time_info *info = matchinfo;
>> +
>> +	/* xt_time's granularity is a minute, hence 24*60 = 1day */
>> +	if (info->time_start > 24 * 60 || info->time_stop >= 24 * 60) {
>> +		printk(KERN_WARNING "ipt_time: invalid argument - start or stop time greater than 23:59h\n");
>> +		return false;
>> +	}
>
>Is there a reason for minute granularity? The data types look large
>enough for at least second granularity (more is probably useless).

Yeah I can do that. Not that I see a real benefit for the user ;^) to
match on sub-minute times. ("Help me, my internet only works for 30
seconds for each minute!") The more I think about it, it might be a
really evil way to shape traffic (ha!)

for $h (0..23) {
for $m (1..60) {
-m time --timestart $h:$m:00 --timestop $h:$m:30 -j MARK --set-mark 1
-m time --timestart $h:$m:00 --timestop $h:$m:30 -j MARK --set-mark 2
}
}

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

* Re: xt_time 20070915
@ 2007-09-17 17:57 Jan Engelhardt
  0 siblings, 0 replies; 10+ messages in thread
From: Jan Engelhardt @ 2007-09-17 17:57 UTC (permalink / raw)
  To: Netfilter Developer Mailing List


Part 2/2.

---------- Forwarded message ----------
Date: Mon, 17 Sep 2007 17:13:55 +0200 (CEST)
Cc: Netfilter Developer Mailing List <netfilter-devel@lists.netfilter.org>
Subject: Re: xt_time 20070915

netfilter list is kinda slow...

On Sep 17 2007 17:01, Jan Engelhardt wrote:
>>> +	u_int32_t monthdays_match;
>>> +	u_int16_t time_start;
>>> +	u_int16_t time_stop;
>>> +	u_int8_t weekdays_match;
>>
>>No inversion?
>
>[...Length explanation about set theory...]

Actually, there is also a technical limitation. There are three evil
cases the user could do:

	(1)	! --timestart 05:00   --timestop 07:00
	(2)	  --timestart 05:00 ! --timestop 07:00
	(3)	! --timestart 05:00 ! --timestop 07:00

and in all of these, we would have to record that ! was given and
fix up the start and stop times in time_final(). Thay may work,
but cases 2 and 3 look so odd that I am not sure I should do that.

I would really leave it as is now and assume the user is smart
enough to write

	--timestart 07:00 --timestop 05:00

if he wanted to match everything but the morning hours
!(--timestart 05:00 --timestop 07:00)


thanks,
Jan

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

* Re: xt_time 20070915
  2007-09-17 15:01   ` Jan Engelhardt
  2007-09-17 15:13     ` Jan Engelhardt
@ 2007-09-18  9:53     ` Patrick McHardy
  2007-09-18 11:42       ` mirq-linux
  2007-09-19 14:32       ` Jan Engelhardt
  1 sibling, 2 replies; 10+ messages in thread
From: Patrick McHardy @ 2007-09-18  9:53 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List

Jan Engelhardt wrote:
> On Sep 17 2007 15:54, Patrick McHardy wrote:
> 
>>>+++ linux-2.6.23/include/linux/netfilter/xt_time.h
>>>@@ -0,0 +1,13 @@
>>>+#ifndef _XT_TIME_H
>>>+#define _XT_TIME_H 1
>>>+
>>>+struct xt_time_info {
>>>+	time_t date_start;
>>>+	time_t date_stop;
>>
>>This is an arch-dependant type and must not be used within the
>>iptables ABI.
> 
> 
> So what would you suggest? time_t will only work for 30 more years,
> then we'll have to change it. So what is your pick for ABI-complete
> time_t, uint64 or uint32?


Both is fine, I intend to replace the interface by something netlink
based before 30 years are gone :) But if you want to be safe, use an
aligned_u64.

>>>+	u_int32_t monthdays_match;
>>>+	u_int16_t time_start;
>>>+	u_int16_t time_stop;
>>>+	u_int8_t weekdays_match;
>>
>>No inversion?
> 
> 
> Inversion is a funny thing with xt_time because (almost) all possible
> times are _finite_.
> 
> Consider xt_connlimit and --connlimit x. The set of possible values
> for x is infinite (assuming we were not limited by 32/64-bit CPUs).
> 
> Along comes xt_time. The set of possible values for --weekdays
> is finite, there are exactly seven days for any given week. Since
> this is the case, the negation of any set/bitmap of weekdays can
> again be represented as a bitmap. So the inversion is done in
> libxt_time.c (iptables).  !(Fri..Sun) == (Mon..Thu)
> 
> The same applies to time_start. Got a noon match that matches
> from 10:00 to 15:00? Its inversion is (simply put) 15:00 to
> 10:00.


Mhh .. but do we have to handle this in the kernel? Instead
of:

+	if (info->time_start < info->time_stop) {
+		...
+	} else {
+		...
+	}

you could switch the times in userspace and use inversion.
But its not important, if you prefer this way thats also
fine with me.

>>Some explanation what the above is doing would be appreciated.
> 
> 
> /*
>  * Each network packet has a (nano)seconds-since-the-epoch (SSTE) timestamp.
>  * Since we match against days and daytime, the SSTE value needs to be
>  * computed back into human-readable dates.
>  */
> static void localtime(struct xtm *r, time_t time)
> {
> 	unsigned int year, i, w;
> 
> 	time -= 60 * sys_tz.tz_minuteswest;
> 
> 	/* Each day has 86400s, so finding the hour/minute is actually easy. */
> 	w  = time % 86400;
> 	w /= 60;
> 	r->minute = w % 60;
> 	r->hour   = w / 60;
> 
> 	/*
> 	 * Here comes the rest (weekday, monthday). First, divide the SSTE
> 	 * by seconds-per-day to get the number of _days_ since the epoch.
> 	 */
> 	w = time / 86400;
> 
> 	/* 1970-01-01 (w=0) was a Thursday (4). */
> 	r->weekday = (4 + w) % 7;
> 
> 	/*
> 	 * In each year, a certain number of days-since-the-epoch have passed.
> 	 * Find the year that is closest to said days.
> 	 *
> 	 * Consider, for example, w=21612 (2029-03-04). Loop will abort on
> 	 * dse[i] <= w, which happens when dse[i] == 21550. This implies
> 	 * year == 2009. w will then be 62.
> 	 */
> 	for (i = 0, year = DSE_FIRST; days_since_epoch[i] > w;
> 	    ++i, --year)
> 		/* just loop */;
> 
> 	w -= days_since_epoch[i];
> 
> 	/*
> 	 * By now we have the current year, and the day of the year.
> 	 * r->yearday = w;
> 	 *
> 	 * On to finding the month (like above). In each month, a certain
> 	 * number of days-since-New Year have passed, and find the closest
> 	 * one.
> 	 *
> 	 * Consider w=62 (in a non-leap year). Loop will abort on
> 	 * dsy[i] < w, which happens when dsy[i] == 31+28 (i == 2).
> 	 * Concludes i == 2, i.e. 3rd month => March.
> 	 *
> 	 * (A different approach to use would be to subtract a monthlength
> 	 * from w repeatedly while counting.)
> 	 */
> 	if (is_leap(year)) {
> 		for (i = ARRAY_SIZE(days_since_leapyear) - 1;
> 		    i > 0 && days_since_year[i] > w; --i)
> 			/* just loop */;
> 	} else {
> 		for (i = ARRAY_SIZE(days_since_year) - 1;
> 		    i > 0 && days_since_year[i] > w; --i)
> 			/* just loop */;
> 	}
> 
> 	r->month    = i + 1;
> 	r->monthday = w - days_since_year[i] + 1;
> 	return;
> }


All of this looks pretty complicated/expansive. I wonder whether
we could omit a few of these calculations (like day, month, ..)
if the user isn't matching on them.

>>>+	s64 stamp;
>>>+
>>>+	if (skb->tstamp.tv64 == 0)
>>>+		__net_timestamp((struct sk_buff *)skb);
>>>+
>>>+	stamp = skb->tstamp.tv64;
>>>+	do_div(stamp, NSEC_PER_SEC);
>>
>>Would get_seconds() work?
> 
> 
> One might think so, but there is a race involved, for example:
> 
> [16:59:59.100000 || 1190041199.100000]
> 
> 	network code stamps the packet, with 1190040556 (we will
> 	ignore the subsecond part since xt_time does not deal with
> 	it.)
> 
> [16:59:59.200000 || 1190041199.200000]
> 
> 	It may happen that the network code, or some iptables code,
> 	or whatever other code takes time. Assume the user added an
> 	xt_complex_match.ko which takes _at least_ that many jiffies
> 	until the next second.
> 
> [17:00:00.000000 || 1190041200.000000]
> 
> 	Now it's 17:00. But the packet stamp still is 16:59:59.
> 	Consider the user had a
> 
> 	-m time --timestart 15:00 --timestop 17:00
> 
> Now what? Further consider the following hypothetical case where we
> inflate the times to show the problem -- since xt_time has only
> minute resolution:
> 
> The user has a multi-core (#Cores >= 2) machine, and xt_complex_match
> takes "a lot of time", say, 3 minutes per packet. It may sound
> unrealistic, but shows the point. Then xt_time entrance would be at
> 17:03, __way__ after the packet actually arrived at the box, and way
> after the 17:00 time boundary that was set.
> 
> Conclusion: It is not really a race, it just depends on what you
> measure time relative against, i.e. whether xt_time is relative to
> the packet timestamp or the actual processing {point in time}.


As a user I would expect that it uses the point in time at which
the packet is processed.

>>>+static bool xt_time_check(const char *tablename, const void *ip,
>>>+                          const struct xt_match *match, void *matchinfo,
>>>+                          unsigned int hook_mask)
>>>+{
>>>+	struct xt_time_info *info = matchinfo;
>>>+
>>>+	/* xt_time's granularity is a minute, hence 24*60 = 1day */
>>>+	if (info->time_start > 24 * 60 || info->time_stop >= 24 * 60) {
>>>+		printk(KERN_WARNING "ipt_time: invalid argument - start or stop time greater than 23:59h\n");
>>>+		return false;
>>>+	}
>>
>>Is there a reason for minute granularity? The data types look large
>>enough for at least second granularity (more is probably useless).
> 
> 
> Yeah I can do that. Not that I see a real benefit for the user ;^) to
> match on sub-minute times. ("Help me, my internet only works for 30
> seconds for each minute!") The more I think about it, it might be a
> really evil way to shape traffic (ha!)


Well, me neither, but if its no trouble, we can let the user
decide whether its useful or not.

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

* Re: xt_time 20070915
  2007-09-18  9:53     ` Patrick McHardy
@ 2007-09-18 11:42       ` mirq-linux
  2007-09-19 14:32       ` Jan Engelhardt
  1 sibling, 0 replies; 10+ messages in thread
From: mirq-linux @ 2007-09-18 11:42 UTC (permalink / raw)
  To: Netfilter Developer Mailing List

On Tue, Sep 18, 2007 at 11:53:57AM +0200, Patrick McHardy wrote:
> Jan Engelhardt wrote:
> > Conclusion: It is not really a race, it just depends on what you
> > measure time relative against, i.e. whether xt_time is relative to
> > the packet timestamp or the actual processing {point in time}.
> As a user I would expect that it uses the point in time at which
> the packet is processed.

Suppose you have two rules:

1. match before 10:00 am
2. match after 10:00 am

If you match against processing time it may happen, that the same packet
matches both rules if it arrived at the right moment before 10:00 am.

  -- Michal Miroslaw

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

* Re: xt_time 20070915
  2007-09-18  9:53     ` Patrick McHardy
  2007-09-18 11:42       ` mirq-linux
@ 2007-09-19 14:32       ` Jan Engelhardt
  1 sibling, 0 replies; 10+ messages in thread
From: Jan Engelhardt @ 2007-09-19 14:32 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: Netfilter Developer Mailing List


On Sep 18 2007 11:53, Patrick McHardy wrote:
>
>Mhh .. but do we have to handle this in the kernel? Instead
>of:
>
>+	if (info->time_start < info->time_stop) {
>+		...
>+	} else {
>+		...
>+	}
>
>you could switch the times in userspace and use inversion.
>But its not important, if you prefer this way thats also
>fine with me.

The issue is that the user can specify inversion twice
(for both --timestamp and --timestop), or is allowed to
specify confusing inversions, like

	iptables ! --timestop 17:00 --timestart 16:00

>All of this looks pretty complicated/expansive. I wonder whether
>we could omit a few of these calculations (like day, month, ..)
>if the user isn't matching on them.

Yup, will shuffle.

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

end of thread, other threads:[~2007-09-19 14:32 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-09-15 16:07 xt_time 20070915 Jan Engelhardt
2007-09-15 17:41 ` xt_time 20070915 (iptables) Jan Engelhardt
2007-09-17 13:54 ` xt_time 20070915 Patrick McHardy
2007-09-17 15:01   ` Jan Engelhardt
2007-09-17 15:13     ` Jan Engelhardt
2007-09-18  9:53     ` Patrick McHardy
2007-09-18 11:42       ` mirq-linux
2007-09-19 14:32       ` Jan Engelhardt
  -- strict thread matches above, loose matches on Subject: below --
2007-09-17 17:57 Jan Engelhardt
2007-09-17 17:57 Jan Engelhardt

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).