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 <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 48static const char *pname = "tc-ipt"; 49static const char *tname = "mangle"; 50static const char *pversion = "0.2"; 51 52static 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 60static struct option original_opts[] = { 61 {"jump", 1, 0, 'j'}, 62 {0, 0, 0, 0} 63}; 64 65static struct option *opts = original_opts; 66static unsigned int global_option_offset = 0; 67char *lib_dir; 68const char *program_version = XTABLES_VERSION; 69const char *program_name = "tc-ipt"; 70struct 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 */ 82static 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 */ 92static struct option * 93merge_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 */ 125int 126check_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 */ 146void 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 */ 160static 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*/ 171int 172build_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 193inline 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 207static 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 353static int 354print_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 431struct action_util ipt_action_util = { 432 .id = "ipt", 433 .parse_aopt = parse_ipt, 434 .print_aopt = print_ipt, 435}; 436