contrib/fwd: update xtables api, reuse iptc handle from caller, fix segfault when...
[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 const char *opt, const char *val, int inv
200 ) {
201 char optcode;
202 const char *opts[3] = { "x", opt, val };
203
204 optind = 0;
205 optcode = getopt_long(val ? 3 : 2, (char **)opts, "", m->extra_opts, NULL);
206
207 if( (optcode > -1) && (optcode != '?') )
208 m->parse(optcode, (char **)opts, inv, &m->mflags, r->entry, &m->m);
209 }
210
211
212 struct xtables_target * fwd_xt_get_target(
213 struct fwd_xt_rule *r, const char *name
214 ) {
215 struct xtables_target *t = xtables_find_target(name, XTF_TRY_LOAD);
216 size_t s;
217
218 if( !t )
219 t = xtables_find_target(IPT_STANDARD_TARGET, XTF_LOAD_MUST_SUCCEED);
220
221 if( t != NULL )
222 {
223 s = IPT_ALIGN(sizeof(struct ipt_entry_target)) + t->size;
224
225 if( (t->t = malloc(s)) != NULL )
226 {
227 memset(t->t, 0, s);
228 strcpy(t->t->u.user.name, name);
229 t->t->u.target_size = s;
230 xtables_set_revision(t->t->u.user.name, t->revision);
231
232 if( t->init )
233 t->init(t->t);
234
235 r->target = t;
236
237 return t;
238 }
239 }
240
241 return NULL;
242 }
243
244 void fwd_xt_parse_target(
245 struct fwd_xt_rule *r, struct xtables_target *t,
246 const char *opt, const char *val, int inv
247 ) {
248 char optcode;
249 const char *opts[3] = { "x", opt, val };
250
251 optind = 0;
252 optcode = getopt_long(val ? 3 : 2, (char **)opts, "", t->extra_opts, NULL);
253
254 if( (optcode > -1) && (optcode != '?') )
255 t->parse(optcode, (char **)opts, inv, &t->tflags, r->entry, &t->t);
256 }
257
258 int fwd_xt_exec_rule(struct fwd_xt_rule *r, const char *chain)
259 {
260 size_t s;
261 struct xtables_rule_match *m, *next;
262 struct xtables_match *em;
263 struct xtables_target *et;
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 rv = iptc_append_entry(chain, e, r->iptc);
291 }
292 else
293 {
294 errno = ENOMEM;
295 }
296
297
298 fwd_free_ptr(e);
299 fwd_free_ptr(r->entry);
300 fwd_free_ptr(r->target->t);
301
302 for( m = r->matches; m; )
303 {
304 next = m->next;
305 fwd_free_ptr(m->match->m);
306
307 if( m->match == m->match->next )
308 fwd_free_ptr(m->match);
309
310 fwd_free_ptr(m);
311 m = next;
312 }
313
314 fwd_free_ptr(r);
315
316 /* reset all targets and matches */
317 for (em = xtables_matches; em; em = em->next)
318 em->mflags = 0;
319
320 for (et = xtables_targets; et; et = et->next)
321 {
322 et->tflags = 0;
323 et->used = 0;
324 }
325
326 return rv;
327 }
328