xtables.c revision 04f8c54dc52e19096d31d94593bd1040716afe4d
1/* 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 19#include <dlfcn.h> 20#include <errno.h> 21#include <fcntl.h> 22#include <netdb.h> 23#include <stdio.h> 24#include <stdlib.h> 25#include <string.h> 26#include <unistd.h> 27#include <sys/socket.h> 28#include <sys/stat.h> 29#include <sys/types.h> 30#include <sys/wait.h> 31 32#include <iptables_common.h> 33#include <xtables.h> 34 35#define NPROTO 255 36 37#ifndef PROC_SYS_MODPROBE 38#define PROC_SYS_MODPROBE "/proc/sys/kernel/modprobe" 39#endif 40 41char *lib_dir; 42 43/* the path to command to load kernel module */ 44const char *modprobe = NULL; 45 46/* Keeping track of external matches and targets: linked lists. */ 47struct xtables_match *xtables_matches; 48struct xtables_target *xtables_targets; 49 50void *fw_calloc(size_t count, size_t size) 51{ 52 void *p; 53 54 if ((p = calloc(count, size)) == NULL) { 55 perror("ip[6]tables: calloc failed"); 56 exit(1); 57 } 58 59 return p; 60} 61 62void *fw_malloc(size_t size) 63{ 64 void *p; 65 66 if ((p = malloc(size)) == NULL) { 67 perror("ip[6]tables: malloc failed"); 68 exit(1); 69 } 70 71 return p; 72} 73 74static char *get_modprobe(void) 75{ 76 int procfile; 77 char *ret; 78 79#define PROCFILE_BUFSIZ 1024 80 procfile = open(PROC_SYS_MODPROBE, O_RDONLY); 81 if (procfile < 0) 82 return NULL; 83 84 ret = (char *) malloc(PROCFILE_BUFSIZ); 85 if (ret) { 86 memset(ret, 0, PROCFILE_BUFSIZ); 87 switch (read(procfile, ret, PROCFILE_BUFSIZ)) { 88 case -1: goto fail; 89 case PROCFILE_BUFSIZ: goto fail; /* Partial read. Wierd */ 90 } 91 if (ret[strlen(ret)-1]=='\n') 92 ret[strlen(ret)-1]=0; 93 close(procfile); 94 return ret; 95 } 96 fail: 97 free(ret); 98 close(procfile); 99 return NULL; 100} 101 102int xtables_insmod(const char *modname, const char *modprobe, int quiet) 103{ 104 char *buf = NULL; 105 char *argv[4]; 106 int status; 107 108 /* If they don't explicitly set it, read out of kernel */ 109 if (!modprobe) { 110 buf = get_modprobe(); 111 if (!buf) 112 return -1; 113 modprobe = buf; 114 } 115 116 switch (fork()) { 117 case 0: 118 argv[0] = (char *)modprobe; 119 argv[1] = (char *)modname; 120 if (quiet) { 121 argv[2] = "-q"; 122 argv[3] = NULL; 123 } else { 124 argv[2] = NULL; 125 argv[3] = NULL; 126 } 127 execv(argv[0], argv); 128 129 /* not usually reached */ 130 exit(1); 131 case -1: 132 return -1; 133 134 default: /* parent */ 135 wait(&status); 136 } 137 138 free(buf); 139 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) 140 return 0; 141 return -1; 142} 143 144int load_xtables_ko(const char *modprobe, int quiet) 145{ 146 static int loaded = 0; 147 static int ret = -1; 148 149 if (!loaded) { 150 ret = xtables_insmod(afinfo.kmod, modprobe, quiet); 151 loaded = (ret == 0); 152 } 153 154 return ret; 155} 156 157int string_to_number_ll(const char *s, unsigned long long min, 158 unsigned long long max, unsigned long long *ret) 159{ 160 unsigned long long number; 161 char *end; 162 163 /* Handle hex, octal, etc. */ 164 errno = 0; 165 number = strtoull(s, &end, 0); 166 if (*end == '\0' && end != s) { 167 /* we parsed a number, let's see if we want this */ 168 if (errno != ERANGE && min <= number && (!max || number <= max)) { 169 *ret = number; 170 return 0; 171 } 172 } 173 return -1; 174} 175 176int string_to_number_l(const char *s, unsigned long min, unsigned long max, 177 unsigned long *ret) 178{ 179 int result; 180 unsigned long long number; 181 182 result = string_to_number_ll(s, min, max, &number); 183 *ret = (unsigned long)number; 184 185 return result; 186} 187 188int string_to_number(const char *s, unsigned int min, unsigned int max, 189 unsigned int *ret) 190{ 191 int result; 192 unsigned long number; 193 194 result = string_to_number_l(s, min, max, &number); 195 *ret = (unsigned int)number; 196 197 return result; 198} 199 200int service_to_port(const char *name, const char *proto) 201{ 202 struct servent *service; 203 204 if ((service = getservbyname(name, proto)) != NULL) 205 return ntohs((unsigned short) service->s_port); 206 207 return -1; 208} 209 210u_int16_t parse_port(const char *port, const char *proto) 211{ 212 unsigned int portnum; 213 214 if ((string_to_number(port, 0, 65535, &portnum)) != -1 || 215 (portnum = service_to_port(port, proto)) != -1) 216 return (u_int16_t)portnum; 217 218 exit_error(PARAMETER_PROBLEM, 219 "invalid port/service `%s' specified", port); 220} 221 222void parse_interface(const char *arg, char *vianame, unsigned char *mask) 223{ 224 int vialen = strlen(arg); 225 unsigned int i; 226 227 memset(mask, 0, IFNAMSIZ); 228 memset(vianame, 0, IFNAMSIZ); 229 230 if (vialen + 1 > IFNAMSIZ) 231 exit_error(PARAMETER_PROBLEM, 232 "interface name `%s' must be shorter than IFNAMSIZ" 233 " (%i)", arg, IFNAMSIZ-1); 234 235 strcpy(vianame, arg); 236 if ((vialen == 0) || (vialen == 1 && vianame[0] == '+')) 237 memset(mask, 0, IFNAMSIZ); 238 else if (vianame[vialen - 1] == '+') { 239 memset(mask, 0xFF, vialen - 1); 240 memset(mask + vialen - 1, 0, IFNAMSIZ - vialen + 1); 241 /* Don't remove `+' here! -HW */ 242 } else { 243 /* Include nul-terminator in match */ 244 memset(mask, 0xFF, vialen + 1); 245 memset(mask + vialen + 1, 0, IFNAMSIZ - vialen - 1); 246 for (i = 0; vianame[i]; i++) { 247 if (vianame[i] == ':' || 248 vianame[i] == '!' || 249 vianame[i] == '*') { 250 printf("Warning: weird character in interface" 251 " `%s' (No aliases, :, ! or *).\n", 252 vianame); 253 break; 254 } 255 } 256 } 257} 258 259struct xtables_match *find_match(const char *name, enum xt_tryload tryload, 260 struct xtables_rule_match **matches) 261{ 262 struct xtables_match *ptr; 263 const char *icmp6 = "icmp6"; 264 265 /* This is ugly as hell. Nonetheless, there is no way of changing 266 * this without hurting backwards compatibility */ 267 if ( (strcmp(name,"icmpv6") == 0) || 268 (strcmp(name,"ipv6-icmp") == 0) || 269 (strcmp(name,"icmp6") == 0) ) 270 name = icmp6; 271 272 for (ptr = xtables_matches; ptr; ptr = ptr->next) { 273 if (strcmp(name, ptr->name) == 0) { 274 struct xtables_match *clone; 275 276 /* First match of this type: */ 277 if (ptr->m == NULL) 278 break; 279 280 /* Second and subsequent clones */ 281 clone = fw_malloc(sizeof(struct xtables_match)); 282 memcpy(clone, ptr, sizeof(struct xtables_match)); 283 clone->mflags = 0; 284 /* This is a clone: */ 285 clone->next = clone; 286 287 ptr = clone; 288 break; 289 } 290 } 291 292#ifndef NO_SHARED_LIBS 293 if (!ptr && tryload != DONT_LOAD && tryload != DURING_LOAD) { 294 char path[strlen(lib_dir) + sizeof("/.so") 295 + strlen(afinfo.libprefix) + strlen(name)]; 296 sprintf(path, "%s/%s%s.so", lib_dir, afinfo.libprefix, 297 name); 298 if (dlopen(path, RTLD_NOW)) { 299 /* Found library. If it didn't register itself, 300 maybe they specified target as match. */ 301 ptr = find_match(name, DONT_LOAD, NULL); 302 303 if (!ptr) 304 exit_error(PARAMETER_PROBLEM, 305 "Couldn't load match `%s'\n", 306 name); 307 } else if (tryload == LOAD_MUST_SUCCEED) 308 exit_error(PARAMETER_PROBLEM, 309 "Couldn't load match `%s':%s\n", 310 name, dlerror()); 311 } 312#else 313 if (ptr && !ptr->loaded) { 314 if (tryload != DONT_LOAD) 315 ptr->loaded = 1; 316 else 317 ptr = NULL; 318 } 319 if(!ptr && (tryload == LOAD_MUST_SUCCEED)) { 320 exit_error(PARAMETER_PROBLEM, 321 "Couldn't find match `%s'\n", name); 322 } 323#endif 324 325 if (ptr && matches) { 326 struct xtables_rule_match **i; 327 struct xtables_rule_match *newentry; 328 329 newentry = fw_malloc(sizeof(struct xtables_rule_match)); 330 331 for (i = matches; *i; i = &(*i)->next) { 332 if (strcmp(name, (*i)->match->name) == 0) 333 (*i)->completed = 1; 334 } 335 newentry->match = ptr; 336 newentry->completed = 0; 337 newentry->next = NULL; 338 *i = newentry; 339 } 340 341 return ptr; 342} 343 344 345struct xtables_target *find_target(const char *name, enum xt_tryload tryload) 346{ 347 struct xtables_target *ptr; 348 349 /* Standard target? */ 350 if (strcmp(name, "") == 0 351 || strcmp(name, XTC_LABEL_ACCEPT) == 0 352 || strcmp(name, XTC_LABEL_DROP) == 0 353 || strcmp(name, XTC_LABEL_QUEUE) == 0 354 || strcmp(name, XTC_LABEL_RETURN) == 0) 355 name = "standard"; 356 357 for (ptr = xtables_targets; ptr; ptr = ptr->next) { 358 if (strcmp(name, ptr->name) == 0) 359 break; 360 } 361 362#ifndef NO_SHARED_LIBS 363 if (!ptr && tryload != DONT_LOAD && tryload != DURING_LOAD) { 364 char path[strlen(lib_dir) + sizeof("/.so") 365 + strlen(afinfo.libprefix) + strlen(name)]; 366 sprintf(path, "%s/%s%s.so", lib_dir, afinfo.libprefix, name); 367 if (dlopen(path, RTLD_NOW)) { 368 /* Found library. If it didn't register itself, 369 maybe they specified match as a target. */ 370 ptr = find_target(name, DONT_LOAD); 371 if (!ptr) 372 exit_error(PARAMETER_PROBLEM, 373 "Couldn't load target `%s'\n", 374 name); 375 } else if (tryload == LOAD_MUST_SUCCEED) 376 exit_error(PARAMETER_PROBLEM, 377 "Couldn't load target `%s':%s\n", 378 name, dlerror()); 379 } 380#else 381 if (ptr && !ptr->loaded) { 382 if (tryload != DONT_LOAD) 383 ptr->loaded = 1; 384 else 385 ptr = NULL; 386 } 387 if(!ptr && (tryload == LOAD_MUST_SUCCEED)) { 388 exit_error(PARAMETER_PROBLEM, 389 "Couldn't find target `%s'\n", name); 390 } 391#endif 392 393 if (ptr) 394 ptr->used = 1; 395 396 return ptr; 397} 398 399static int compatible_revision(const char *name, u_int8_t revision, int opt) 400{ 401 struct xt_get_revision rev; 402 socklen_t s = sizeof(rev); 403 int max_rev, sockfd; 404 405 sockfd = socket(afinfo.family, SOCK_RAW, IPPROTO_RAW); 406 if (sockfd < 0) { 407 fprintf(stderr, "Could not open socket to kernel: %s\n", 408 strerror(errno)); 409 exit(1); 410 } 411 412 load_xtables_ko(modprobe, 1); 413 414 strcpy(rev.name, name); 415 rev.revision = revision; 416 417 max_rev = getsockopt(sockfd, afinfo.ipproto, opt, &rev, &s); 418 if (max_rev < 0) { 419 /* Definitely don't support this? */ 420 if (errno == ENOENT || errno == EPROTONOSUPPORT) { 421 close(sockfd); 422 return 0; 423 } else if (errno == ENOPROTOOPT) { 424 close(sockfd); 425 /* Assume only revision 0 support (old kernel) */ 426 return (revision == 0); 427 } else { 428 fprintf(stderr, "getsockopt failed strangely: %s\n", 429 strerror(errno)); 430 exit(1); 431 } 432 } 433 close(sockfd); 434 return 1; 435} 436 437 438static int compatible_match_revision(const char *name, u_int8_t revision) 439{ 440 return compatible_revision(name, revision, afinfo.so_rev_match); 441} 442 443static int compatible_target_revision(const char *name, u_int8_t revision) 444{ 445 return compatible_revision(name, revision, afinfo.so_rev_target); 446} 447 448void xtables_register_match(struct xtables_match *me) 449{ 450 struct xtables_match **i, *old; 451 452 if (strcmp(me->version, program_version) != 0) { 453 fprintf(stderr, "%s: match `%s' v%s (I'm v%s).\n", 454 program_name, me->name, me->version, program_version); 455 exit(1); 456 } 457 458 /* Revision field stole a char from name. */ 459 if (strlen(me->name) >= XT_FUNCTION_MAXNAMELEN-1) { 460 fprintf(stderr, "%s: target `%s' has invalid name\n", 461 program_name, me->name); 462 exit(1); 463 } 464 465 if (me->family >= NPROTO) { 466 fprintf(stderr, 467 "%s: BUG: match %s has invalid protocol family\n", 468 program_name, me->name); 469 exit(1); 470 } 471 472 /* ignore not interested match */ 473 if (me->family != afinfo.family) 474 return; 475 476 old = find_match(me->name, DURING_LOAD, NULL); 477 if (old) { 478 if (old->revision == me->revision) { 479 fprintf(stderr, 480 "%s: match `%s' already registered.\n", 481 program_name, me->name); 482 exit(1); 483 } 484 485 /* Now we have two (or more) options, check compatibility. */ 486 if (compatible_match_revision(old->name, old->revision) 487 && old->revision > me->revision) 488 return; 489 490 /* Replace if compatible. */ 491 if (!compatible_match_revision(me->name, me->revision)) 492 return; 493 494 /* Delete old one. */ 495 for (i = &xtables_matches; *i!=old; i = &(*i)->next); 496 *i = old->next; 497 } 498 499 if (me->size != XT_ALIGN(me->size)) { 500 fprintf(stderr, "%s: match `%s' has invalid size %u.\n", 501 program_name, me->name, (unsigned int)me->size); 502 exit(1); 503 } 504 505 /* Append to list. */ 506 for (i = &xtables_matches; *i; i = &(*i)->next); 507 me->next = NULL; 508 *i = me; 509 510 me->m = NULL; 511 me->mflags = 0; 512} 513 514void xtables_register_target(struct xtables_target *me) 515{ 516 struct xtables_target *old; 517 518 if (strcmp(me->version, program_version) != 0) { 519 fprintf(stderr, "%s: target `%s' v%s (I'm v%s).\n", 520 program_name, me->name, me->version, program_version); 521 exit(1); 522 } 523 524 /* Revision field stole a char from name. */ 525 if (strlen(me->name) >= XT_FUNCTION_MAXNAMELEN-1) { 526 fprintf(stderr, "%s: target `%s' has invalid name\n", 527 program_name, me->name); 528 exit(1); 529 } 530 531 if (me->family >= NPROTO) { 532 fprintf(stderr, 533 "%s: BUG: target %s has invalid protocol family\n", 534 program_name, me->name); 535 exit(1); 536 } 537 538 /* ignore not interested target */ 539 if (me->family != afinfo.family) 540 return; 541 542 old = find_target(me->name, DURING_LOAD); 543 if (old) { 544 struct xtables_target **i; 545 546 if (old->revision == me->revision) { 547 fprintf(stderr, 548 "%s: target `%s' already registered.\n", 549 program_name, me->name); 550 exit(1); 551 } 552 553 /* Now we have two (or more) options, check compatibility. */ 554 if (compatible_target_revision(old->name, old->revision) 555 && old->revision > me->revision) 556 return; 557 558 /* Replace if compatible. */ 559 if (!compatible_target_revision(me->name, me->revision)) 560 return; 561 562 /* Delete old one. */ 563 for (i = &xtables_targets; *i!=old; i = &(*i)->next); 564 *i = old->next; 565 } 566 567 if (me->size != XT_ALIGN(me->size)) { 568 fprintf(stderr, "%s: target `%s' has invalid size %u.\n", 569 program_name, me->name, (unsigned int)me->size); 570 exit(1); 571 } 572 573 /* Prepend to list. */ 574 me->next = xtables_targets; 575 xtables_targets = me; 576 me->t = NULL; 577 me->tflags = 0; 578} 579