m_xt.c revision 52d6a850505d70a699fc89604986c45b0cd0c54e
1/* 2 * m_xt.c xtables based targets 3 * utilities mostly ripped from iptables <duh, its the linux way> 4 * 5 * This program is free software; you can distribute it and/or 6 * modify it under the terms of the GNU General Public License 7 * as published by the Free Software Foundation; either version 8 * 2 of the License, or (at your option) any later version. 9 * 10 * Authors: J Hadi Salim (hadi@cyberus.ca) 11 */ 12 13/*XXX: in the future (xtables 1.4.3?) get rid of everything tagged 14 * as TC_CONFIG_XT_H */ 15 16#include <syslog.h> 17#include <sys/socket.h> 18#include <netinet/in.h> 19#include <arpa/inet.h> 20#include <net/if.h> 21#include <linux/netfilter.h> 22#include <linux/netfilter_ipv4/ip_tables.h> 23#include <xtables.h> 24#include "utils.h" 25#include "tc_util.h" 26#include <linux/tc_act/tc_ipt.h> 27#include <stdio.h> 28#include <dlfcn.h> 29#include <getopt.h> 30#include <errno.h> 31#include <string.h> 32#include <netdb.h> 33#include <stdlib.h> 34#include <ctype.h> 35#include <stdarg.h> 36#include <limits.h> 37#include <unistd.h> 38#include <fcntl.h> 39#include <sys/wait.h> 40#ifdef TC_CONFIG_XT_H 41#include "xt-internal.h" 42#endif 43 44static const char *pname = "tc-ipt"; 45static const char *tname = "mangle"; 46static const char *pversion = "0.2"; 47 48static const char *ipthooks[] = { 49 "NF_IP_PRE_ROUTING", 50 "NF_IP_LOCAL_IN", 51 "NF_IP_FORWARD", 52 "NF_IP_LOCAL_OUT", 53 "NF_IP_POST_ROUTING", 54}; 55 56static struct option original_opts[] = { 57 {"jump", 1, 0, 'j'}, 58 {0, 0, 0, 0} 59}; 60 61static struct option *opts = original_opts; 62static unsigned int global_option_offset = 0; 63char *lib_dir; 64const char *program_version = XTABLES_VERSION; 65const char *program_name = "tc-ipt"; 66struct afinfo afinfo = { 67 .family = AF_INET, 68 .libprefix = "libxt_", 69 .ipproto = IPPROTO_IP, 70 .kmod = "ip_tables", 71 .so_rev_target = IPT_SO_GET_REVISION_TARGET, 72}; 73 74 75#define OPTION_OFFSET 256 76 77/*XXX: TC_CONFIG_XT_H */ 78static void free_opts(struct option *local_opts) 79{ 80 if (local_opts != original_opts) { 81 free(local_opts); 82 opts = original_opts; 83 global_option_offset = 0; 84 } 85} 86 87/*XXX: TC_CONFIG_XT_H */ 88static struct option * 89merge_options(struct option *oldopts, const struct option *newopts, 90 unsigned int *option_offset) 91{ 92 struct option *merge; 93 unsigned int num_old, num_new, i; 94 95 for (num_old = 0; oldopts[num_old].name; num_old++) ; 96 for (num_new = 0; newopts[num_new].name; num_new++) ; 97 98 *option_offset = global_option_offset + OPTION_OFFSET; 99 100 merge = malloc(sizeof (struct option) * (num_new + num_old + 1)); 101 memcpy(merge, oldopts, num_old * sizeof (struct option)); 102 for (i = 0; i < num_new; i++) { 103 merge[num_old + i] = newopts[i]; 104 merge[num_old + i].val += *option_offset; 105 } 106 memset(merge + num_old + num_new, 0, sizeof (struct option)); 107 108 return merge; 109} 110 111 112/*XXX: TC_CONFIG_XT_H */ 113#ifndef TRUE 114#define TRUE 1 115#endif 116#ifndef FALSE 117#define FALSE 0 118#endif 119 120/*XXX: TC_CONFIG_XT_H */ 121int 122check_inverse(const char option[], int *invert, int *my_optind, int argc) 123{ 124 if (option && strcmp(option, "!") == 0) { 125 if (*invert) 126 exit_error(PARAMETER_PROBLEM, 127 "Multiple `!' flags not allowed"); 128 *invert = TRUE; 129 if (my_optind != NULL) { 130 ++*my_optind; 131 if (argc && *my_optind > argc) 132 exit_error(PARAMETER_PROBLEM, 133 "no argument following `!'"); 134 } 135 136 return TRUE; 137 } 138 return FALSE; 139} 140 141/*XXX: TC_CONFIG_XT_H */ 142void exit_error(enum exittype status, const char *msg, ...) 143{ 144 va_list args; 145 146 va_start(args, msg); 147 fprintf(stderr, "%s v%s: ", pname, pversion); 148 vfprintf(stderr, msg, args); 149 va_end(args); 150 fprintf(stderr, "\n"); 151 /* On error paths, make sure that we don't leak memory */ 152 exit(status); 153} 154 155/*XXX: TC_CONFIG_XT_H */ 156static void set_revision(char *name, u_int8_t revision) 157{ 158 /* Old kernel sources don't have ".revision" field, 159 * but we stole a byte from name. */ 160 name[IPT_FUNCTION_MAXNAMELEN - 2] = '\0'; 161 name[IPT_FUNCTION_MAXNAMELEN - 1] = revision; 162} 163 164/* 165 * we may need to check for version mismatch 166*/ 167int 168build_st(struct xtables_target *target, struct xt_entry_target *t) 169{ 170 171 size_t size = 172 XT_ALIGN(sizeof (struct xt_entry_target)) + target->size; 173 174 if (NULL == t) { 175 target->t = fw_calloc(1, size); 176 target->t->u.target_size = size; 177 strcpy(target->t->u.user.name, target->name); 178 set_revision(target->t->u.user.name, target->revision); 179 180 if (target->init != NULL) 181 target->init(target->t); 182 } else { 183 target->t = t; 184 } 185 return 0; 186 187} 188 189inline void set_lib_dir(void) 190{ 191 192 lib_dir = getenv("XTABLES_LIBDIR"); 193 if (!lib_dir) { 194 lib_dir = getenv("IPTABLES_LIB_DIR"); 195 if (lib_dir) 196 fprintf(stderr, "using deprecated IPTABLES_LIB_DIR \n"); 197 } 198 if (lib_dir == NULL) 199 lib_dir = XT_LIB_DIR; 200 201} 202 203static int parse_ipt(struct action_util *a,int *argc_p, 204 char ***argv_p, int tca_id, struct nlmsghdr *n) 205{ 206 struct xtables_target *m = NULL; 207 struct ipt_entry fw; 208 struct rtattr *tail; 209 int c; 210 int rargc = *argc_p; 211 char **argv = *argv_p; 212 int argc = 0, iargc = 0; 213 char k[16]; 214 int res = -1; 215 int size = 0; 216 int iok = 0, ok = 0; 217 __u32 hook = 0, index = 0; 218 res = 0; 219 220 set_lib_dir(); 221 222 { 223 int i; 224 for (i = 0; i < rargc; i++) { 225 if (NULL == argv[i] || 0 == strcmp(argv[i], "action")) { 226 break; 227 } 228 } 229 iargc = argc = i; 230 } 231 232 if (argc <= 2) { 233 fprintf(stderr,"bad arguements to ipt %d vs %d \n", argc, rargc); 234 return -1; 235 } 236 237 while (1) { 238 c = getopt_long(argc, argv, "j:", opts, NULL); 239 if (c == -1) 240 break; 241 switch (c) { 242 case 'j': 243 m = find_target(optarg, TRY_LOAD); 244 if (NULL != m) { 245 246 if (0 > build_st(m, NULL)) { 247 printf(" %s error \n", m->name); 248 return -1; 249 } 250 opts = 251 merge_options(opts, m->extra_opts, 252 &m->option_offset); 253 } else { 254 fprintf(stderr," failed to find target %s\n\n", optarg); 255 return -1; 256 } 257 ok++; 258 break; 259 260 default: 261 memset(&fw, 0, sizeof (fw)); 262 if (m) { 263 m->parse(c - m->option_offset, argv, 0, 264 &m->tflags, NULL, &m->t); 265 } else { 266 fprintf(stderr," failed to find target %s\n\n", optarg); 267 return -1; 268 269 } 270 ok++; 271 break; 272 273 } 274 } 275 276 if (iargc > optind) { 277 if (matches(argv[optind], "index") == 0) { 278 if (get_u32(&index, argv[optind + 1], 10)) { 279 fprintf(stderr, "Illegal \"index\"\n"); 280 free_opts(opts); 281 return -1; 282 } 283 iok++; 284 285 optind += 2; 286 } 287 } 288 289 if (!ok && !iok) { 290 fprintf(stderr," ipt Parser BAD!! (%s)\n", *argv); 291 return -1; 292 } 293 294 /* check that we passed the correct parameters to the target */ 295 if (m) 296 m->final_check(m->tflags); 297 298 { 299 struct tcmsg *t = NLMSG_DATA(n); 300 if (t->tcm_parent != TC_H_ROOT 301 && t->tcm_parent == TC_H_MAJ(TC_H_INGRESS)) { 302 hook = NF_IP_PRE_ROUTING; 303 } else { 304 hook = NF_IP_POST_ROUTING; 305 } 306 } 307 308 tail = NLMSG_TAIL(n); 309 addattr_l(n, MAX_MSG, tca_id, NULL, 0); 310 fprintf(stdout, "tablename: %s hook: %s\n ", tname, ipthooks[hook]); 311 fprintf(stdout, "\ttarget: "); 312 313 if (m) 314 m->print(NULL, m->t, 0); 315 fprintf(stdout, " index %d\n", index); 316 317 if (strlen(tname) > 16) { 318 size = 16; 319 k[15] = 0; 320 } else { 321 size = 1 + strlen(tname); 322 } 323 strncpy(k, tname, size); 324 325 addattr_l(n, MAX_MSG, TCA_IPT_TABLE, k, size); 326 addattr_l(n, MAX_MSG, TCA_IPT_HOOK, &hook, 4); 327 addattr_l(n, MAX_MSG, TCA_IPT_INDEX, &index, 4); 328 if (m) 329 addattr_l(n, MAX_MSG, TCA_IPT_TARG, m->t, m->t->u.target_size); 330 tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; 331 332 argc -= optind; 333 argv += optind; 334 *argc_p = rargc - iargc; 335 *argv_p = argv; 336 337 optind = 0; 338 free_opts(opts); 339 /* Clear flags if target will be used again */ 340 m->tflags=0; 341 m->used=0; 342 /* Free allocated memory */ 343 if (m->t) 344 free(m->t); 345 346 347 return 0; 348 349} 350 351static int 352print_ipt(struct action_util *au,FILE * f, struct rtattr *arg) 353{ 354 struct rtattr *tb[TCA_IPT_MAX + 1]; 355 struct xt_entry_target *t = NULL; 356 357 if (arg == NULL) 358 return -1; 359 360 set_lib_dir(); 361 362 parse_rtattr_nested(tb, TCA_IPT_MAX, arg); 363 364 if (tb[TCA_IPT_TABLE] == NULL) { 365 fprintf(f, "[NULL ipt table name ] assuming mangle "); 366 } else { 367 fprintf(f, "tablename: %s ", 368 (char *) RTA_DATA(tb[TCA_IPT_TABLE])); 369 } 370 371 if (tb[TCA_IPT_HOOK] == NULL) { 372 fprintf(f, "[NULL ipt hook name ]\n "); 373 return -1; 374 } else { 375 __u32 hook; 376 hook = *(__u32 *) RTA_DATA(tb[TCA_IPT_HOOK]); 377 fprintf(f, " hook: %s \n", ipthooks[hook]); 378 } 379 380 if (tb[TCA_IPT_TARG] == NULL) { 381 fprintf(f, "\t[NULL ipt target parameters ] \n"); 382 return -1; 383 } else { 384 struct xtables_target *m = NULL; 385 t = RTA_DATA(tb[TCA_IPT_TARG]); 386 m = find_target(t->u.user.name, TRY_LOAD); 387 if (NULL != m) { 388 if (0 > build_st(m, t)) { 389 fprintf(stderr, " %s error \n", m->name); 390 return -1; 391 } 392 393 opts = 394 merge_options(opts, m->extra_opts, 395 &m->option_offset); 396 } else { 397 fprintf(stderr, " failed to find target %s\n\n", 398 t->u.user.name); 399 return -1; 400 } 401 fprintf(f, "\ttarget "); 402 m->print(NULL, m->t, 0); 403 if (tb[TCA_IPT_INDEX] == NULL) { 404 fprintf(f, " [NULL ipt target index ]\n"); 405 } else { 406 __u32 index; 407 index = *(__u32 *) RTA_DATA(tb[TCA_IPT_INDEX]); 408 fprintf(f, " \n\tindex %d", index); 409 } 410 411 if (tb[TCA_IPT_CNT]) { 412 struct tc_cnt *c = RTA_DATA(tb[TCA_IPT_CNT]);; 413 fprintf(f, " ref %d bind %d", c->refcnt, c->bindcnt); 414 } 415 if (show_stats) { 416 if (tb[TCA_IPT_TM]) { 417 struct tcf_t *tm = RTA_DATA(tb[TCA_IPT_TM]); 418 print_tm(f,tm); 419 } 420 } 421 fprintf(f, " \n"); 422 423 } 424 free_opts(opts); 425 426 return 0; 427} 428 429struct action_util ipt_action_util = { 430 .id = "ipt", 431 .parse_aopt = parse_ipt, 432 .print_aopt = print_ipt, 433}; 434 435