contrib: fwd - initial C implementation of the uci firewall
[project/luci.git] / contrib / fwd / src / fwd_rules.c
1 /*
2 * fwd - OpenWrt firewall daemon - iptables rule set
3 *
4 * Copyright (C) 2009 Jo-Philipp Wich <xm@subsignal.org>
5 *
6 * The fwd program is free software: you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License version 2
8 * as published by the Free Software Foundation.
9 *
10 * The fwd program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with the fwd program. If not, see http://www.gnu.org/licenses/.
17 */
18
19
20 #include "fwd.h"
21 #include "fwd_addr.h"
22 #include "fwd_rules.h"
23
24 static void
25 fwd_ipt_rule_append(struct fwd_ipt_rulebuf *r, const char *fmt, ...)
26 {
27 int len = 0;
28 char buf[256]; buf[0] = 0;
29
30 va_list ap;
31 va_start(ap, fmt);
32 len = vsnprintf(buf, sizeof(buf), fmt, ap);
33 va_end(ap);
34
35 if( len > 0 )
36 {
37 r->buf = realloc(r->buf, r->len + len + 1);
38 memcpy(&r->buf[r->len], buf, len);
39 r->buf[r->len + len] = 0;
40 r->len += len;
41 }
42 }
43
44 static struct fwd_ipt_rulebuf * fwd_ipt_init(const char *table)
45 {
46 struct fwd_ipt_rulebuf *r;
47
48 if( (r = fwd_alloc_ptr(struct fwd_ipt_rulebuf)) != NULL )
49 {
50 fwd_ipt_rule_append(r, IPT " -t %s", table);
51 return r;
52 }
53
54 return NULL;
55 }
56
57 static void fwd_ipt_add_srcport(
58 struct fwd_ipt_rulebuf *r, struct fwd_portrange *p
59 ) {
60 if( p != NULL )
61 {
62 if( p->min < p->max )
63 fwd_ipt_rule_append(r, " --sport %u:%u", p->min, p->max);
64 else
65 fwd_ipt_rule_append(r, " --sport %u", p->min);
66 }
67 }
68
69 static void fwd_ipt_add_destport(
70 struct fwd_ipt_rulebuf *r, struct fwd_portrange *p
71 ) {
72 if( p != NULL )
73 {
74 if( p->min < p->max )
75 fwd_ipt_rule_append(r, " --dport %u:%u", p->min, p->max);
76 else
77 fwd_ipt_rule_append(r, " --dport %u", p->min);
78 }
79 }
80
81 static void fwd_ipt_add_proto(
82 struct fwd_ipt_rulebuf *r, struct fwd_proto *p
83 ) {
84 if( p != NULL )
85 {
86 switch( p->type )
87 {
88 case FWD_PR_TCPUDP:
89 fwd_ipt_rule_append(r, " -p tcp -p udp");
90 break;
91
92 case FWD_PR_TCP:
93 fwd_ipt_rule_append(r, " -p tcp");
94 break;
95
96 case FWD_PR_UDP:
97 fwd_ipt_rule_append(r, " -p udp");
98 break;
99
100 case FWD_PR_ICMP:
101 fwd_ipt_rule_append(r, " -p icmp");
102 break;
103
104 case FWD_PR_ALL:
105 fwd_ipt_rule_append(r, " -p all");
106 break;
107
108 case FWD_PR_CUSTOM:
109 fwd_ipt_rule_append(r, " -p %u", p->proto);
110 break;
111 }
112 }
113 }
114
115 static void fwd_ipt_add_srcaddr(
116 struct fwd_ipt_rulebuf *r, struct fwd_cidr *c
117 ) {
118 if( c != NULL )
119 {
120 if( c->prefix < 32 )
121 fwd_ipt_rule_append(r, " -s %s/%u",
122 inet_ntoa(c->addr), c->prefix);
123 else
124 fwd_ipt_rule_append(r, " -s %s", inet_ntoa(c->addr));
125 }
126 }
127
128 static void fwd_ipt_add_destaddr(
129 struct fwd_ipt_rulebuf *r, struct fwd_cidr *c
130 ) {
131 if( c != NULL )
132 {
133 if( c->prefix < 32 )
134 fwd_ipt_rule_append(r, " -d %s/%u",
135 inet_ntoa(c->addr), c->prefix);
136 else
137 fwd_ipt_rule_append(r, " -d %s", inet_ntoa(c->addr));
138 }
139 }
140
141 static void fwd_ipt_add_srcmac(
142 struct fwd_ipt_rulebuf *r, struct fwd_mac *m
143 ) {
144 if( m != NULL )
145 {
146 fwd_ipt_rule_append(r,
147 " -m mac --mac-source %02x:%02x:%02x:%02x:%02x:%02x",
148 m->mac[0], m->mac[1], m->mac[2],
149 m->mac[3], m->mac[4], m->mac[5]);
150 }
151 }
152
153 static void fwd_ipt_add_icmptype(
154 struct fwd_ipt_rulebuf *r, struct fwd_icmptype *i
155 ) {
156 if( i != NULL )
157 {
158 if( i->name )
159 fwd_ipt_rule_append(r, " --icmp-type %s", i->name);
160 else if( i->code > -1 )
161 fwd_ipt_rule_append(r, " --icmp-type %u/%u", i->type, i->code);
162 else
163 fwd_ipt_rule_append(r, " --icmp-type %u", i->type);
164 }
165 }
166
167 static void fwd_ipt_add_dnat_target(
168 struct fwd_ipt_rulebuf *r, struct fwd_cidr *c, struct fwd_portrange *p
169 ) {
170 if( c != NULL )
171 {
172 fwd_ipt_rule_append(r, " -j DNAT --to-destination %s",
173 inet_ntoa(c->addr));
174
175 if( (p != NULL) && (p->min < p->max) )
176 fwd_ipt_rule_append(r, ":%u-%u", p->min, p->max);
177 else if( p != NULL )
178 fwd_ipt_rule_append(r, ":%u", p->min);
179 }
180 }
181
182 static void fwd_ipt_exec(struct fwd_ipt_rulebuf *r)
183 {
184 if( r->len )
185 printf("%s\n", r->buf);
186
187 fwd_free_ptr(r->buf);
188 fwd_free_ptr(r);
189 }
190
191 static const char * fwd_str_policy(enum fwd_policy pol)
192 {
193 return (pol == FWD_P_ACCEPT ? "ACCEPT" : "DROP");
194 }
195
196 static const char * fwd_str_target(enum fwd_policy pol)
197 {
198 switch(pol)
199 {
200 case FWD_P_ACCEPT:
201 return "ACCEPT";
202
203 case FWD_P_REJECT:
204 return "REJECT";
205
206 default:
207 return "DROP";
208 }
209
210 return "DROP";
211 }
212
213
214 static void fwd_ipt_defaults_create(struct fwd_data *d)
215 {
216 struct fwd_defaults *def = &d->section.defaults;
217
218 /* policies */
219 fwd_ipt_exec_format("filter", " -P INPUT %s", fwd_str_policy(def->input));
220 fwd_ipt_exec_format("filter", " -P OUTPUT %s", fwd_str_policy(def->output));
221 fwd_ipt_exec_format("filter", " -P FORWARD %s", fwd_str_policy(def->forward));
222
223 /* invalid state drop */
224 if( def->drop_invalid )
225 {
226 fwd_ipt_exec_format("filter", " -A INPUT --state INVALID -j DROP");
227 fwd_ipt_exec_format("filter", " -A OUTPUT --state INVALID -j DROP");
228 fwd_ipt_exec_format("filter", " -A FORWARD --state INVALID -j DROP");
229 }
230
231 /* default accept related */
232 fwd_ipt_exec_format("filter", " -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT");
233 fwd_ipt_exec_format("filter", " -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT");
234 fwd_ipt_exec_format("filter", " -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT");
235
236 /* default accept on lo */
237 fwd_ipt_exec_format("filter", " -A INPUT -i lo -j ACCEPT");
238 fwd_ipt_exec_format("filter", " -A OUTPUT -o lo -j ACCEPT");
239
240 /* syn flood protection */
241 if( def->syn_flood )
242 {
243 fwd_ipt_exec_format("filter", " -N syn_flood");
244
245 fwd_ipt_exec_format("filter",
246 " -A syn_flood -p tcp --syn -m limit --limit %i/second"
247 " --limit-burst %i -j RETURN",
248 def->syn_rate, def->syn_burst);
249
250 fwd_ipt_exec_format("filter", " -A syn_flood -j DROP");
251 fwd_ipt_exec_format("filter", " -A INPUT -p tcp --syn -j syn_flood");
252 }
253
254 /* standard input/output/forward chain */
255 fwd_ipt_exec_format("filter", " -N input");
256 fwd_ipt_exec_format("filter", " -N output");
257 fwd_ipt_exec_format("filter", " -N forward");
258 fwd_ipt_exec_format("filter", " -A INPUT -j input");
259 fwd_ipt_exec_format("filter", " -A OUTPUT -j output");
260 fwd_ipt_exec_format("filter", " -A FORWARD -j forward");
261
262 /* standard reject chain */
263 fwd_ipt_exec_format("filter", " -N reject");
264 fwd_ipt_exec_format("filter", " -A reject -p tcp -j REJECT --reject-with tcp-reset");
265 fwd_ipt_exec_format("filter", " -A reject -j REJECT --reject-with icmp-port-unreachable");
266 }
267
268 static void fwd_ipt_zone_create(struct fwd_data *d)
269 {
270 struct fwd_zone *z = &d->section.zone;
271
272 if( !strcmp(z->name, "loopback") )
273 return;
274
275 fwd_ipt_exec_format("filter", " -N zone_%s", z->name);
276 fwd_ipt_exec_format("filter", " -N zone_%s_forward", z->name);
277 fwd_ipt_exec_format("filter", " -N zone_%s_ACCEPT", z->name);
278 fwd_ipt_exec_format("filter", " -N zone_%s_REJECT", z->name);
279 fwd_ipt_exec_format("filter", " -N zone_%s_DROP", z->name);
280 fwd_ipt_exec_format("filter", " -N zone_%s_MSSFIX", z->name);
281
282 if( z->forward != FWD_P_UNSPEC )
283 fwd_ipt_exec_format("filter", " -A zone_%s_forward -j zone_%s_%s",
284 z->name, z->name, fwd_str_target(z->forward));
285
286 if( z->input != FWD_P_UNSPEC )
287 fwd_ipt_exec_format("filter", " -A zone_%s -j zone_%s_%s",
288 z->name, z->name, fwd_str_target(z->input));
289
290 if( z->output != FWD_P_UNSPEC )
291 fwd_ipt_exec_format("filter", " -A output -j zone_%s_%s",
292 z->name, fwd_str_target(z->output));
293
294 fwd_ipt_exec_format("nat", " -N zone_%s_nat", z->name);
295 fwd_ipt_exec_format("nat", " -N zone_%s_prerouting", z->name);
296 fwd_ipt_exec_format("raw", " -N zone_%s_notrack", z->name);
297
298 if( z->masq )
299 fwd_ipt_exec_format("nat", " -A POSTROUTING -j zone_%s_nat",
300 z->name);
301
302 if( z->mtu_fix )
303 fwd_ipt_exec_format("filter", " -A FORWARD -j zone_%s_MSSFIX",
304 z->name);
305 }
306
307 static void fwd_ipt_forwarding_create(struct fwd_data *d)
308 {
309 struct fwd_forwarding *f = &d->section.forwarding;
310 struct fwd_ipt_rulebuf *b;
311
312 b = fwd_ipt_init("filter");
313
314 if( f->src )
315 fwd_ipt_add_format(b, " -I zone_%s_forward 1", f->src->name);
316 else
317 fwd_ipt_add_format(b, " -I forward 1");
318
319 if( f->dest )
320 fwd_ipt_add_format(b, " -j zone_%s_ACCEPT", f->dest->name);
321 else
322 fwd_ipt_add_format(b, " -j ACCEPT");
323
324 fwd_ipt_exec(b);
325 }
326
327 static void fwd_ipt_redirect_create(struct fwd_data *d)
328 {
329 struct fwd_redirect *r = &d->section.redirect;
330 struct fwd_ipt_rulebuf *b;
331
332 b = fwd_ipt_init("nat");
333 fwd_ipt_add_format(b, " -A zone_%s_prerouting", r->src->name);
334 fwd_ipt_add_proto(b, r->proto);
335 fwd_ipt_add_srcaddr(b, r->src_ip);
336 fwd_ipt_add_srcport(b, r->src_port);
337 fwd_ipt_add_destport(b, r->src_dport);
338 fwd_ipt_add_srcmac(b, r->src_mac);
339 fwd_ipt_add_dnat_target(b, r->dest_ip, r->dest_port);
340 fwd_ipt_exec(b);
341
342 b = fwd_ipt_init("nat");
343 fwd_ipt_add_format(b, " -I zone_%s_forward 1", r->src->name);
344 fwd_ipt_add_proto(b, r->proto);
345 fwd_ipt_add_srcmac(b, r->src_mac);
346 fwd_ipt_add_srcaddr(b, r->src_ip);
347 fwd_ipt_add_srcport(b, r->src_port);
348 fwd_ipt_add_destaddr(b, r->dest_ip);
349 fwd_ipt_add_destport(b, r->dest_port);
350 fwd_ipt_add_format(b, " -j ACCEPT");
351 fwd_ipt_exec(b);
352 }
353
354 static void fwd_ipt_rule_create(struct fwd_data *d)
355 {
356 struct fwd_rule *r = &d->section.rule;
357 struct fwd_ipt_rulebuf *b;
358
359 b = fwd_ipt_init("filter");
360
361 if( r->dest )
362 fwd_ipt_add_format(b, " -A zone_%s_forward", r->src->name);
363 else
364 fwd_ipt_add_format(b, " -A zone_%s", r->src->name);
365
366 fwd_ipt_add_proto(b, r->proto);
367 fwd_ipt_add_icmptype(b, r->icmp_type);
368 fwd_ipt_add_srcmac(b, r->src_mac);
369 fwd_ipt_add_srcaddr(b, r->src_ip);
370 fwd_ipt_add_srcport(b, r->src_port);
371 fwd_ipt_add_destaddr(b, r->dest_ip);
372 fwd_ipt_add_destport(b, r->dest_port);
373
374 if( r->dest )
375 fwd_ipt_add_format(b, " -j zone_%s_%s",
376 r->dest->name, fwd_str_target(r->target));
377 else
378 fwd_ipt_add_format(b, " -j %s", fwd_str_target(r->target));
379
380 fwd_ipt_exec(b);
381 }
382
383
384 static struct fwd_network_list *
385 fwd_lookup_network(struct fwd_network_list *n, const char *net)
386 {
387 struct fwd_network_list *e;
388
389 if( n != NULL )
390 for( e = n; e; e = e->next )
391 if( !strcmp(e->name, net) )
392 return e;
393
394 return NULL;
395 }
396
397 static struct fwd_addr_list *
398 fwd_lookup_addr(struct fwd_addr_list *a, const char *ifname)
399 {
400 struct fwd_addr_list *e;
401
402 if( a != NULL )
403 for( e = a; e; e = e->next )
404 if( !strcmp(e->ifname, ifname) )
405 return e;
406
407 return NULL;
408 }
409
410
411 void fwd_ipt_build_ruleset(struct fwd_handle *h)
412 {
413 struct fwd_data *e;
414
415 for( e = h->conf; e; e = e->next )
416 {
417 switch(e->type)
418 {
419 case FWD_S_DEFAULTS:
420 printf("\n## DEFAULTS\n");
421 fwd_ipt_defaults_create(e);
422 break;
423
424 case FWD_S_ZONE:
425 printf("\n## ZONE %s\n", e->section.zone.name);
426 fwd_ipt_zone_create(e);
427 break;
428
429 case FWD_S_FORWARD:
430 printf("\n## FORWARD %s -> %s\n",
431 e->section.forwarding.src
432 ? e->section.forwarding.src->name : "(all)",
433 e->section.forwarding.dest
434 ? e->section.forwarding.dest->name : "(all)");
435 fwd_ipt_forwarding_create(e);
436 break;
437
438 case FWD_S_REDIRECT:
439 printf("\n## REDIRECT %s\n", e->section.forwarding.src->name);
440 fwd_ipt_redirect_create(e);
441 break;
442
443 case FWD_S_RULE:
444 printf("\n## RULE %s\n", e->section.rule.src->name);
445 fwd_ipt_rule_create(e);
446 break;
447
448 case FWD_S_INCLUDE:
449 printf("\n## INCLUDE %s\n", e->section.include.path);
450 break;
451 }
452 }
453 }
454
455 void fwd_ipt_addif(struct fwd_handle *h, const char *net)
456 {
457 struct fwd_data *e;
458 struct fwd_zone *z;
459 struct fwd_addr_list *a;
460 struct fwd_network_list *n;
461
462 for( e = h->conf; e; e = e->next )
463 {
464 if( (e->type != FWD_S_ZONE) ||
465 !(n = fwd_lookup_network(e->section.zone.networks, net)) ||
466 !(a = fwd_lookup_addr(h->addrs, n->ifname)) )
467 continue;
468
469 z = &e->section.zone;
470
471 printf("\n## NETWORK %s (%s - %s/%u)\n",
472 n->name, n->ifname,
473 inet_ntoa(a->ipaddr.v4), a->prefix
474 );
475
476 fwd_ipt_exec_format("filter", " -A input -i %s -j zone_%s",
477 n->ifname, z->name);
478
479 fwd_ipt_exec_format("filter",
480 " -I zone_%s_MSSFIX 1 -o %s -p tcp --tcp-flags SYN,RST SYN"
481 " -j TCPMSS --clamp-mss-to-pmtu",
482 z->name, n->ifname);
483
484 fwd_ipt_exec_format("filter", " -I zone_%s_ACCEPT 1 -o %s -j ACCEPT",
485 z->name, n->ifname);
486
487 fwd_ipt_exec_format("filter", " -I zone_%s_DROP 1 -o %s -j DROP",
488 z->name, n->ifname);
489
490 fwd_ipt_exec_format("filter", " -I zone_%s_REJECT 1 -o %s -j reject",
491 z->name, n->ifname);
492
493 fwd_ipt_exec_format("filter", " -I zone_%s_ACCEPT 1 -i %s -j ACCEPT",
494 z->name, n->ifname);
495
496 fwd_ipt_exec_format("filter", " -I zone_%s_DROP 1 -i %s -j DROP",
497 z->name, n->ifname);
498
499 fwd_ipt_exec_format("filter", " -I zone_%s_REJECT 1 -i %s -j reject",
500 z->name, n->ifname);
501
502 fwd_ipt_exec_format("filter",
503 " -I zone_%s_nat 1 -t nat -o %s -j MASQUERADE",
504 z->name, n->ifname);
505
506 fwd_ipt_exec_format("filter",
507 " -I PREROUTING 1 -t nat -i %s -j zone_%s_prerouting",
508 n->ifname, z->name);
509
510 fwd_ipt_exec_format("filter", " -A forward -i %s -j zone_%s_forward",
511 n->ifname, z->name);
512
513 fwd_ipt_exec_format("raw", " -I PREROUTING 1 -i %s -j zone_%s_notrack",
514 n->ifname, z->name);
515 }
516 }
517