Mailing List Archive

xt_time 20070915
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");
Re: xt_time 20070915 [ In reply to ]
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).
Re: xt_time 20070915 [ In reply to ]
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
}
}
Re: xt_time 20070915 [ In reply to ]
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
Re: xt_time 20070915 [ In reply to ]
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
}
}
-
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: xt_time 20070915 [ In reply to ]
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
-
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: xt_time 20070915 [ In reply to ]
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.
Re: xt_time 20070915 [ In reply to ]
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
Re: xt_time 20070915 [ In reply to ]
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.