* xt_time 2007-09-22
@ 2007-09-22 11:26 Jan Engelhardt
2007-09-22 11:28 ` xt_time 2007-09-22 (iptables) Jan Engelhardt
2007-09-22 15:19 ` xt_time 2007-09-22 Patrick McHardy
0 siblings, 2 replies; 11+ messages in thread
From: Jan Engelhardt @ 2007-09-22 11:26 UTC (permalink / raw)
To: Netfilter Developer Mailing List
Add the xt_time netfilter match
This is ipt_time from POM-ng enhanced by the following:
* xtables/ipv6 support
* second granularity for daytime
* day-of-month support (for example "match on the 15th of each month")
* match against UTC or local timezone
Signed-off-by: Jan Engelhardt <jengelh@computergmbh.de>
---
include/linux/netfilter/xt_time.h | 25 +++
net/netfilter/Kconfig | 14 ++
net/netfilter/Makefile | 1
net/netfilter/xt_time.c | 266 ++++++++++++++++++++++++++++++++++++++
4 files changed, 306 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,25 @@
+#ifndef _XT_TIME_H
+#define _XT_TIME_H 1
+
+struct xt_time_info {
+ u_int32_t date_start;
+ u_int32_t date_stop;
+ u_int32_t daytime_start;
+ u_int32_t daytime_stop;
+ u_int32_t monthdays_match;
+ u_int8_t weekdays_match;
+ u_int8_t flags;
+};
+
+enum {
+ /* Match against local time (instead of UTC) */
+ XT_TIME_LOCAL_TZ = 1 << 0,
+
+ /* Shortcuts */
+ XT_TIME_ALL_MONTHDAYS = 0xFFFFFFFE,
+ XT_TIME_ALL_WEEKDAYS = 0xFE,
+ XT_TIME_MIN_DAYTIME = 0,
+ XT_TIME_MAX_DAYTIME = 24 * 60 * 60 - 1,
+};
+
+#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,266 @@
+/*
+ * 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 <linux/netfilter/xt_time.h>
+
+struct xtm {
+ u_int8_t month; /* (1-12) */
+ u_int8_t monthday; /* (1-31) */
+ u_int8_t weekday; /* (1-7) */
+ u_int8_t hour; /* (0-23) */
+ u_int8_t minute; /* (0-59) */
+ u_int8_t second; /* (0-59) */
+ unsigned int dse;
+};
+
+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);
+}
+
+/*
+ * 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.
+ *
+ * This is done in three separate functions so that the most expensive
+ * calculations are done last, in case a "simple match" can be found earlier.
+ */
+static inline unsigned int localtime_1(struct xtm *r, time_t time)
+{
+ unsigned int v, w;
+
+ /* Each day has 86400s, so finding the hour/minute is actually easy. */
+ v = time % 86400;
+ r->second = v % 60;
+ w = v / 60;
+ r->minute = w % 60;
+ r->hour = w / 60;
+ return v;
+}
+
+static inline void localtime_2(struct xtm *r, time_t time)
+{
+ /*
+ * Here comes the rest (weekday, monthday). First, divide the SSTE
+ * by seconds-per-day to get the number of _days_ since the epoch.
+ */
+ r->dse = time / 86400;
+
+ /* 1970-01-01 (w=0) was a Thursday (4). */
+ r->weekday = (4 + r->dse) % 7;
+}
+
+static void localtime_3(struct xtm *r, time_t time)
+{
+ unsigned int year, i, w = r->dse;
+
+ /*
+ * 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;
+}
+
+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;
+
+ /*
+ * We cannot use get_seconds() instead of __net_timestamp() here.
+ * Suppose you have two rules:
+ * 1. match before 13:00
+ * 2. match after 13:00
+ * If you match against processing time (get_seconds) it
+ * may happen that the same packet matches both rules if
+ * it arrived at the right moment before 13:00.
+ */
+ if (skb->tstamp.tv64 == 0)
+ __net_timestamp((struct sk_buff *)skb);
+
+ stamp = skb->tstamp.tv64;
+ do_div(stamp, NSEC_PER_SEC);
+
+ if (info->flags & XT_TIME_LOCAL_TZ)
+ /* Adjust for local timezone */
+ stamp -= 60 * sys_tz.tz_minuteswest;
+
+ /*
+ * 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;
+
+ packet_time = localtime_1(¤t_time, stamp);
+
+ if (info->daytime_start < info->daytime_stop) {
+ if (packet_time < info->daytime_start ||
+ packet_time > info->daytime_stop)
+ return false;
+ } else {
+ if (packet_time < info->daytime_start &&
+ packet_time > info->daytime_stop)
+ return false;
+ }
+
+ localtime_2(¤t_time, stamp);
+
+ if (!(info->weekdays_match & (1 << current_time.weekday)))
+ return false;
+
+ localtime_3(¤t_time, stamp);
+
+ if (!(info->monthdays_match & (1 << current_time.monthday)))
+ 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;
+
+ if (info->daytime_start > XT_TIME_MAX_DAYTIME ||
+ info->daytime_stop > XT_TIME_MAX_DAYTIME) {
+ printk(KERN_WARNING "xt_time: invalid argument - start or "
+ "stop time greater than 23:59:59\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");
-
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
^ permalink raw reply [flat|nested] 11+ messages in thread* xt_time 2007-09-22 (iptables) 2007-09-22 11:26 xt_time 2007-09-22 Jan Engelhardt @ 2007-09-22 11:28 ` Jan Engelhardt 2007-09-23 15:08 ` Patrick McHardy 2007-09-22 15:19 ` xt_time 2007-09-22 Patrick McHardy 1 sibling, 1 reply; 11+ messages in thread From: Jan Engelhardt @ 2007-09-22 11:28 UTC (permalink / raw) To: Netfilter Developer Mailing List Add the libxt_time iptables match This is libipt_time from POM-ng enhanced by the following: * day-of-month support (for example "match on the 15th of each month") * inversion support for --weekdays and --monthdays * match against UTC or local timezone * a manpage Signed-off-by: Jan Engelhardt <jengelh@computergmbh.de> --- extensions/Makefile | 2 extensions/libipt_time.man | 69 +++++ extensions/libxt_time.c | 504 ++++++++++++++++++++++++++++++++++++++ include/linux/netfilter/xt_time.h | 25 + 4 files changed, 599 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,69 @@ +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[\fB-\fR\fIDD\fR[\fBT\fR\fIhh\fR[\fB:\fR\fImm\fR[\fB:\fR\fIss\fR]]]]] +.TP +\fB--datestop\fR \fIYYYY\fR[\fB-\fR\fIMM\fR[\fB-\fR\fIDD\fR[\fBT\fR\fIhh\fR[\fB:\fR\fImm\fR[\fB:\fR\fIss\fR]]]]] +.IP +Only match during the given time, which must be in ISO 8601 "T" notation. +The possible time range is 1970-01-01T00:00:00 to 2038-01-19T04:17:07. +.IP +If --datestart or --datestop are not specified, it will default to 1970-01-01 +and 2038-01-19, respectively. +.TP +\fB--timestart\fR \fIhh\fR\fB:\fR\fImm\fR[\fB:\fR\fIss\fR] +.TP +\fB--timestop\fR \fIhh\fR\fB:\fR\fImm\fR[\fB:\fR\fIss\fR] +.IP +Only match during the given daytime. The possible time range is 00:00:00 to +23:59:59. Leading zeroes are allowed (e.g. "06:03") and correctly interpreted +as base-10. +.TP +[\fB!\fR] \fB--monthday\fR \fIday\fR[\fB,\fR\fIday\fR...] +.IP +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...] +.IP +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.). +.TP +\fB--utc\fR +.IP +Interpret the times given for \fB--datestart\fR, \fB--datestop\fR, +\fB--timestart\fR and \fB--timestop\fR to be UTC. +.TP +\fB--localtz\fR +.IP +Interpret the times given for \fB--datestart\fR, \fB--datestop\fR, +\fB--timestart\fR and \fB--timestop\fR to be local kernel time. (Default) +.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 the following stop +time 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,504 @@ +/* + * 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 { /* getopt "seen" bits */ + 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, + F_TIMEZONE = 1 << 6, +}; + +static const char *const week_days[] = { + NULL, "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun", +}; + +static const struct option time_opts[] = { + {"datestart", true, NULL, 'D'}, + {"datestop", true, NULL, 'E'}, + {"timestart", true, NULL, 'X'}, + {"timestop", true, NULL, 'Y'}, + {"weekdays", true, NULL, 'w'}, + {"monthdays", true, NULL, 'm'}, + {"localtz", false, NULL, 'l'}, + {"utc", false, NULL, 'u'}, + {NULL}, +}; + +static void time_help(void) +{ + printf( +"TIME v%s options:\n" +" --datestart time Start and stop time, to be given in ISO 8601\n" +" --datestop time (YYYY[-MM[-DD[Thh[:mm[:ss]]]]])\n" +" --timestart time Start and stop daytime (hh:mm[:ss])\n" +" --timestop time (between 00:00:00 and 23:59:59)\n" +" --monthdays value List of days on which to match, separated by comma\n" +" (Possible days: 1 to 31; defaults to all)\n" +" --weekdays value List of weekdays on which to match, sep. by comma\n" +" (Possible days: Mon,Tue,Wed,Thu,Fri,Sat,Sun or 1 to 7\n" +" Defaults to all weekdays.)\n" +" --localtz/--utc Time is interpreted as UTC/local time\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 = XT_TIME_ALL_MONTHDAYS; + info->weekdays_match = XT_TIME_ALL_WEEKDAYS; + info->daytime_start = XT_TIME_MIN_DAYTIME; + info->daytime_stop = XT_TIME_MAX_DAYTIME; + + /* ...and have no date-begin or date-end boundary */ + info->date_start = 0; + info->date_stop = LONG_MAX; + + /* local time is default */ + info->flags |= XT_TIME_LOCAL_TZ; +} + +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, second = 0; + char *e; + + hour = strtoul(s, &e, 10); + if (*e != ':' || hour > 23) + goto out; + + 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: + return 60 * 60 * hour + 60 * minute + second; + + out: + exit_error(PARAMETER_PROBLEM, "invalid time \"%s\" specified, " + "should be hh:mm[:ss] format and within the boundaries", 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; + return 1; + 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; + return 1; + 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->daytime_start = time_parse_minutes(optarg); + *flags |= F_TIME_START; + return 1; + 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->daytime_stop = time_parse_minutes(optarg); + *flags |= F_TIME_STOP; + return 1; + case 'l': /* --localtz */ + if (*flags & F_TIMEZONE) + exit_error(PARAMETER_PROBLEM, + "Can only specify exactly one of --localtz or --utc"); + info->flags |= XT_TIME_LOCAL_TZ; + *flags |= F_TIMEZONE; + return 1; + 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 ^= XT_TIME_ALL_MONTHDAYS; + *flags |= F_MONTHDAYS; + return 1; + 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 ^= XT_TIME_ALL_WEEKDAYS; + *flags |= F_WEEKDAYS; + return 1; + case 'u': /* --utc */ + if (*flags & F_TIMEZONE) + exit_error(PARAMETER_PROBLEM, + "Can only specify exactly one of --localtz or --utc"); + info->flags &= ~XT_TIME_LOCAL_TZ; + *flags |= F_TIMEZONE; + return 1; + } + return 0; +} + +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 "T" variant. + */ + 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, bool human_readable) +{ + unsigned int i, nbdays = 0; + + for (i = 1; i <= 31; ++i) + if (mask & (1 << i)) { + if (nbdays++ > 0) + printf(","); + printf("%u", i); + if (human_readable) + switch (i % 10) { + case 1: + printf("st"); + break; + case 2: + printf("nd"); + break; + case 3: + printf("rd"); + break; + default: + printf("th"); + break; + } + } + 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(" "); +} + +static inline void divide_time(unsigned int fulltime, unsigned int *hours, + unsigned int *minutes, unsigned int *seconds) +{ + *seconds = fulltime % 60; + fulltime /= 60; + *minutes = fulltime % 60; + *hours = fulltime / 60; +} + +static void time_print(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + struct xt_time_info *info = (void *)match->data; + unsigned int h, m, s; + + printf("TIME "); + + if (info->daytime_start != XT_TIME_MIN_DAYTIME || + info->daytime_stop != XT_TIME_MAX_DAYTIME) { + divide_time(info->daytime_start, &h, &m, &s); + printf("from %02u:%02u:%02u ", h, m, s); + divide_time(info->daytime_stop, &h, &m, &s); + printf("to %02u:%02u:%02u ", h, m, s); + } + if (info->weekdays_match != XT_TIME_ALL_WEEKDAYS) { + printf("on "); + time_print_weekdays(info->weekdays_match); + } + if (info->monthdays_match != XT_TIME_ALL_MONTHDAYS) { + printf("on "); + time_print_monthdays(info->monthdays_match, true); + } + if (info->date_start != 0) { + printf("starting from "); + time_print_date(info->date_start, NULL); + } + if (info->date_stop != LONG_MAX) { + printf("until date "); + time_print_date(info->date_stop, NULL); + } + if (!(info->flags & XT_TIME_LOCAL_TZ)) + printf("UTC "); +} + +static void time_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_time_info *info = (const void *)match->data; + unsigned int h, m, s; + + if (info->daytime_start != XT_TIME_MIN_DAYTIME || + info->daytime_stop != XT_TIME_MAX_DAYTIME) { + divide_time(info->daytime_start, &h, &m, &s); + printf("--timestart %02u:%02u:%02u ", h, m, s); + divide_time(info->daytime_stop, &h, &m, &s); + printf("--timestop %02u:%02u:%02u ", h, m, s); + } + if (info->monthdays_match != XT_TIME_ALL_MONTHDAYS) { + printf("--monthdays "); + time_print_monthdays(info->monthdays_match, false); + } + if (info->weekdays_match != XT_TIME_ALL_WEEKDAYS) { + printf("--weekdays "); + time_print_weekdays(info->weekdays_match); + printf(" "); + } + time_print_date(info->date_start, "--datestart"); + time_print_date(info->date_stop, "--datestop"); + if (!(info->flags & XT_TIME_LOCAL_TZ)) + printf("--utc "); +} + +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,25 @@ +#ifndef _XT_TIME_H +#define _XT_TIME_H 1 + +struct xt_time_info { + u_int32_t date_start; + u_int32_t date_stop; + u_int32_t daytime_start; + u_int32_t daytime_stop; + u_int32_t monthdays_match; + u_int8_t weekdays_match; + u_int8_t flags; +}; + +enum { + /* Match against local time (instead of UTC) */ + XT_TIME_LOCAL_TZ = 1 << 0, + + /* Shortcuts */ + XT_TIME_ALL_MONTHDAYS = 0xFFFFFFFE, + XT_TIME_ALL_WEEKDAYS = 0xFE, + XT_TIME_MIN_DAYTIME = 0, + XT_TIME_MAX_DAYTIME = 24 * 60 * 60 - 1, +}; + +#endif /* _XT_TIME_H */ - 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 ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: xt_time 2007-09-22 (iptables) 2007-09-22 11:28 ` xt_time 2007-09-22 (iptables) Jan Engelhardt @ 2007-09-23 15:08 ` Patrick McHardy 0 siblings, 0 replies; 11+ messages in thread From: Patrick McHardy @ 2007-09-23 15:08 UTC (permalink / raw) To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List Jan Engelhardt wrote: > Add the libxt_time iptables match > > This is libipt_time from POM-ng enhanced by the following: > > * day-of-month support (for example "match on the 15th of each month") > * inversion support for --weekdays and --monthdays > * match against UTC or local timezone > * a manpage > > Signed-off-by: Jan Engelhardt <jengelh@computergmbh.de> Also applied, thanks Jan. ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: xt_time 2007-09-22 2007-09-22 11:26 xt_time 2007-09-22 Jan Engelhardt 2007-09-22 11:28 ` xt_time 2007-09-22 (iptables) Jan Engelhardt @ 2007-09-22 15:19 ` Patrick McHardy 2007-09-22 15:38 ` Jan Engelhardt 1 sibling, 1 reply; 11+ messages in thread From: Patrick McHardy @ 2007-09-22 15:19 UTC (permalink / raw) To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List Jan Engelhardt wrote: > +/* > + * 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. > + * > + * This is done in three separate functions so that the most expensive > + * calculations are done last, in case a "simple match" can be found earlier. > + */ > +static inline unsigned int localtime_1(struct xtm *r, time_t time) > +{ > + unsigned int v, w; > + > + /* Each day has 86400s, so finding the hour/minute is actually easy. */ > + v = time % 86400; > + r->second = v % 60; > + w = v / 60; > + r->minute = w % 60; > + r->hour = w / 60; > + return v; > +} > + > +static inline void localtime_2(struct xtm *r, time_t time) > +{ > + /* > + * Here comes the rest (weekday, monthday). First, divide the SSTE > + * by seconds-per-day to get the number of _days_ since the epoch. > + */ > + r->dse = time / 86400; > + > + /* 1970-01-01 (w=0) was a Thursday (4). */ > + r->weekday = (4 + r->dse) % 7; > +} > + > +static void localtime_3(struct xtm *r, time_t time) > +{ > + unsigned int year, i, w = r->dse; > + > + /* > + * 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; > +} > A thought just occured to me (a bit late, but better than never :) .. Can't we just convert the user-specified times/dates to unix time and match on that without all these expensive calculations? At least for pure time matching without days this should work fine .. > + > +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; > + > + /* > + * We cannot use get_seconds() instead of __net_timestamp() here. > + * Suppose you have two rules: > + * 1. match before 13:00 > + * 2. match after 13:00 > + * If you match against processing time (get_seconds) it > + * may happen that the same packet matches both rules if > + * it arrived at the right moment before 13:00. > + */ > + if (skb->tstamp.tv64 == 0) > + __net_timestamp((struct sk_buff *)skb); > + > + stamp = skb->tstamp.tv64; > + do_div(stamp, NSEC_PER_SEC); > + > + if (info->flags & XT_TIME_LOCAL_TZ) > + /* Adjust for local timezone */ > + stamp -= 60 * sys_tz.tz_minuteswest; > + > + /* > + * 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; > + > + packet_time = localtime_1(¤t_time, stamp); > + > + if (info->daytime_start < info->daytime_stop) { > + if (packet_time < info->daytime_start || > + packet_time > info->daytime_stop) > + return false; > + } else { > + if (packet_time < info->daytime_start && > + packet_time > info->daytime_stop) > + return false; > + } > + > + localtime_2(¤t_time, stamp); > + > + if (!(info->weekdays_match & (1 << current_time.weekday))) > + return false; > + > + localtime_3(¤t_time, stamp); > + > + if (!(info->monthdays_match & (1 << current_time.monthday))) > + return false; > I'd add a flag or check whether weekdays_match/monthdays_match equals ~0 before doing the localtime calculations. Besides that, looks good to me, thanks for doing this work. ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: xt_time 2007-09-22 2007-09-22 15:19 ` xt_time 2007-09-22 Patrick McHardy @ 2007-09-22 15:38 ` Jan Engelhardt 2007-09-22 15:39 ` Patrick McHardy 2007-09-22 15:58 ` Jan Engelhardt 0 siblings, 2 replies; 11+ messages in thread From: Jan Engelhardt @ 2007-09-22 15:38 UTC (permalink / raw) To: Patrick McHardy; +Cc: Netfilter Developer Mailing List On Sep 22 2007 17:19, Patrick McHardy wrote: > > A thought just occured to me (a bit late, but better than never :) .. > > Can't we just convert the user-specified times/dates to unix time > and match on that without all these expensive calculations? At least > for pure time matching without days this should work fine .. Pure time matching (--datestart, --datestop) already use unix timing. Daytime (--timestart) ... I wonder how you'd model "from 12:00 to 13:00, each day" other than it is done now. Same for weekday. Leaves monthday matching, which could probably be done with some more static tables, but I'd have to dig up some old encyclopedia from 1972 and see how they did it. >> + localtime_3(¤t_time, stamp); >> + >> + if (!(info->monthdays_match & (1 << current_time.monthday))) >> + return false; >> > > I'd add a flag or check whether weekdays_match/monthdays_match > equals ~0 before doing the localtime calculations. Certainly. ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: xt_time 2007-09-22 2007-09-22 15:38 ` Jan Engelhardt @ 2007-09-22 15:39 ` Patrick McHardy 2007-09-22 15:47 ` Jan Engelhardt 2007-09-22 15:58 ` Jan Engelhardt 1 sibling, 1 reply; 11+ messages in thread From: Patrick McHardy @ 2007-09-22 15:39 UTC (permalink / raw) To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List Jan Engelhardt wrote: > On Sep 22 2007 17:19, Patrick McHardy wrote: > >> A thought just occured to me (a bit late, but better than never :) .. >> >> Can't we just convert the user-specified times/dates to unix time >> and match on that without all these expensive calculations? At least >> for pure time matching without days this should work fine .. >> > > Pure time matching (--datestart, --datestop) already use unix timing. > Right, I missed that. > Daytime (--timestart) ... I wonder how you'd model > "from 12:00 to 13:00, each day" other than it is done now. > Same for weekday. > You're right again, that doesn't seem to be possible. > Leaves monthday matching, which could probably be done with some more > static tables, but I'd have to dig up some old encyclopedia from 1972 > and see how they did it. > I guess that will be a very rarely used feature anyway, don't bother. > >>> + localtime_3(¤t_time, stamp); >>> + >>> + if (!(info->monthdays_match & (1 << current_time.monthday))) >>> + return false; >>> >>> >> I'd add a flag or check whether weekdays_match/monthdays_match >> equals ~0 before doing the localtime calculations. >> > > Certainly. > > Thanks. ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: xt_time 2007-09-22 2007-09-22 15:39 ` Patrick McHardy @ 2007-09-22 15:47 ` Jan Engelhardt 0 siblings, 0 replies; 11+ messages in thread From: Jan Engelhardt @ 2007-09-22 15:47 UTC (permalink / raw) To: Patrick McHardy; +Cc: Netfilter Developer Mailing List On Sep 22 2007 17:39, Patrick McHardy wrote: >> Leaves monthday matching, which could probably be done with some more >> static tables, but I'd have to dig up some old encyclopedia from 1972 >> and see how they did it. > > I guess that will be a very rarely used feature anyway, don't > bother. I checked, and it's only a formula (with static int[] table) that transforms a date (e.g. 2007-09-02) into a weekday (Mo-Su). But since we are starting with a UNIX time, it does not apply. (Especially since UNIX->weekday transformation is faster than UNIX->day->weekday.) ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: xt_time 2007-09-22 2007-09-22 15:38 ` Jan Engelhardt 2007-09-22 15:39 ` Patrick McHardy @ 2007-09-22 15:58 ` Jan Engelhardt 2007-09-23 15:05 ` Patrick McHardy 1 sibling, 1 reply; 11+ messages in thread From: Jan Engelhardt @ 2007-09-22 15:58 UTC (permalink / raw) To: Patrick McHardy; +Cc: Netfilter Developer Mailing List On Sep 22 2007 17:38, Jan Engelhardt wrote: >>> + localtime_3(¤t_time, stamp); >>> + >>> + if (!(info->monthdays_match & (1 << current_time.monthday))) >>> + return false; >>> >> >> I'd add a flag or check whether weekdays_match/monthdays_match >> equals ~0 before doing the localtime calculations. > >Certainly. Add the xt_time netfilter match This is ipt_time from POM-ng enhanced by the following: * xtables/ipv6 support * second granularity for daytime * day-of-month support (for example "match on the 15th of each month") * match against UTC or local timezone Signed-off-by: Jan Engelhardt <jengelh@computergmbh.de> --- include/linux/netfilter/xt_time.h | 25 +++ net/netfilter/Kconfig | 14 + net/netfilter/Makefile | 1 net/netfilter/xt_time.c | 269 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 309 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,25 @@ +#ifndef _XT_TIME_H +#define _XT_TIME_H 1 + +struct xt_time_info { + u_int32_t date_start; + u_int32_t date_stop; + u_int32_t daytime_start; + u_int32_t daytime_stop; + u_int32_t monthdays_match; + u_int8_t weekdays_match; + u_int8_t flags; +}; + +enum { + /* Match against local time (instead of UTC) */ + XT_TIME_LOCAL_TZ = 1 << 0, + + /* Shortcuts */ + XT_TIME_ALL_MONTHDAYS = 0xFFFFFFFE, + XT_TIME_ALL_WEEKDAYS = 0xFE, + XT_TIME_MIN_DAYTIME = 0, + XT_TIME_MAX_DAYTIME = 24 * 60 * 60 - 1, +}; + +#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,269 @@ +/* + * 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/types.h> +#include <linux/netfilter/x_tables.h> +#include <linux/netfilter/xt_time.h> + +struct xtm { + u_int8_t month; /* (1-12) */ + u_int8_t monthday; /* (1-31) */ + u_int8_t weekday; /* (1-7) */ + u_int8_t hour; /* (0-23) */ + u_int8_t minute; /* (0-59) */ + u_int8_t second; /* (0-59) */ + unsigned int dse; +}; + +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); +} + +/* + * 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. + * + * This is done in three separate functions so that the most expensive + * calculations are done last, in case a "simple match" can be found earlier. + */ +static inline unsigned int localtime_1(struct xtm *r, time_t time) +{ + unsigned int v, w; + + /* Each day has 86400s, so finding the hour/minute is actually easy. */ + v = time % 86400; + r->second = v % 60; + w = v / 60; + r->minute = w % 60; + r->hour = w / 60; + return v; +} + +static inline void localtime_2(struct xtm *r, time_t time) +{ + /* + * Here comes the rest (weekday, monthday). First, divide the SSTE + * by seconds-per-day to get the number of _days_ since the epoch. + */ + r->dse = time / 86400; + + /* 1970-01-01 (w=0) was a Thursday (4). */ + r->weekday = (4 + r->dse) % 7; +} + +static void localtime_3(struct xtm *r, time_t time) +{ + unsigned int year, i, w = r->dse; + + /* + * 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; +} + +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; + + /* + * We cannot use get_seconds() instead of __net_timestamp() here. + * Suppose you have two rules: + * 1. match before 13:00 + * 2. match after 13:00 + * If you match against processing time (get_seconds) it + * may happen that the same packet matches both rules if + * it arrived at the right moment before 13:00. + */ + if (skb->tstamp.tv64 == 0) + __net_timestamp((struct sk_buff *)skb); + + stamp = skb->tstamp.tv64; + do_div(stamp, NSEC_PER_SEC); + + if (info->flags & XT_TIME_LOCAL_TZ) + /* Adjust for local timezone */ + stamp -= 60 * sys_tz.tz_minuteswest; + + /* + * 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; + + packet_time = localtime_1(¤t_time, stamp); + + if (info->daytime_start < info->daytime_stop) { + if (packet_time < info->daytime_start || + packet_time > info->daytime_stop) + return false; + } else { + if (packet_time < info->daytime_start && + packet_time > info->daytime_stop) + return false; + } + + localtime_2(¤t_time, stamp); + + if (!(info->weekdays_match & (1 << current_time.weekday))) + return false; + + /* Do not spend time computing monthday if all days match anyway */ + if (info->monthdays_match != XT_TIME_ALL_MONTHDAYS) { + localtime_3(¤t_time, stamp); + if (!(info->monthdays_match & (1 << current_time.monthday))) + 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; + + if (info->daytime_start > XT_TIME_MAX_DAYTIME || + info->daytime_stop > XT_TIME_MAX_DAYTIME) { + printk(KERN_WARNING "xt_time: invalid argument - start or " + "stop time greater than 23:59:59\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"); - 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 ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: xt_time 2007-09-22 2007-09-22 15:58 ` Jan Engelhardt @ 2007-09-23 15:05 ` Patrick McHardy 2007-09-23 15:32 ` Jan Engelhardt 0 siblings, 1 reply; 11+ messages in thread From: Patrick McHardy @ 2007-09-23 15:05 UTC (permalink / raw) To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List Jan Engelhardt wrote: > Add the xt_time netfilter match > > This is ipt_time from POM-ng enhanced by the following: > > * xtables/ipv6 support > * second granularity for daytime > * day-of-month support (for example "match on the 15th of each month") > * match against UTC or local timezone > > Signed-off-by: Jan Engelhardt <jengelh@computergmbh.de> Applied, thanks Jan. Unfortunately gcc-4.2 seems to ignore casts from const -> non-const, so the __net_timestamp call causes this warning: net/netfilter/xt_time.c: In function 'xt_time_match': net/netfilter/xt_time.c:170: warning: passing argument 1 of '__net_timestamp' discards qualifiers from pointer target type Not sure how we could shut this up, I definitely don't want to make all match functions take non-const skb args. ip_queue has the same warning. > + localtime_2(¤t_time, stamp); > + > + if (!(info->weekdays_match & (1 << current_time.weekday))) > + return false; Shouldn't this do the same optimization as below? > + > + /* Do not spend time computing monthday if all days match anyway */ > + if (info->monthdays_match != XT_TIME_ALL_MONTHDAYS) { > + localtime_3(¤t_time, stamp); > + if (!(info->monthdays_match & (1 << current_time.monthday))) > + return false; > + } > + > + return true; > +} ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: xt_time 2007-09-22 2007-09-23 15:05 ` Patrick McHardy @ 2007-09-23 15:32 ` Jan Engelhardt 2007-09-23 16:20 ` Patrick McHardy 0 siblings, 1 reply; 11+ messages in thread From: Jan Engelhardt @ 2007-09-23 15:32 UTC (permalink / raw) To: Patrick McHardy; +Cc: Netfilter Developer Mailing List On Sep 23 2007 17:05, Patrick McHardy wrote: > >Unfortunately gcc-4.2 seems to ignore casts from const -> non-const, >so the __net_timestamp call causes this warning: > >net/netfilter/xt_time.c: In function 'xt_time_match': >net/netfilter/xt_time.c:170: warning: passing argument 1 of >'__net_timestamp' discards qualifiers from pointer target type > >Not sure how we could shut this up, I definitely don't want to make >all match functions take non-const skb args. ip_queue has the same >warning. Introduce some const_cast<> from C++ in C? Blame some GCC guys? :-) >> + localtime_2(¤t_time, stamp); >> + >> + if (!(info->weekdays_match & (1 << current_time.weekday))) >> + return false; > >Shouldn't this do the same optimization as below? No, it's not worth it. localtime_3 requires that localtime_2 be run before, so it would look like: if (info->weekdays_match != XT_TIME_ALL_WEEKDAYS && info->monthdays_match != XT_TIME_ALL_MONTHDAYS) { localtime_2(¤t_time, stamp); if (!(info->weekdays_match & (1 << current_time.weekday))) return false; if (info->monthdays_match != XT_TIME_ALL_MONTHDAYS) { localtime_3(¤t_time, stamp); if (!(info->monthdays_match & (1 << current_time.monthday))) return false; } } Since localtime_2() is miniscule, all this if'ing does not improve things at all. ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: xt_time 2007-09-22 2007-09-23 15:32 ` Jan Engelhardt @ 2007-09-23 16:20 ` Patrick McHardy 0 siblings, 0 replies; 11+ messages in thread From: Patrick McHardy @ 2007-09-23 16:20 UTC (permalink / raw) To: Jan Engelhardt; +Cc: Netfilter Developer Mailing List Jan Engelhardt wrote: > On Sep 23 2007 17:05, Patrick McHardy wrote: > >>>+ localtime_2(¤t_time, stamp); >>>+ >>>+ if (!(info->weekdays_match & (1 << current_time.weekday))) >>>+ return false; >> >>Shouldn't this do the same optimization as below? > > > No, it's not worth it. localtime_3 requires that localtime_2 be run > before, so it would look like: > > if (info->weekdays_match != XT_TIME_ALL_WEEKDAYS && > info->monthdays_match != XT_TIME_ALL_MONTHDAYS) { > localtime_2(¤t_time, stamp); > if (!(info->weekdays_match & (1 << current_time.weekday))) > return false; > if (info->monthdays_match != XT_TIME_ALL_MONTHDAYS) { > localtime_3(¤t_time, stamp); > if (!(info->monthdays_match & (1 << current_time.monthday))) > return false; > } > } > > Since localtime_2() is miniscule, all this if'ing does not improve > things at all. OK, thanks for the explanation. ^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2007-09-23 16:29 UTC | newest] Thread overview: 11+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2007-09-22 11:26 xt_time 2007-09-22 Jan Engelhardt 2007-09-22 11:28 ` xt_time 2007-09-22 (iptables) Jan Engelhardt 2007-09-23 15:08 ` Patrick McHardy 2007-09-22 15:19 ` xt_time 2007-09-22 Patrick McHardy 2007-09-22 15:38 ` Jan Engelhardt 2007-09-22 15:39 ` Patrick McHardy 2007-09-22 15:47 ` Jan Engelhardt 2007-09-22 15:58 ` Jan Engelhardt 2007-09-23 15:05 ` Patrick McHardy 2007-09-23 15:32 ` Jan Engelhardt 2007-09-23 16:20 ` Patrick McHardy
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).