c0a3c582d870144ea51190a30667c1ec21c9d196
[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 void fwd_xt_parse_frag(
86         struct fwd_xt_rule *r, int frag, int inv
87 ) {
88         if( frag )
89         {
90                 r->entry->ip.flags |= IPT_F_FRAG;
91
92                 if( inv )
93                         r->entry->ip.invflags |= IPT_INV_FRAG;
94         }
95 }
96
97 void fwd_xt_parse_proto(
98         struct fwd_xt_rule *r, struct fwd_proto *p, int inv
99 ) {
100         if( p != NULL )
101         {
102                 switch(p->type)
103                 {
104                         case FWD_PR_TCP:
105                                 r->entry->ip.proto = 6;
106                                 break;
107
108                         case FWD_PR_UDP:
109                                 r->entry->ip.proto = 17;
110                                 break;
111
112                         case FWD_PR_ICMP:
113                                 r->entry->ip.proto = 1;
114                                 break;
115
116                         case FWD_PR_CUSTOM:
117                                 r->entry->ip.proto = p->proto;
118                                 break;
119
120                         case FWD_PR_ALL:
121                         case FWD_PR_TCPUDP:
122                                 r->entry->ip.proto = 0;
123                                 break;
124                 }
125
126                 if( inv )
127                         r->entry->ip.invflags |= IPT_INV_PROTO;
128         }
129 }
130
131 void fwd_xt_parse_in(
132         struct fwd_xt_rule *r, struct fwd_network_list *n, int inv
133 ) {
134         if( n != NULL )
135         {
136                 strncpy(r->entry->ip.iniface, n->ifname, IFNAMSIZ);
137
138                 if( inv )
139                         r->entry->ip.invflags |= IPT_INV_VIA_IN;
140         }
141 }
142
143 void fwd_xt_parse_out(
144         struct fwd_xt_rule *r, struct fwd_network_list *n, int inv
145 ) {
146         if( n != NULL )
147         {
148                 strncpy(r->entry->ip.outiface, n->ifname, IFNAMSIZ);
149
150                 if( inv )
151                         r->entry->ip.invflags |= IPT_INV_VIA_OUT;
152         }
153 }
154
155 void fwd_xt_parse_src(
156         struct fwd_xt_rule *r, struct fwd_cidr *c, int inv
157 ) {
158         if( c != NULL )
159         {
160                 r->entry->ip.src.s_addr  = c->addr.s_addr;
161                 r->entry->ip.smsk.s_addr = htonl(~((1 << (32 - c->prefix)) - 1));
162
163                 if( inv )
164                         r->entry->ip.invflags |= IPT_INV_SRCIP;
165         }
166 }
167
168 void fwd_xt_parse_dest(
169         struct fwd_xt_rule *r, struct fwd_cidr *c, int inv
170 ) {
171         if( c != NULL )
172         {
173                 r->entry->ip.dst.s_addr  = c->addr.s_addr;
174                 r->entry->ip.dmsk.s_addr = htonl(~((1 << (32 - c->prefix)) - 1));
175
176                 if( inv )
177                         r->entry->ip.invflags |= IPT_INV_DSTIP;
178         }
179 }
180
181
182 struct xtables_match * fwd_xt_get_match(
183         struct fwd_xt_rule *r, const char *name
184 ) {
185         struct xtables_match *m = xtables_find_match(name, XTF_TRY_LOAD, &r->matches);
186         size_t s;
187
188         if( m != NULL )
189         {
190                 s = IPT_ALIGN(sizeof(struct ipt_entry_match)) + m->size;
191
192                 if(     (m->m = malloc(s)) != NULL )
193                 {
194                         memset(m->m, 0, s);
195                         strcpy(m->m->u.user.name, m->name);
196                         m->m->u.match_size = s;
197
198                         if( m->init )
199                                 m->init(m->m);
200
201                         return m;
202                 }
203         }
204
205         return NULL;
206 }
207
208 void __fwd_xt_parse_match(
209         struct fwd_xt_rule *r, struct xtables_match *m, ...
210 ) {
211         char optc;
212         char *s, **opts;
213         size_t len = 1;
214         int inv = 0;
215
216         va_list ap;
217         va_start(ap, m);
218
219         opts = malloc(len * sizeof(*opts));
220         opts[0] = "x";
221
222         while( (s = (char *)va_arg(ap, char *)) != NULL )
223         {
224                 opts = realloc(opts, ++len * sizeof(*opts));
225                 opts[len-1] = s;
226         }
227
228         va_end(ap);
229
230         if( len > 1 )
231         {
232                 optind = 0;
233
234                 while( (optc = getopt_long(len, opts, "", m->extra_opts, NULL)) > -1 )
235                 {
236                         if( (optc == '?') && (optarg[0] == '!') && (optarg[1] == '\0') )
237                         {
238                                 inv = 1;
239                                 continue;
240                         }
241
242                         m->parse(optc, opts, inv, &m->mflags, r->entry, &m->m);
243                         inv = 0;
244                 }
245         }
246
247         free(opts);
248 }
249
250
251 struct xtables_target * fwd_xt_get_target(
252         struct fwd_xt_rule *r, const char *name
253 ) {
254         struct xtables_target *t = xtables_find_target(name, XTF_TRY_LOAD);
255         size_t s;
256
257         if( !t )
258                 t = xtables_find_target(IPT_STANDARD_TARGET, XTF_LOAD_MUST_SUCCEED);
259
260         if( t != NULL )
261         {
262                 s = IPT_ALIGN(sizeof(struct ipt_entry_target)) + t->size;
263
264                 if(     (t->t = malloc(s)) != NULL )
265                 {
266                         memset(t->t, 0, s);
267                         strcpy(t->t->u.user.name, name);
268                         t->t->u.target_size = s;
269                         xtables_set_revision(t->t->u.user.name, t->revision);
270
271                         if( t->init )
272                                 t->init(t->t);
273
274                         r->target = t;
275
276                         return t;
277                 }
278         }
279
280         return NULL;
281 }
282
283 void __fwd_xt_parse_target(
284         struct fwd_xt_rule *r, struct xtables_target *t, ...
285 ) {
286         char optc;
287         char *s, **opts;
288         size_t len = 1;
289         int inv = 0;
290
291         va_list ap;
292         va_start(ap, t);
293
294         opts = malloc(len * sizeof(*opts));
295         opts[0] = "x";
296
297         while( (s = (char *)va_arg(ap, char *)) != NULL )
298         {
299                 opts = realloc(opts, ++len * sizeof(*opts));
300                 opts[len-1] = s;
301         }
302
303         va_end(ap);
304
305         if( len > 1 )
306         {
307                 optind = 0;
308
309                 while( (optc = getopt_long(len, opts, "", t->extra_opts, NULL)) > -1 )
310                 {
311                         if( (optc == '?') && (optarg[0] == '!') && (optarg[1] == '\0') )
312                         {
313                                 inv = 1;
314                                 continue;
315                         }
316
317                         t->parse(optc, opts, inv, &t->tflags, r->entry, &t->t);
318                         inv = 0;
319                 }
320         }
321
322         free(opts);
323 }
324
325
326 static int fwd_xt_exec_rule(struct fwd_xt_rule *r, const char *chain, int pos)
327 {
328         size_t s;
329         struct xtables_rule_match *m, *next;
330         struct xtables_match *em;
331         struct xtables_target *et;
332         struct ipt_entry *e;
333         int rv = 0;
334
335         s = IPT_ALIGN(sizeof(struct ipt_entry));
336
337         for( m = r->matches; m; m = m->next )
338                 s += m->match->m->u.match_size;
339
340         if( (e = malloc(s + r->target->t->u.target_size)) != NULL )
341         {
342                 memset(e, 0, s + r->target->t->u.target_size);
343                 memcpy(e, r->entry, sizeof(struct ipt_entry));
344
345                 e->target_offset = s;
346                 e->next_offset = s + r->target->t->u.target_size;
347
348                 s = 0;
349
350                 for( m = r->matches; m; m = m->next )
351                 {
352                         memcpy(e->elems + s, m->match->m, m->match->m->u.match_size);
353                         s += m->match->m->u.match_size;
354                 }
355
356                 memcpy(e->elems + s, r->target->t, r->target->t->u.target_size);
357
358                 rv = (pos > -1)
359                         ? iptc_insert_entry(chain, e, (unsigned int) pos, r->iptc)
360                         : iptc_append_entry(chain, e, r->iptc)
361                 ;
362         }
363         else
364         {
365                 errno = ENOMEM;
366         }
367
368
369         fwd_free_ptr(e);
370         fwd_free_ptr(r->entry);
371         fwd_free_ptr(r->target->t);
372
373         for( m = r->matches; m; )
374         {
375                 next = m->next;
376                 fwd_free_ptr(m->match->m);
377
378                 if( m->match == m->match->next )
379                         fwd_free_ptr(m->match);
380
381                 fwd_free_ptr(m);
382                 m = next;
383         }
384
385         fwd_free_ptr(r);
386
387         /* reset all targets and matches */
388         for (em = xtables_matches; em; em = em->next)
389                 em->mflags = 0;
390
391         for (et = xtables_targets; et; et = et->next)
392         {
393                 et->tflags = 0;
394                 et->used = 0;
395         }
396
397         return rv;
398 }
399
400 int fwd_xt_insert_rule(
401         struct fwd_xt_rule *r, const char *chain, unsigned int pos
402 ) {
403         return fwd_xt_exec_rule(r, chain, pos);
404 }
405
406 int fwd_xt_append_rule(
407         struct fwd_xt_rule *r, const char *chain
408 ) {
409         return fwd_xt_exec_rule(r, chain, -1);
410 }
411