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 res = -1; 219 int size = 0; 220 int iok = 0, ok = 0; 221 __u32 hook = 0, index = 0; 222 res = 0; 223 224 set_lib_dir(); 225 226 { 227 int i; 228 for (i = 0; i < rargc; i++) { 229 if (NULL == argv[i] || 0 == strcmp(argv[i], "action")) { 230 break; 231 } 232 } 233 iargc = argc = i; 234 } 235 236 if (argc <= 2) { 237 fprintf(stderr,"bad arguements to ipt %d vs %d \n", argc, rargc); 238 return -1; 239 } 240 241 while (1) { 242 c = getopt_long(argc, argv, "j:", opts, NULL); 243 if (c == -1) 244 break; 245 switch (c) { 246 case 'j': 247 m = find_target(optarg, TRY_LOAD); 248 if (NULL != m) { 249 250 if (0 > build_st(m, NULL)) { 251 printf(" %s error \n", m->name); 252 return -1; 253 } 254 opts = 255 merge_options(opts, m->extra_opts, 256 &m->option_offset); 257 } else { 258 fprintf(stderr," failed to find target %s\n\n", optarg); 259 return -1; 260 } 261 ok++; 262 break; 263 264 default: 265 memset(&fw, 0, sizeof (fw)); 266 if (m) { 267 m->parse(c - m->option_offset, argv, 0, 268 &m->tflags, NULL, &m->t); 269 } else { 270 fprintf(stderr," failed to find target %s\n\n", optarg); 271 return -1; 272 273 } 274 ok++; 275 break; 276 277 } 278 } 279 280 if (iargc > optind) { 281 if (matches(argv[optind], "index") == 0) { 282 if (get_u32(&index, argv[optind + 1], 10)) { 283 fprintf(stderr, "Illegal \"index\"\n"); 284 free_opts(opts); 285 return -1; 286 } 287 iok++; 288 289 optind += 2; 290 } 291 } 292 293 if (!ok && !iok) { 294 fprintf(stderr," ipt Parser BAD!! (%s)\n", *argv); 295 return -1; 296 } 297 298 /* check that we passed the correct parameters to the target */ 299 if (m) 300 m->final_check(m->tflags); 301 302 { 303 struct tcmsg *t = NLMSG_DATA(n); 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 355static int 356print_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 (char *) RTA_DATA(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 hook = *(__u32 *) RTA_DATA(tb[TCA_IPT_HOOK]); 381 fprintf(f, " hook: %s \n", ipthooks[hook]); 382 } 383 384 if (tb[TCA_IPT_TARG] == NULL) { 385 fprintf(f, "\t[NULL ipt target parameters ] \n"); 386 return -1; 387 } else { 388 struct xtables_target *m = NULL; 389 t = RTA_DATA(tb[TCA_IPT_TARG]); 390 m = find_target(t->u.user.name, TRY_LOAD); 391 if (NULL != m) { 392 if (0 > build_st(m, t)) { 393 fprintf(stderr, " %s error \n", m->name); 394 return -1; 395 } 396 397 opts = 398 merge_options(opts, m->extra_opts, 399 &m->option_offset); 400 } else { 401 fprintf(stderr, " failed to find target %s\n\n", 402 t->u.user.name); 403 return -1; 404 } 405 fprintf(f, "\ttarget "); 406 m->print(NULL, m->t, 0); 407 if (tb[TCA_IPT_INDEX] == NULL) { 408 fprintf(f, " [NULL ipt target index ]\n"); 409 } else { 410 __u32 index; 411 index = *(__u32 *) RTA_DATA(tb[TCA_IPT_INDEX]); 412 fprintf(f, " \n\tindex %d", index); 413 } 414 415 if (tb[TCA_IPT_CNT]) { 416 struct tc_cnt *c = RTA_DATA(tb[TCA_IPT_CNT]);; 417 fprintf(f, " ref %d bind %d", c->refcnt, c->bindcnt); 418 } 419 if (show_stats) { 420 if (tb[TCA_IPT_TM]) { 421 struct tcf_t *tm = RTA_DATA(tb[TCA_IPT_TM]); 422 print_tm(f,tm); 423 } 424 } 425 fprintf(f, " \n"); 426 427 } 428 free_opts(opts); 429 430 return 0; 431} 432 433struct action_util ipt_action_util = { 434 .id = "ipt", 435 .parse_aopt = parse_ipt, 436 .print_aopt = print_ipt, 437}; 438 439