contrib/fwd: initial xtables binding
[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(const char *table)
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                         if( (r->iptc = iptc_init(table)) != NULL )
77                         {
78                                 return r;
79                         }
80                 }
81         }
82
83         fwd_free_ptr(r);
84         return NULL;
85 }
86
87
88 void fwd_xt_parse_proto(
89         struct fwd_xt_rule *r, struct fwd_proto *p, int inv
90 ) {
91         if( p != NULL )
92         {
93                 switch(p->type)
94                 {
95                         case FWD_PR_TCP:
96                                 r->entry->ip.proto = 6;
97                                 break;
98
99                         case FWD_PR_UDP:
100                                 r->entry->ip.proto = 17;
101                                 break;
102
103                         case FWD_PR_ICMP:
104                                 r->entry->ip.proto = 1;
105                                 break;
106
107                         case FWD_PR_CUSTOM:
108                                 r->entry->ip.proto = p->proto;
109                                 break;
110
111                         case FWD_PR_ALL:
112                         case FWD_PR_TCPUDP:
113                                 r->entry->ip.proto = 0;
114                                 break;
115                 }
116
117                 if( inv )
118                         r->entry->ip.invflags |= IPT_INV_PROTO;
119         }
120 }
121
122 void fwd_xt_parse_in(
123         struct fwd_xt_rule *r, struct fwd_network_list *n, int inv
124 ) {
125         if( n != NULL )
126         {
127                 strncpy(r->entry->ip.iniface, n->ifname, IFNAMSIZ);
128
129                 if( inv )
130                         r->entry->ip.invflags |= IPT_INV_VIA_IN;
131         }
132 }
133
134 void fwd_xt_parse_out(
135         struct fwd_xt_rule *r, struct fwd_network_list *n, int inv
136 ) {
137         if( n != NULL )
138         {
139                 strncpy(r->entry->ip.outiface, n->ifname, IFNAMSIZ);
140
141                 if( inv )
142                         r->entry->ip.invflags |= IPT_INV_VIA_OUT;
143         }
144 }
145
146 void fwd_xt_parse_src(
147         struct fwd_xt_rule *r, struct fwd_cidr *c, int inv
148 ) {
149         if( c != NULL )
150         {
151                 r->entry->ip.src.s_addr  = c->addr.s_addr;
152                 r->entry->ip.smsk.s_addr = htonl(~((1 << (32 - c->prefix)) - 1));
153
154                 if( inv )
155                         r->entry->ip.invflags |= IPT_INV_SRCIP;
156         }
157 }
158
159 void fwd_xt_parse_dest(
160         struct fwd_xt_rule *r, struct fwd_cidr *c, int inv
161 ) {
162         if( c != NULL )
163         {
164                 r->entry->ip.dst.s_addr  = c->addr.s_addr;
165                 r->entry->ip.dmsk.s_addr = htonl(~((1 << (32 - c->prefix)) - 1));
166
167                 if( inv )
168                         r->entry->ip.invflags |= IPT_INV_DSTIP;
169         }
170 }
171
172
173 struct xtables_match * fwd_xt_get_match(
174         struct fwd_xt_rule *r, const char *name
175 ) {
176         struct xtables_match *m = xtables_find_match(name, XTF_TRY_LOAD, &r->matches);
177         size_t s;
178
179         if( m != NULL )
180         {
181                 s = IPT_ALIGN(sizeof(struct ipt_entry_match)) + m->size;
182
183                 if(     (m->m = malloc(s)) != NULL )
184                 {
185                         memset(m->m, 0, s);
186                         strcpy(m->m->u.user.name, m->name);
187                         m->m->u.match_size = s;
188
189                         if( m->init )
190                                 m->init(m->m);
191
192                         return m;
193                 }
194         }
195
196         return NULL;
197 }
198
199 void fwd_xt_parse_match(
200         struct fwd_xt_rule *r, struct xtables_match *m,
201         const char *opt, const char *val
202 ) {
203         char optcode;
204         const char *opts[3] = { "x", opt, val };
205
206         optind  = 0;
207         optcode = getopt_long(val ? 3 : 2, (char **)opts, "", m->extra_opts, NULL);
208
209         if( (optcode > -1) && (optcode != '?') )
210                 m->parse(optcode, (char **)opts, 0, &m->mflags, r->entry, &m->m);
211 }
212
213
214 struct xtables_target * fwd_xt_get_target(
215         struct fwd_xt_rule *r, const char *name
216 ) {
217         struct xtables_target *t = xtables_find_target(name, XTF_TRY_LOAD);
218         size_t s;
219
220         if( !t )
221                 t = xtables_find_target(IPT_STANDARD_TARGET, XTF_LOAD_MUST_SUCCEED);
222
223         if( t != NULL )
224         {
225                 s = IPT_ALIGN(sizeof(struct ipt_entry_target)) + t->size;
226
227                 if(     (t->t = malloc(s)) != NULL )
228                 {
229                         memset(t->t, 0, s);
230                         strcpy(t->t->u.user.name, name);
231                         t->t->u.target_size = s;
232                         xtables_set_revision(t->t->u.user.name, t->revision);
233
234                         if( t->init )
235                                 t->init(t->t);
236
237                         r->target = t;
238
239                         return t;
240                 }
241         }
242
243         return NULL;
244 }
245
246 void fwd_xt_parse_target(
247         struct fwd_xt_rule *r, struct xtables_target *t,
248         const char *opt, const char *val
249 ) {
250         char optcode;
251         const char *opts[3] = { "x", opt, val };
252
253         optind  = 0;
254         optcode = getopt_long(val ? 3 : 2, (char **)opts, "", t->extra_opts, NULL);
255
256         if( (optcode > -1) && (optcode != '?') )
257                 t->parse(optcode, (char **)opts, 0, &t->tflags, r->entry, &t->t);
258 }
259
260 int fwd_xt_exec_rule(struct fwd_xt_rule *r, const char *chain)
261 {
262         size_t s;
263         struct xtables_rule_match *m, *next;
264         struct ipt_entry *e;
265         int rv = 0;
266
267         s = IPT_ALIGN(sizeof(struct ipt_entry));
268
269         for( m = r->matches; m; m = m->next )
270                 s += m->match->m->u.match_size;
271
272         if( (e = malloc(s + r->target->t->u.target_size)) != NULL )
273         {
274                 memset(e, 0, s + r->target->t->u.target_size);
275                 memcpy(e, r->entry, sizeof(struct ipt_entry));
276
277                 e->target_offset = s;
278                 e->next_offset = s + r->target->t->u.target_size;
279
280                 s = 0;
281
282                 for( m = r->matches; m; m = m->next )
283                 {
284                         memcpy(e->elems + s, m->match->m, m->match->m->u.match_size);
285                         s += m->match->m->u.match_size;
286                 }
287
288                 memcpy(e->elems + s, r->target->t, r->target->t->u.target_size);
289
290                 if( (rv = iptc_append_entry(chain, e, r->iptc)) > 0 )
291                         iptc_commit(r->iptc);
292         }
293         else
294         {
295                 errno = ENOMEM;
296         }
297
298
299         fwd_free_ptr(e);
300         fwd_free_ptr(r->entry);
301         fwd_free_ptr(r->target->t);
302
303         for( m = r->matches; m; )
304         {
305                 next = m->next;
306                 fwd_free_ptr(m->match->m);
307                 fwd_free_ptr(m);
308                 m = next;
309         }
310
311         iptc_free(r->iptc);
312         fwd_free_ptr(r);
313
314         return rv;
315 }
316