blob: f00531c441e9f692e083795de15ee235cad9f249 [file] [log] [blame]
Yasuyuki KOZAKAI52088062007-07-24 05:44:11 +00001/*
2 * (C) 2000-2006 by the netfilter coreteam <coreteam@netfilter.org>:
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18
Yasuyuki KOZAKAI0b82e8e2007-07-24 05:47:40 +000019#include <dlfcn.h>
Yasuyuki KOZAKAI3dfa4482007-07-24 05:45:33 +000020#include <errno.h>
Yasuyuki KOZAKAI0b82e8e2007-07-24 05:47:40 +000021#include <fcntl.h>
Yasuyuki KOZAKAI3dfa4482007-07-24 05:45:33 +000022#include <stdio.h>
23#include <stdlib.h>
Yasuyuki KOZAKAI0b82e8e2007-07-24 05:47:40 +000024#include <string.h>
25#include <unistd.h>
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +000026#include <sys/socket.h>
Yasuyuki KOZAKAI0b82e8e2007-07-24 05:47:40 +000027#include <sys/stat.h>
28#include <sys/types.h>
29#include <sys/wait.h>
Yasuyuki KOZAKAI3dfa4482007-07-24 05:45:33 +000030
31#include <iptables_common.h>
Yasuyuki KOZAKAI52088062007-07-24 05:44:11 +000032#include <xtables.h>
Yasuyuki KOZAKAI3dfa4482007-07-24 05:45:33 +000033
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +000034#define NPROTO 255
35
Yasuyuki KOZAKAI0b82e8e2007-07-24 05:47:40 +000036#ifndef PROC_SYS_MODPROBE
37#define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe"
38#endif
39
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +000040char *lib_dir;
41
Yasuyuki KOZAKAI0b82e8e2007-07-24 05:47:40 +000042/* the path to command to load kernel module */
43const char *modprobe = NULL;
44
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +000045/* Keeping track of external matches and targets: linked lists. */
46struct xtables_match *xtables_matches;
47struct xtables_target *xtables_targets;
48
Yasuyuki KOZAKAI3dfa4482007-07-24 05:45:33 +000049void *fw_calloc(size_t count, size_t size)
50{
51 void *p;
52
53 if ((p = calloc(count, size)) == NULL) {
54 perror("ip[6]tables: calloc failed");
55 exit(1);
56 }
57
58 return p;
59}
60
61void *fw_malloc(size_t size)
62{
63 void *p;
64
65 if ((p = malloc(size)) == NULL) {
66 perror("ip[6]tables: malloc failed");
67 exit(1);
68 }
69
70 return p;
71}
Yasuyuki KOZAKAI0b82e8e2007-07-24 05:47:40 +000072
73static char *get_modprobe(void)
74{
75 int procfile;
76 char *ret;
77
78#define PROCFILE_BUFSIZ 1024
79 procfile = open(PROC_SYS_MODPROBE, O_RDONLY);
80 if (procfile < 0)
81 return NULL;
82
83 ret = (char *) malloc(PROCFILE_BUFSIZ);
84 if (ret) {
85 memset(ret, 0, PROCFILE_BUFSIZ);
86 switch (read(procfile, ret, PROCFILE_BUFSIZ)) {
87 case -1: goto fail;
88 case PROCFILE_BUFSIZ: goto fail; /* Partial read. Wierd */
89 }
90 if (ret[strlen(ret)-1]=='\n')
91 ret[strlen(ret)-1]=0;
92 close(procfile);
93 return ret;
94 }
95 fail:
96 free(ret);
97 close(procfile);
98 return NULL;
99}
100
101int xtables_insmod(const char *modname, const char *modprobe, int quiet)
102{
103 char *buf = NULL;
104 char *argv[4];
105 int status;
106
107 /* If they don't explicitly set it, read out of kernel */
108 if (!modprobe) {
109 buf = get_modprobe();
110 if (!buf)
111 return -1;
112 modprobe = buf;
113 }
114
115 switch (fork()) {
116 case 0:
117 argv[0] = (char *)modprobe;
118 argv[1] = (char *)modname;
119 if (quiet) {
120 argv[2] = "-q";
121 argv[3] = NULL;
122 } else {
123 argv[2] = NULL;
124 argv[3] = NULL;
125 }
126 execv(argv[0], argv);
127
128 /* not usually reached */
129 exit(1);
130 case -1:
131 return -1;
132
133 default: /* parent */
134 wait(&status);
135 }
136
137 free(buf);
138 if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
139 return 0;
140 return -1;
141}
142
Yasuyuki KOZAKAI0d502bc2007-07-24 05:52:07 +0000143int load_xtables_ko(const char *modprobe, int quiet)
144{
145 static int loaded = 0;
146 static int ret = -1;
147
148 if (!loaded) {
149 ret = xtables_insmod(afinfo.kmod, modprobe, quiet);
150 loaded = (ret == 0);
151 }
152
153 return ret;
154}
155
156struct xtables_match *find_match(const char *name, enum xt_tryload tryload,
157 struct xtables_rule_match **matches)
158{
159 struct xtables_match *ptr;
160 const char *icmp6 = "icmp6";
161
162 /* This is ugly as hell. Nonetheless, there is no way of changing
163 * this without hurting backwards compatibility */
164 if ( (strcmp(name,"icmpv6") == 0) ||
165 (strcmp(name,"ipv6-icmp") == 0) ||
166 (strcmp(name,"icmp6") == 0) )
167 name = icmp6;
168
169 for (ptr = xtables_matches; ptr; ptr = ptr->next) {
170 if (strcmp(name, ptr->name) == 0) {
171 struct xtables_match *clone;
172
173 /* First match of this type: */
174 if (ptr->m == NULL)
175 break;
176
177 /* Second and subsequent clones */
178 clone = fw_malloc(sizeof(struct xtables_match));
179 memcpy(clone, ptr, sizeof(struct xtables_match));
180 clone->mflags = 0;
181 /* This is a clone: */
182 clone->next = clone;
183
184 ptr = clone;
185 break;
186 }
187 }
188
189#ifndef NO_SHARED_LIBS
190 if (!ptr && tryload != DONT_LOAD && tryload != DURING_LOAD) {
191 char path[strlen(lib_dir) + sizeof("/.so")
192 + strlen(afinfo.libprefix) + strlen(name)];
193 sprintf(path, "%s/%s%s.so", lib_dir, afinfo.libprefix,
194 name);
195 if (dlopen(path, RTLD_NOW)) {
196 /* Found library. If it didn't register itself,
197 maybe they specified target as match. */
198 ptr = find_match(name, DONT_LOAD, NULL);
199
200 if (!ptr)
201 exit_error(PARAMETER_PROBLEM,
202 "Couldn't load match `%s'\n",
203 name);
204 } else if (tryload == LOAD_MUST_SUCCEED)
205 exit_error(PARAMETER_PROBLEM,
206 "Couldn't load match `%s':%s\n",
207 name, dlerror());
208 }
209#else
210 if (ptr && !ptr->loaded) {
211 if (tryload != DONT_LOAD)
212 ptr->loaded = 1;
213 else
214 ptr = NULL;
215 }
216 if(!ptr && (tryload == LOAD_MUST_SUCCEED)) {
217 exit_error(PARAMETER_PROBLEM,
218 "Couldn't find match `%s'\n", name);
219 }
220#endif
221
222 if (ptr && matches) {
223 struct xtables_rule_match **i;
224 struct xtables_rule_match *newentry;
225
226 newentry = fw_malloc(sizeof(struct xtables_rule_match));
227
228 for (i = matches; *i; i = &(*i)->next) {
229 if (strcmp(name, (*i)->match->name) == 0)
230 (*i)->completed = 1;
231 }
232 newentry->match = ptr;
233 newentry->completed = 0;
234 newentry->next = NULL;
235 *i = newentry;
236 }
237
238 return ptr;
239}
240
241
242struct xtables_target *find_target(const char *name, enum xt_tryload tryload)
243{
244 struct xtables_target *ptr;
245
246 /* Standard target? */
247 if (strcmp(name, "") == 0
248 || strcmp(name, XTC_LABEL_ACCEPT) == 0
249 || strcmp(name, XTC_LABEL_DROP) == 0
250 || strcmp(name, XTC_LABEL_QUEUE) == 0
251 || strcmp(name, XTC_LABEL_RETURN) == 0)
252 name = "standard";
253
254 for (ptr = xtables_targets; ptr; ptr = ptr->next) {
255 if (strcmp(name, ptr->name) == 0)
256 break;
257 }
258
259#ifndef NO_SHARED_LIBS
260 if (!ptr && tryload != DONT_LOAD && tryload != DURING_LOAD) {
261 char path[strlen(lib_dir) + sizeof("/.so")
262 + strlen(afinfo.libprefix) + strlen(name)];
263 sprintf(path, "%s/%s%s.so", lib_dir, afinfo.libprefix, name);
264 if (dlopen(path, RTLD_NOW)) {
265 /* Found library. If it didn't register itself,
266 maybe they specified match as a target. */
267 ptr = find_target(name, DONT_LOAD);
268 if (!ptr)
269 exit_error(PARAMETER_PROBLEM,
270 "Couldn't load target `%s'\n",
271 name);
272 } else if (tryload == LOAD_MUST_SUCCEED)
273 exit_error(PARAMETER_PROBLEM,
274 "Couldn't load target `%s':%s\n",
275 name, dlerror());
276 }
277#else
278 if (ptr && !ptr->loaded) {
279 if (tryload != DONT_LOAD)
280 ptr->loaded = 1;
281 else
282 ptr = NULL;
283 }
284 if(!ptr && (tryload == LOAD_MUST_SUCCEED)) {
285 exit_error(PARAMETER_PROBLEM,
286 "Couldn't find target `%s'\n", name);
287 }
288#endif
289
290 if (ptr)
291 ptr->used = 1;
292
293 return ptr;
294}
295
296static int compatible_revision(const char *name, u_int8_t revision, int opt)
297{
298 struct xt_get_revision rev;
299 socklen_t s = sizeof(rev);
300 int max_rev, sockfd;
301
302 sockfd = socket(afinfo.family, SOCK_RAW, IPPROTO_RAW);
303 if (sockfd < 0) {
304 fprintf(stderr, "Could not open socket to kernel: %s\n",
305 strerror(errno));
306 exit(1);
307 }
308
309 load_xtables_ko(modprobe, 1);
310
311 strcpy(rev.name, name);
312 rev.revision = revision;
313
314 max_rev = getsockopt(sockfd, afinfo.ipproto, opt, &rev, &s);
315 if (max_rev < 0) {
316 /* Definitely don't support this? */
317 if (errno == ENOENT || errno == EPROTONOSUPPORT) {
318 close(sockfd);
319 return 0;
320 } else if (errno == ENOPROTOOPT) {
321 close(sockfd);
322 /* Assume only revision 0 support (old kernel) */
323 return (revision == 0);
324 } else {
325 fprintf(stderr, "getsockopt failed strangely: %s\n",
326 strerror(errno));
327 exit(1);
328 }
329 }
330 close(sockfd);
331 return 1;
332}
333
334
335static int compatible_match_revision(const char *name, u_int8_t revision)
336{
337 return compatible_revision(name, revision, afinfo.so_rev_match);
338}
339
340static int compatible_target_revision(const char *name, u_int8_t revision)
341{
342 return compatible_revision(name, revision, afinfo.so_rev_target);
343}
344
345void xtables_register_match(struct xtables_match *me)
346{
347 struct xtables_match **i, *old;
348
349 if (strcmp(me->version, program_version) != 0) {
350 fprintf(stderr, "%s: match `%s' v%s (I'm v%s).\n",
351 program_name, me->name, me->version, program_version);
352 exit(1);
353 }
354
355 /* Revision field stole a char from name. */
356 if (strlen(me->name) >= XT_FUNCTION_MAXNAMELEN-1) {
357 fprintf(stderr, "%s: target `%s' has invalid name\n",
358 program_name, me->name);
359 exit(1);
360 }
361
362 if (me->family >= NPROTO) {
363 fprintf(stderr,
364 "%s: BUG: match %s has invalid protocol family\n",
365 program_name, me->name);
366 exit(1);
367 }
368
369 /* ignore not interested match */
370 if (me->family != afinfo.family)
371 return;
372
373 old = find_match(me->name, DURING_LOAD, NULL);
374 if (old) {
375 if (old->revision == me->revision) {
376 fprintf(stderr,
377 "%s: match `%s' already registered.\n",
378 program_name, me->name);
379 exit(1);
380 }
381
382 /* Now we have two (or more) options, check compatibility. */
383 if (compatible_match_revision(old->name, old->revision)
384 && old->revision > me->revision)
385 return;
386
387 /* Replace if compatible. */
388 if (!compatible_match_revision(me->name, me->revision))
389 return;
390
391 /* Delete old one. */
392 for (i = &xtables_matches; *i!=old; i = &(*i)->next);
393 *i = old->next;
394 }
395
396 if (me->size != XT_ALIGN(me->size)) {
397 fprintf(stderr, "%s: match `%s' has invalid size %u.\n",
398 program_name, me->name, (unsigned int)me->size);
399 exit(1);
400 }
401
402 /* Append to list. */
403 for (i = &xtables_matches; *i; i = &(*i)->next);
404 me->next = NULL;
405 *i = me;
406
407 me->m = NULL;
408 me->mflags = 0;
409}
410
411void xtables_register_target(struct xtables_target *me)
412{
413 struct xtables_target *old;
414
415 if (strcmp(me->version, program_version) != 0) {
416 fprintf(stderr, "%s: target `%s' v%s (I'm v%s).\n",
417 program_name, me->name, me->version, program_version);
418 exit(1);
419 }
420
421 /* Revision field stole a char from name. */
422 if (strlen(me->name) >= XT_FUNCTION_MAXNAMELEN-1) {
423 fprintf(stderr, "%s: target `%s' has invalid name\n",
424 program_name, me->name);
425 exit(1);
426 }
427
428 if (me->family >= NPROTO) {
429 fprintf(stderr,
430 "%s: BUG: target %s has invalid protocol family\n",
431 program_name, me->name);
432 exit(1);
433 }
434
435 /* ignore not interested target */
436 if (me->family != afinfo.family)
437 return;
438
439 old = find_target(me->name, DURING_LOAD);
440 if (old) {
441 struct xtables_target **i;
442
443 if (old->revision == me->revision) {
444 fprintf(stderr,
445 "%s: target `%s' already registered.\n",
446 program_name, me->name);
447 exit(1);
448 }
449
450 /* Now we have two (or more) options, check compatibility. */
451 if (compatible_target_revision(old->name, old->revision)
452 && old->revision > me->revision)
453 return;
454
455 /* Replace if compatible. */
456 if (!compatible_target_revision(me->name, me->revision))
457 return;
458
459 /* Delete old one. */
460 for (i = &xtables_targets; *i!=old; i = &(*i)->next);
461 *i = old->next;
462 }
463
464 if (me->size != XT_ALIGN(me->size)) {
465 fprintf(stderr, "%s: target `%s' has invalid size %u.\n",
466 program_name, me->name, (unsigned int)me->size);
467 exit(1);
468 }
469
470 /* Prepend to list. */
471 me->next = xtables_targets;
472 xtables_targets = me;
473 me->t = NULL;
474 me->tflags = 0;
475}