Added extrarules to iptables to get UDP request.
[tomato.git] / release / src / router / rc / restrict.c
blobdb6a5447b3aba6c3b625ca5436f223cb012ffaf8
1 /*
3 Tomato Firmware
4 Copyright (C) 2006-2009 Jonathan Zarate
5 Portions rebuild by victek@tomato.raf, 2013. String chain restriction
7 */
9 #include "rc.h"
10 #include <time.h>
11 #include <string.h>
13 #define MAX_NRULES 50
15 static inline void unsched_restrictions(void)
17 system("cru d rcheck");
20 void sched_restrictions(void)
22 system("rcheck");
25 static int in_sched(int now_mins, int now_dow, int sched_begin, int sched_end, int sched_dow)
27 // all day
28 if ((sched_begin < 0) || (sched_end < 0)) {
29 return (sched_dow & now_dow) != 0;
32 // simple min to max
33 if (sched_begin < sched_end) {
34 return (((sched_dow & now_dow) != 0) && (now_mins >= sched_begin) && (now_mins < sched_end));
37 // 15:00 - 01:00 = 15:00 Sun to 01:00 Mon
38 // 12:00 - 12:00 = 12:00 Sun to 12:00 Mon
40 if ((now_dow & sched_dow) != 0) {
41 if (now_mins >= sched_begin) return 1;
44 // ror now_dow, 1
45 if (now_dow == 1) now_dow = (1 << 6);
46 else now_dow >>= 1;
48 if ((now_dow & sched_dow) != 0) {
49 if (now_mins < sched_end) return 1;
52 // printf("now_mins=%d sched_end=%d now_dow=%d sched_dow=%d\n", now_mins, sched_end, now_dow, sched_dow);
53 return 0;
56 static int radio_on(int idx, int unit, int subunit, void *param)
58 return nvram_match(wl_nvname("radio", unit, 0), "1");
61 int rcheck_main(int argc, char *argv[])
63 char buf[256];
64 char *p;
65 int sched_begin;
66 int sched_end;
67 int sched_dow;
68 time_t now;
69 struct tm *tms;
70 int now_dow;
71 int now_mins;
72 int n;
73 int nrule;
74 char comp;
75 int insch;
76 unsigned long long activated;
77 int count;
78 int radio;
79 int r;
80 #ifdef TCONFIG_IPV6
81 int r6;
82 #endif
83 #ifdef LINUX26
84 int ipt_active;
85 #endif
87 if (!nvram_contains_word("log_events", "acre")) {
88 setlogmask(LOG_MASK(LOG_EMERG)); // can't set to 0
91 simple_lock("restrictions");
93 now = time(0);
94 if (now < Y2K) {
95 if (!nvram_match("rrules_timewarn", "1")) {
96 nvram_set("rrules_timewarn", "1");
97 syslog(LOG_INFO, "Time not yet set. Only \"all day, everyday\" restrictions will be activated.");
99 now_mins = now_dow = 0;
101 else {
102 tms = localtime(&now);
103 now_dow = 1 << tms->tm_wday;
104 now_mins = (tms->tm_hour * 60) + tms->tm_min;
107 #ifdef LINUX26
108 ipt_active = 0;
109 #endif
111 activated = strtoull(nvram_safe_get("rrules_activated"), NULL, 16);
112 count = 0;
113 radio = foreach_wif(0, NULL, radio_on) ? -1 : -2;
114 for (nrule = 0; nrule < MAX_NRULES; ++nrule) {
115 sprintf(buf, "rrule%d", nrule);
116 if ((p = nvram_get(buf)) == NULL) continue;
117 if (sscanf(p, "%d|%d|%d|%d|%c", &n, &sched_begin, &sched_end, &sched_dow, &comp) != 5) continue;
118 if (n == 0) continue;
120 ++count;
122 if (now < Y2K) {
123 if ((sched_begin >= 0) || (sched_end >= 0) || (sched_dow != 0x7F)) continue;
124 insch = 1;
126 else {
127 insch = in_sched(now_mins, now_dow, sched_begin, sched_end, sched_dow);
130 #ifdef LINUX26
131 if ((insch) && (comp != '~'))
132 ++ipt_active;
133 #endif
135 n = 1 << nrule;
136 if ((insch) == ((activated & n) != 0)) {
137 continue;
140 syslog(LOG_INFO, "%sctivating rule %d", insch ? "A" : "Dea", nrule);
142 if (comp == '~') {
143 if ((radio != 0) && (radio != -2)) radio = !insch;
145 else {
146 sprintf(buf, "r%s%02d", (comp != '|') ? "dev" : "res", nrule);
148 r = eval("iptables", "-D", "restrict", "-j", buf);
149 if (insch) {
150 // ignore error above (if any)
152 r = eval("iptables", "-A", "restrict", "-j", buf);
155 #ifdef TCONFIG_IPV6
156 r6 = eval("ip6tables", "-D", "restrict", "-j", buf);
157 if (ipv6_enabled()) {
158 if (insch) {
159 // ignore error above (if any)
161 r6 = eval("ip6tables", "-A", "restrict", "-j", buf);
163 r |= r6;
165 #endif
167 if (r != 0) {
168 syslog(LOG_ERR, "Iptables: %sactivating chain \"%s\" failed. Retrying in 15 minutes.",
169 insch ? "" : "de", buf);
170 continue;
174 if (insch) activated |= n;
175 else activated &= ~n;
178 sprintf(buf, "%llx", activated);
179 nvram_set("rrules_activated", buf);
181 if (count > 0) {
182 if ((argc != 2) || (strcmp(argv[1], "--cron") != 0)) {
183 system("cru a rcheck '*/15 * * * * rcheck --cron'");
186 else {
187 unsched_restrictions();
190 if (radio >= 0) {
191 nvram_set("rrules_radio", radio ? "0" : "1");
192 #if 1
193 // changed for dual radio support
194 _dprintf("%s: radio = %d\n", __FUNCTION__, radio);
195 eval("radio", radio ? "on" : "off");
196 #else
197 if (get_radio() != radio) {
198 _dprintf("%s: radio = %d\n", __FUNCTION__, radio);
199 eval("radio", radio ? "on" : "off");
201 else {
202 _dprintf("%s: no radio change = %d\n", __FUNCTION__, radio);
204 #endif
207 #ifdef LINUX26
208 allow_fastnat("restrictions", (ipt_active == 0));
209 try_enabling_fastnat();
210 #endif
211 simple_unlock("restrictions");
212 return 0;
215 void ipt_restrictions(void)
217 char buf[8192];
218 char *p, *q;
219 int n;
220 char *comps, *matches, *http;
221 int nrule;
222 int blockall;
223 char reschain[32];
224 char devchain[32];
225 char strchain[32];
226 char nextchain[32];
227 int need_web;
228 char *pproto;
229 char *dir;
230 char *pport;
231 int proto;
232 char *ipp2p;
233 char *layer7;
234 char *addr_type, *addr;
235 char app[256];
236 char ports[256];
237 char iptaddr[192];
238 int http_file;
239 int ex;
240 int first;
241 int v4v6_ok;
243 need_web = 0;
244 first = 1;
245 nvram_unset("rrules_timewarn");
246 nvram_set("rrules_radio", "-1");
247 unsched_restrictions();
249 for (nrule = 0; nrule < MAX_NRULES; ++nrule) {
250 sprintf(buf, "rrule%d", nrule);
251 if ((p = nvram_get(buf)) == NULL) continue;
252 if (strlen(p) >= sizeof(buf)) continue;
253 strcpy(buf, p);
255 if ((vstrsep(buf, "|",
256 &q, // 0/1
257 &p, &p, &p, // time (ignored)
258 &comps, //
259 &matches, //
260 &http, //
261 &p // http file match
262 ) != 8) || (*q != '1')) continue;
263 http_file = atoi(p);
265 if (comps[0] == '~') {
266 // a wireless disable rule, skip
267 continue;
270 if (first) {
271 first = 0;
273 ip46t_write(":restrict - [0:0]\n");
274 #ifdef TCONFIG_IPV6
275 if (*wan6face)
276 ip6t_write("-A FORWARD -o %s -j restrict\n",
277 wan6face);
278 #endif
279 for (n = 0; n < wanfaces.count; ++n) {
280 if (*(wanfaces.iface[n].name)) {
281 ipt_write("-A FORWARD -o %s -j restrict\n",
282 wanfaces.iface[n].name);
285 // Only mess with DNS requests that are coming in on INPUT
286 ip46t_write("-I INPUT 1 -p udp --dport 53 -j restrict\n");
289 sprintf(reschain, "rres%02d", nrule);
290 ip46t_write(":%s - [0:0]\n", reschain);
292 blockall = 1;
294 while ((q = strsep(&matches, ">")) != NULL) {
295 n = vstrsep(q, "<", &pproto, &dir, &pport, &ipp2p, &layer7, &addr_type, &addr);
296 if (n == 5) {
297 // fixup for backward compatibility
298 addr_type = "0";
300 else if (n != 7) continue;
302 if ((*dir != 'a') && (*dir != 's') && (*dir != 'd') && (*dir != 'x')) continue;
304 // p2p, layer7
305 if (!ipt_ipp2p(ipp2p, app)) {
306 if (ipt_layer7(layer7, app) == -1) continue;
308 #ifdef TCONFIG_IPV6
309 v4v6_ok = ((*app) ? 0 : IPT_V6) | IPT_V4;
310 #else
311 v4v6_ok = IPT_V4;
312 #endif
314 // dest ip/domain address
315 if ((*addr_type == '1') || (*addr_type == '2')) {
316 v4v6_ok = ipt_addr(iptaddr, sizeof(iptaddr), addr, (*addr_type == '1') ? "dst" : "src", v4v6_ok, (v4v6_ok == IPT_V4), "restrictions", NULL);
317 if (!v4v6_ok)
318 continue;
320 else {
321 iptaddr[0] = 0;
324 blockall = 0;
326 // proto & ports
327 proto = atoi(pproto);
328 if (proto <= -2) {
329 // shortcut if any proto+any port
330 ip46t_flagged_write(v4v6_ok, "-A %s %s %s -j %s\n", reschain, iptaddr, app, chain_out_drop);
331 continue;
333 else if ((proto == 6) || (proto == 17) || (proto == -1)) {
334 if ((*dir != 'a') && (*pport)) {
335 if ((*dir == 'x') || (strchr(pport, ','))) {
336 // use multiport for multiple ports or src-or-dst type matches
337 snprintf(ports, sizeof(ports), "-m multiport --%sports %s", (*dir == 'x') ? "" : dir, pport);
339 else {
340 // else, use built-in
341 snprintf(ports, sizeof(ports), "--%sport %s", dir, pport);
344 else {
345 ports[0] = 0;
347 if (proto != 17)
348 ip46t_flagged_write(v4v6_ok, "-A %s -p tcp %s %s %s -j %s\n", reschain, ports, iptaddr, app, chain_out_drop);
349 if (proto != 6)
350 ip46t_flagged_write(v4v6_ok, "-A %s -p udp %s %s %s -j %s\n", reschain, ports, iptaddr, app, chain_out_drop);
352 else {
353 ip46t_flagged_write(v4v6_ok, "-A %s -p %d %s %s -j %s\n", reschain, proto, iptaddr, app, chain_out_drop);
357 // Build chain to perform string matching
358 sprintf(strchain, "rstr%02d", nrule);
359 ip46t_write(":%s - [0:0]\n", strchain);
361 // Multiport match for ports 53,80,443 goto strchain
362 ip46t_write("-A %s -p tcp -m multiport --dports 53,80,443 -j %s\n", reschain, strchain);
363 ip46t_write("-A %s -p udp --dport 53 -j %s\n", reschain, strchain);
365 p = http;
366 while (*p) {
367 if ((*p == '\t') || (*p == '\r') || (*p == '\n') || (*p == '"')) *p = ' ';
368 ++p;
370 while ((n = strlen(http)) > 0) {
371 if (n >= 511) {
372 p = http + 510;
373 while ((p > http) && (*p != ' ')) --p;
374 if (p <= http) {
375 // too long
376 break;
378 *p = 0;
380 else p = NULL;
382 // Trim trailing whitespace from http
383 char *p2 = http + strlen(http) - 1;
384 while (*p2 == ' ')
386 *p2-- = '\0';
389 // Split the string delimited by whitespace. Each substring should be a new iptables entry.
390 char delim[] = " ";
391 p2 = strtok(http, delim);
392 while (p2 != NULL)
394 ip46t_write("-I %s 1 -p tcp -m string --string \"%s\" --algo bm --from 1 --to 600 -j %s\n", strchain, p2, chain_out_reject);
395 ip46t_write("-I %s 1 -p udp -m string --string \"%s\" --algo bm --from 1 --to 600 -j REJECT\n", strchain, p2);
396 p2 = strtok(NULL, delim);
399 need_web = 1;
400 blockall = 0;
401 if (p == NULL) break;
402 http = p + 1;
407 app[0] = 0;
408 if (http_file & 1) strcat(app, ".ocx$ .cab$ ");
409 if (http_file & 2) strcpy(app, ".swf$ ");
410 if (http_file & 4) strcat(app, ".class$ .jar$");
411 if (app[0]) {
412 ip46t_write("-A %s -p tcp -m multiport --dports %s -m web --path \"%s\" -j %s\n",
413 reschain, nvram_safe_get("rrulewp"), app, chain_out_reject);
414 need_web = 1;
415 blockall = 0;
418 if (*comps) {
419 if (blockall) {
420 ip46t_write("-F %s\n", reschain); // https://github.com/ReliefLabs/EasyTomato/
421 ip46t_write("-X %s\n", reschain); // chain not needed
422 sprintf(nextchain, "-j %s", chain_out_drop);
424 else {
425 sprintf(nextchain, "-g %s", reschain);
428 ex = 0;
429 sprintf(devchain, "rdev%02d", nrule);
430 ip46t_write(":%s - [0:0]\n", devchain);
431 while ((q = strsep(&comps, ">")) != NULL) {
432 if (*q == 0) continue;
433 if (*q == '!') {
434 ex = 1;
435 continue;
437 #ifdef TCONFIG_IPV6
438 v4v6_ok = IPT_V6 | IPT_V4;
439 #else
440 v4v6_ok = IPT_V4;
441 #endif
442 if (sscanf(q, "%2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx",
443 iptaddr, iptaddr, iptaddr, iptaddr, iptaddr, iptaddr) == 6) {
444 snprintf(iptaddr, sizeof(iptaddr), "-m mac --mac-source %s", q);
446 else {
447 v4v6_ok = ipt_addr(iptaddr, sizeof(iptaddr), q, "src", v4v6_ok, (v4v6_ok == IPT_V4), "restrictions", "filtering");
448 if (!v4v6_ok)
449 continue;
451 ip46t_flagged_write(v4v6_ok,
452 "-A %s %s %s\n", devchain, iptaddr, ex ? "-j RETURN" : nextchain);
455 if (ex) {
456 ip46t_write("-A %s %s\n", devchain, nextchain);
459 else if (blockall) {
460 ip46t_write("-A %s -j %s\n", reschain, chain_out_drop);
464 nvram_set("rrules_activated", "0");
466 if (need_web)
467 #ifdef LINUX26
468 modprobe("xt_web");
469 #else
470 modprobe("ipt_web");
471 #endif