1/*
2  This is a module which is used for time matching
3  It is using some modified code from dietlibc (localtime() function)
4  that you can find at http://www.fefe.de/dietlibc/
5  This file is distributed under the terms of the GNU General Public
6  License (GPL). Copies of the GPL can be obtained from: ftp://prep.ai.mit.edu/pub/gnu/GPL
7  2001-05-04 Fabrice MARIE <fabrice@netfilter.org> : initial development.
8  2001-21-05 Fabrice MARIE <fabrice@netfilter.org> : bug fix in the match code,
9  thanks to "Zeng Yu" <zengy@capitel.com.cn> for bug report.
10  2001-26-09 Fabrice MARIE <fabrice@netfilter.org> : force the match to be in
11  LOCAL_IN or PRE_ROUTING only.
12  2001-30-11 Fabrice : added the possibility to use the match in FORWARD/OUTPUT with a little hack,
13  added Nguyen Dang Phuoc Dong <dongnd@tlnet.com.vn> patch to support timezones.
14  2004-05-02 Fabrice : added support for date matching, from an idea of Fabien COELHO.
15*/
16
17#include <linux/module.h>
18#include <linux/skbuff.h>
19#include <linux/netfilter/x_tables.h>
20#include <linux/netfilter_ipv4/ipt_time.h>
21#include <linux/time.h>
22
23MODULE_AUTHOR("Fabrice MARIE <fabrice@netfilter.org>");
24MODULE_DESCRIPTION("Match arrival timestamp/date");
25MODULE_LICENSE("GPL");
26
27struct tm
28{
29	int tm_sec;                   /* Seconds.     [0-60] (1 leap second) */
30	int tm_min;                   /* Minutes.     [0-59] */
31	int tm_hour;                  /* Hours.       [0-23] */
32	int tm_mday;                  /* Day.         [1-31] */
33	int tm_mon;                   /* Month.       [0-11] */
34	int tm_year;                  /* Year - 1900.  */
35	int tm_wday;                  /* Day of week. [0-6] */
36	int tm_yday;                  /* Days in year.[0-365] */
37	int tm_isdst;                 /* DST.         [-1/0/1]*/
38
39	long int tm_gmtoff;           /* we don't care, we count from GMT */
40	const char *tm_zone;          /* we don't care, we count from GMT */
41};
42
43static void
44localtime(const u32 time, struct tm *r);
45
46static int
47match(const struct sk_buff *skb,
48      const struct net_device *in,
49      const struct net_device *out,
50      const struct xt_match *match,
51      const void *matchinfo,
52      int offset,
53      unsigned int protoff,
54      int *hotdrop)
55{
56	const struct ipt_time_info *info = matchinfo;   /* match info for rule */
57	struct tm currenttime;                          /* time human readable */
58	struct timeval tv;
59	u_int8_t days_of_week[7] = {1, 2, 4, 8, 16, 32, 64};
60	u_int16_t packet_time;
61
62	/* We might not have a timestamp, get one */
63	tv = ktime_to_timeval(skb->tstamp);
64	if (tv.tv_sec == 0) {
65		__net_timestamp((struct sk_buff *)skb);
66		tv = ktime_to_timeval(skb->tstamp);
67	}
68
69	/* Transform the timestamp of the packet, in a human readable form */
70	localtime(tv.tv_sec, &currenttime);
71
72	/* check if we match this timestamp, we start by the days... */
73	if ((days_of_week[currenttime.tm_wday] & info->days_match) != days_of_week[currenttime.tm_wday])
74		return 0; /* the day doesn't match */
75
76	/* ... check the time now */
77	packet_time = (currenttime.tm_hour * 600) + (currenttime.tm_min *60) + currenttime.tm_sec;
78	if ((packet_time < info->time_start) || (packet_time > info->time_stop))
79		return 0;
80
81	/* here we match ! */
82	return 1;
83}
84
85static int
86checkentry(const char *tablename,
87           const void *ip,
88	   const struct xt_match *match,
89           void *matchinfo,
90           unsigned int hook_mask)
91{
92	struct ipt_time_info *info = matchinfo;   /* match info for rule */
93
94	/* First, check that we are in the correct hooks */
95	if (hook_mask
96            & ~((1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_LOCAL_IN) | (1 << NF_IP_FORWARD) | (1 << NF_IP_LOCAL_OUT)))
97	{
98		printk("ipt_time: error, only valid for PRE_ROUTING, LOCAL_IN, FORWARD and OUTPUT)\n");
99		return 0;
100	}
101
102	/* Now check the coherence of the data ... */
103	if ((info->time_start > 86399) || (info->time_stop  > 86399))
104		{
105		printk(KERN_WARNING "ipt_time: invalid argument\n");
106		return 0;
107	}
108
109	return 1;
110}
111
112static struct xt_match time_match = {
113	.name = "time",
114	.family = AF_INET,
115	.match = match,
116	.matchsize = sizeof(struct ipt_time_info) + 8,
117	.checkentry = &checkentry,
118	.me = THIS_MODULE
119};
120
121static int __init init(void)
122{
123	return xt_register_match(&time_match);
124}
125
126static void __exit fini(void)
127{
128	xt_unregister_match(&time_match);
129}
130
131module_init(init);
132module_exit(fini);
133
134
135/* The part below is borowed and modified from dietlibc */
136
137/* seconds per day */
138#define SPD 24*60*60
139
140static void
141localtime(const u32 time, struct tm *r) {
142	u32 i, timep;
143	extern struct timezone sys_tz;
144	const unsigned int __spm[12] =
145		{ 0,
146		  (31),
147		  (31+28),
148		  (31+28+31),
149		  (31+28+31+30),
150		  (31+28+31+30+31),
151		  (31+28+31+30+31+30),
152		  (31+28+31+30+31+30+31),
153		  (31+28+31+30+31+30+31+31),
154		  (31+28+31+30+31+30+31+31+30),
155		  (31+28+31+30+31+30+31+31+30+31),
156		  (31+28+31+30+31+30+31+31+30+31+30),
157		};
158	register u32 work;
159
160	timep = time - (sys_tz.tz_minuteswest * 60);
161	work=timep%(SPD);
162	r->tm_sec=work%60; work/=60;
163	r->tm_min=work%60; r->tm_hour=work/60;
164	work=timep/(SPD);
165	r->tm_wday=(4+work)%7;
166	for (i=1970; ; ++i) {
167		register time_t k= (!(i%4) && ((i%100) || !(i%400)))?366:365;
168		if (work>k)
169			work-=k;
170		else
171			break;
172	}
173	r->tm_year=i-1900;
174	for (i=11; i && __spm[i]>work; --i) ;
175	r->tm_mon=i;
176	r->tm_mday=work-__spm[i]+1;
177}
178