6f569fd27ab9333f12c934cb1548dd73f59c6b82
[project/luci.git] / contrib / fwd / src / fwd_xtables.c
1 /*
2  * fwd - OpenWrt firewall daemon - libiptc/libxtables interface
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_xtables.h"
22
23
24 /* Required by certain extensions like SNAT and DNAT */
25 int kernel_version;
26
27 extern void
28 get_kernel_version(void) {
29         static struct utsname uts;
30         int x = 0, y = 0, z = 0;
31
32         if (uname(&uts) == -1) {
33                 fprintf(stderr, "Unable to retrieve kernel version.\n");
34                 xtables_free_opts(1);
35                 exit(1);
36         }
37
38         sscanf(uts.release, "%d.%d.%d", &x, &y, &z);
39         kernel_version = LINUX_VERSION(x, y, z);
40 }
41
42
43 static void xt_exit_error(enum xtables_exittype status, const char *msg, ...)
44 {
45         va_list ap;
46         va_start(ap, msg);
47         vprintf(msg, ap);
48         va_end(ap);     
49         exit(1);
50 }
51
52 void fwd_xt_init(void)
53 {
54         struct xtables_globals xt_globals = {
55                 .option_offset = 0,
56                 .program_version = IPTABLES_VERSION,
57                 .opts = 0,
58                 .orig_opts = 0,
59                 .exit_err = (void *)&xt_exit_error,
60         };
61
62         xtables_init();
63         xtables_set_nfproto(NFPROTO_IPV4);
64         xtables_set_params(&xt_globals);
65 }
66
67
68 struct fwd_xt_rule * fwd_xt_init_rule(struct iptc_handle *h)
69 {
70         struct fwd_xt_rule *r;
71
72         if( (r = fwd_alloc_ptr(struct fwd_xt_rule)) != NULL )
73         {
74                 if( (r->entry = fwd_alloc_ptr(struct ipt_entry)) != NULL )
75                 {
76                         r->iptc = h;
77                         return r;
78                 }
79         }
80
81         fwd_free_ptr(r);
82         return NULL;
83 }
84
85
86 void fwd_xt_parse_proto(
87         struct fwd_xt_rule *r, struct fwd_proto *p, int inv
88 ) {
89         if( p != NULL )
90         {
91                 switch(p->type)
92                 {
93                         case FWD_PR_TCP:
94                                 r->entry->ip.proto = 6;
95                                 break;
96
97                         case FWD_PR_UDP:
98                                 r->entry->ip.proto = 17;
99                                 break;
100
101                         case FWD_PR_ICMP:
102                                 r->entry->ip.proto = 1;
103                                 break;
104
105                         case FWD_PR_CUSTOM:
106                                 r->entry->ip.proto = p->proto;
107                                 break;
108
109                         case FWD_PR_ALL:
110                         case FWD_PR_TCPUDP:
111                                 r->entry->ip.proto = 0;
112                                 break;
113                 }
114
115                 if( inv )
116                         r->entry->ip.invflags |= IPT_INV_PROTO;
117         }
118 }
119
120 void fwd_xt_parse_in(
121         struct fwd_xt_rule *r, struct fwd_network_list *n, int inv
122 ) {
123         if( n != NULL )
124         {
125                 strncpy(r->entry->ip.iniface, n->ifname, IFNAMSIZ);
126
127                 if( inv )
128                         r->entry->ip.invflags |= IPT_INV_VIA_IN;
129         }
130 }
131
132 void fwd_xt_parse_out(
133         struct fwd_xt_rule *r, struct fwd_network_list *n, int inv
134 ) {
135         if( n != NULL )
136         {
137                 strncpy(r->entry->ip.outiface, n->ifname, IFNAMSIZ);
138
139                 if( inv )
140                         r->entry->ip.invflags |= IPT_INV_VIA_OUT;
141         }
142 }
143
144 void fwd_xt_parse_src(
145         struct fwd_xt_rule *r, struct fwd_cidr *c, int inv
146 ) {
147         if( c != NULL )
148         {
149                 r->entry->ip.src.s_addr  = c->addr.s_addr;
150                 r->entry->ip.smsk.s_addr = htonl(~((1 << (32 - c->prefix)) - 1));
151
152                 if( inv )
153                         r->entry->ip.invflags |= IPT_INV_SRCIP;
154         }
155 }
156
157 void fwd_xt_parse_dest(
158         struct fwd_xt_rule *r, struct fwd_cidr *c, int inv
159 ) {
160         if( c != NULL )
161         {
162                 r->entry->ip.dst.s_addr  = c->addr.s_addr;
163                 r->entry->ip.dmsk.s_addr = htonl(~((1 << (32 - c->prefix)) - 1));
164
165                 if( inv )
166                         r->entry->ip.invflags |= IPT_INV_DSTIP;
167         }
168 }
169
170
171 struct xtables_match * fwd_xt_get_match(
172         struct fwd_xt_rule *r, const char *name
173 ) {
174         struct xtables_match *m = xtables_find_match(name, XTF_TRY_LOAD, &r->matches);
175         size_t s;
176
177         if( m != NULL )
178         {
179                 s = IPT_ALIGN(sizeof(struct ipt_entry_match)) + m->size;
180
181                 if(     (m->m = malloc(s)) != NULL )
182                 {
183                         memset(m->m, 0, s);
184                         strcpy(m->m->u.user.name, m->name);
185                         m->m->u.match_size = s;
186
187                         if( m->init )
188                                 m->init(m->m);
189
190                         return m;
191                 }
192         }
193
194         return NULL;
195 }
196
197 void __fwd_xt_parse_match(
198         struct fwd_xt_rule *r, struct xtables_match *m, ...
199 ) {
200         char optc;
201         char *s, **opts;
202         size_t len = 1;
203         int inv = 0;
204
205         va_list ap;
206         va_start(ap, m);
207
208         opts = malloc(len * sizeof(*opts));
209         opts[0] = "x";
210
211         while( (s = (char *)va_arg(ap, char *)) != NULL )
212         {
213                 opts = realloc(opts, ++len * sizeof(*opts));
214                 opts[len-1] = s;
215         }
216
217         va_end(ap);
218
219         if( len > 1 )
220         {
221                 optind = 0;
222
223                 while( (optc = getopt_long(len, opts, "", m->extra_opts, NULL)) > -1 )
224                 {
225                         if( (optc == '?') && (optarg[0] == '!') && (optarg[1] == '\0') )
226                         {
227                                 inv = 1;
228                                 continue;
229                         }
230
231                         m->parse(optc, opts, inv, &m->mflags, r->entry, &m->m);
232                         inv = 0;
233                 }
234         }
235
236         free(opts);
237 }
238
239
240 struct xtables_target * fwd_xt_get_target(
241         struct fwd_xt_rule *r, const char *name
242 ) {
243         struct xtables_target *t = xtables_find_target(name, XTF_TRY_LOAD);
244         size_t s;
245
246         if( !t )
247                 t = xtables_find_target(IPT_STANDARD_TARGET, XTF_LOAD_MUST_SUCCEED);
248
249         if( t != NULL )
250         {
251                 s = IPT_ALIGN(sizeof(struct ipt_entry_target)) + t->size;
252
253                 if(     (t->t = malloc(s)) != NULL )
254                 {
255                         memset(t->t, 0, s);
256                         strcpy(t->t->u.user.name, name);
257                         t->t->u.target_size = s;
258                         xtables_set_revision(t->t->u.user.name, t->revision);
259
260                         if( t->init )
261                                 t->init(t->t);
262
263                         r->target = t;
264
265                         return t;
266                 }
267         }
268
269         return NULL;
270 }
271
272 void __fwd_xt_parse_target(
273         struct fwd_xt_rule *r, struct xtables_target *t, ...
274 ) {
275         char optc;
276         char *s, **opts;
277         size_t len = 1;
278         int inv = 0;
279
280         va_list ap;
281         va_start(ap, t);
282
283         opts = malloc(len * sizeof(*opts));
284         opts[0] = "x";
285
286         while( (s = (char *)va_arg(ap, char *)) != NULL )
287         {
288                 opts = realloc(opts, ++len * sizeof(*opts));
289                 opts[len-1] = s;
290         }
291
292         va_end(ap);
293
294         if( len > 1 )
295         {
296                 optind = 0;
297
298                 while( (optc = getopt_long(len, opts, "", t->extra_opts, NULL)) > -1 )
299                 {
300                         if( (optc == '?') && (optarg[0] == '!') && (optarg[1] == '\0') )
301                         {
302                                 inv = 1;
303                                 continue;
304                         }
305
306                         t->parse(optc, opts, inv, &t->tflags, r->entry, &t->t);
307                         inv = 0;
308                 }
309         }
310
311         free(opts);
312 }
313
314
315 int fwd_xt_exec_rule(struct fwd_xt_rule *r, const char *chain)
316 {
317         size_t s;
318         struct xtables_rule_match *m, *next;
319         struct xtables_match *em;
320         struct xtables_target *et;
321         struct ipt_entry *e;
322         int rv = 0;
323
324         s = IPT_ALIGN(sizeof(struct ipt_entry));
325
326         for( m = r->matches; m; m = m->next )
327                 s += m->match->m->u.match_size;
328
329         if( (e = malloc(s + r->target->t->u.target_size)) != NULL )
330         {
331                 memset(e, 0, s + r->target->t->u.target_size);
332                 memcpy(e, r->entry, sizeof(struct ipt_entry));
333
334                 e->target_offset = s;
335                 e->next_offset = s + r->target->t->u.target_size;
336
337                 s = 0;
338
339                 for( m = r->matches; m; m = m->next )
340                 {
341                         memcpy(e->elems + s, m->match->m, m->match->m->u.match_size);
342                         s += m->match->m->u.match_size;
343                 }
344
345                 memcpy(e->elems + s, r->target->t, r->target->t->u.target_size);
346
347                 rv = iptc_append_entry(chain, e, r->iptc);
348         }
349         else
350         {
351                 errno = ENOMEM;
352         }
353
354
355         fwd_free_ptr(e);
356         fwd_free_ptr(r->entry);
357         fwd_free_ptr(r->target->t);
358
359         for( m = r->matches; m; )
360         {
361                 next = m->next;
362                 fwd_free_ptr(m->match->m);
363
364                 if( m->match == m->match->next )
365                         fwd_free_ptr(m->match);
366
367                 fwd_free_ptr(m);
368                 m = next;
369         }
370
371         fwd_free_ptr(r);
372
373         /* reset all targets and matches */
374         for (em = xtables_matches; em; em = em->next)
375                 em->mflags = 0;
376
377         for (et = xtables_targets; et; et = et->next)
378         {
379                 et->tflags = 0;
380                 et->used = 0;
381         }
382
383         return rv;
384 }
385