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