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 = 0; 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 (NULL == t) { 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 for (i = 0; i < rargc; i++) { 227 if (NULL == argv[i] || 0 == strcmp(argv[i], "action")) { 228 break; 229 } 230 } 231 iargc = argc = i; 232 } 233 234 if (argc <= 2) { 235 fprintf(stderr,"bad arguments to ipt %d vs %d \n", argc, rargc); 236 return -1; 237 } 238 239 while (1) { 240 c = getopt_long(argc, argv, "j:", opts, NULL); 241 if (c == -1) 242 break; 243 switch (c) { 244 case 'j': 245 m = find_target(optarg, TRY_LOAD); 246 if (NULL != m) { 247 248 if (0 > build_st(m, NULL)) { 249 printf(" %s error \n", m->name); 250 return -1; 251 } 252 opts = 253 merge_options(opts, m->extra_opts, 254 &m->option_offset); 255 } else { 256 fprintf(stderr," failed to find target %s\n\n", optarg); 257 return -1; 258 } 259 ok++; 260 break; 261 262 default: 263 memset(&fw, 0, sizeof (fw)); 264 if (m) { 265 m->parse(c - m->option_offset, argv, 0, 266 &m->tflags, NULL, &m->t); 267 } else { 268 fprintf(stderr," failed to find target %s\n\n", optarg); 269 return -1; 270 271 } 272 ok++; 273 break; 274 275 } 276 } 277 278 if (iargc > optind) { 279 if (matches(argv[optind], "index") == 0) { 280 if (get_u32(&index, argv[optind + 1], 10)) { 281 fprintf(stderr, "Illegal \"index\"\n"); 282 free_opts(opts); 283 return -1; 284 } 285 iok++; 286 287 optind += 2; 288 } 289 } 290 291 if (!ok && !iok) { 292 fprintf(stderr," ipt Parser BAD!! (%s)\n", *argv); 293 return -1; 294 } 295 296 /* check that we passed the correct parameters to the target */ 297 if (m) 298 m->final_check(m->tflags); 299 300 { 301 struct tcmsg *t = NLMSG_DATA(n); 302 if (t->tcm_parent != TC_H_ROOT 303 && t->tcm_parent == TC_H_MAJ(TC_H_INGRESS)) { 304 hook = NF_IP_PRE_ROUTING; 305 } else { 306 hook = NF_IP_POST_ROUTING; 307 } 308 } 309 310 tail = NLMSG_TAIL(n); 311 addattr_l(n, MAX_MSG, tca_id, NULL, 0); 312 fprintf(stdout, "tablename: %s hook: %s\n ", tname, ipthooks[hook]); 313 fprintf(stdout, "\ttarget: "); 314 315 if (m) 316 m->print(NULL, m->t, 0); 317 fprintf(stdout, " index %d\n", index); 318 319 if (strlen(tname) > 16) { 320 size = 16; 321 k[15] = 0; 322 } else { 323 size = 1 + strlen(tname); 324 } 325 strncpy(k, tname, size); 326 327 addattr_l(n, MAX_MSG, TCA_IPT_TABLE, k, size); 328 addattr_l(n, MAX_MSG, TCA_IPT_HOOK, &hook, 4); 329 addattr_l(n, MAX_MSG, TCA_IPT_INDEX, &index, 4); 330 if (m) 331 addattr_l(n, MAX_MSG, TCA_IPT_TARG, m->t, m->t->u.target_size); 332 tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail; 333 334 argc -= optind; 335 argv += optind; 336 *argc_p = rargc - iargc; 337 *argv_p = argv; 338 339 optind = 0; 340 free_opts(opts); 341 /* Clear flags if target will be used again */ 342 m->tflags=0; 343 m->used=0; 344 /* Free allocated memory */ 345 if (m->t) 346 free(m->t); 347 348 349 return 0; 350 351 } 352 353 static int 354 print_ipt(struct action_util *au,FILE * f, struct rtattr *arg) 355 { 356 struct rtattr *tb[TCA_IPT_MAX + 1]; 357 struct xt_entry_target *t = NULL; 358 359 if (arg == NULL) 360 return -1; 361 362 set_lib_dir(); 363 364 parse_rtattr_nested(tb, TCA_IPT_MAX, arg); 365 366 if (tb[TCA_IPT_TABLE] == NULL) { 367 fprintf(f, "[NULL ipt table name ] assuming mangle "); 368 } else { 369 fprintf(f, "tablename: %s ", 370 rta_getattr_str(tb[TCA_IPT_TABLE])); 371 } 372 373 if (tb[TCA_IPT_HOOK] == NULL) { 374 fprintf(f, "[NULL ipt hook name ]\n "); 375 return -1; 376 } else { 377 __u32 hook; 378 hook = rta_getattr_u32(tb[TCA_IPT_HOOK]); 379 fprintf(f, " hook: %s \n", ipthooks[hook]); 380 } 381 382 if (tb[TCA_IPT_TARG] == NULL) { 383 fprintf(f, "\t[NULL ipt target parameters ] \n"); 384 return -1; 385 } else { 386 struct xtables_target *m = NULL; 387 t = RTA_DATA(tb[TCA_IPT_TARG]); 388 m = find_target(t->u.user.name, TRY_LOAD); 389 if (NULL != m) { 390 if (0 > build_st(m, t)) { 391 fprintf(stderr, " %s error \n", m->name); 392 return -1; 393 } 394 395 opts = 396 merge_options(opts, m->extra_opts, 397 &m->option_offset); 398 } else { 399 fprintf(stderr, " failed to find target %s\n\n", 400 t->u.user.name); 401 return -1; 402 } 403 fprintf(f, "\ttarget "); 404 m->print(NULL, m->t, 0); 405 if (tb[TCA_IPT_INDEX] == NULL) { 406 fprintf(f, " [NULL ipt target index ]\n"); 407 } else { 408 __u32 index; 409 index = rta_getattr_u32(tb[TCA_IPT_INDEX]); 410 fprintf(f, " \n\tindex %d", index); 411 } 412 413 if (tb[TCA_IPT_CNT]) { 414 struct tc_cnt *c = RTA_DATA(tb[TCA_IPT_CNT]);; 415 fprintf(f, " ref %d bind %d", c->refcnt, c->bindcnt); 416 } 417 if (show_stats) { 418 if (tb[TCA_IPT_TM]) { 419 struct tcf_t *tm = RTA_DATA(tb[TCA_IPT_TM]); 420 print_tm(f,tm); 421 } 422 } 423 fprintf(f, " \n"); 424 425 } 426 free_opts(opts); 427 428 return 0; 429 } 430 431 struct action_util ipt_action_util = { 432 .id = "ipt", 433 .parse_aopt = parse_ipt, 434 .print_aopt = print_ipt, 435 }; 436