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