1/* Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu> 2 * Patrick Schaaf <bof@bof.de> 3 * Martin Josefsson <gandalf@wlug.westbo.se> 4 * Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 */ 10 11/* Shared library add-on to iptables to add IP set mangling target. */ 12#include <stdbool.h> 13#include <stdio.h> 14#include <netdb.h> 15#include <string.h> 16#include <stdlib.h> 17#include <getopt.h> 18#include <ctype.h> 19 20#include <xtables.h> 21#include <linux/netfilter/xt_set.h> 22#include "libxt_set.h" 23 24/* Revision 0 */ 25 26static void 27set_target_help_v0(void) 28{ 29 printf("SET target options:\n" 30 " --add-set name flags\n" 31 " --del-set name flags\n" 32 " add/del src/dst IP/port from/to named sets,\n" 33 " where flags are the comma separated list of\n" 34 " 'src' and 'dst' specifications.\n"); 35} 36 37static const struct option set_target_opts_v0[] = { 38 {.name = "add-set", .has_arg = true, .val = '1'}, 39 {.name = "del-set", .has_arg = true, .val = '2'}, 40 XT_GETOPT_TABLEEND, 41}; 42 43static void 44set_target_check_v0(unsigned int flags) 45{ 46 if (!flags) 47 xtables_error(PARAMETER_PROBLEM, 48 "You must specify either `--add-set' or `--del-set'"); 49} 50 51static void 52set_target_init_v0(struct xt_entry_target *target) 53{ 54 struct xt_set_info_target_v0 *info = 55 (struct xt_set_info_target_v0 *) target->data; 56 57 info->add_set.index = 58 info->del_set.index = IPSET_INVALID_ID; 59 60} 61 62static void 63parse_target_v0(char **argv, int invert, unsigned int *flags, 64 struct xt_set_info_v0 *info, const char *what) 65{ 66 if (info->u.flags[0]) 67 xtables_error(PARAMETER_PROBLEM, 68 "--%s can be specified only once", what); 69 70 if (!argv[optind] 71 || argv[optind][0] == '-' || argv[optind][0] == '!') 72 xtables_error(PARAMETER_PROBLEM, 73 "--%s requires two args.", what); 74 75 if (strlen(optarg) > IPSET_MAXNAMELEN - 1) 76 xtables_error(PARAMETER_PROBLEM, 77 "setname `%s' too long, max %d characters.", 78 optarg, IPSET_MAXNAMELEN - 1); 79 80 get_set_byname(optarg, (struct xt_set_info *)info); 81 parse_dirs_v0(argv[optind], info); 82 optind++; 83 84 *flags = 1; 85} 86 87static int 88set_target_parse_v0(int c, char **argv, int invert, unsigned int *flags, 89 const void *entry, struct xt_entry_target **target) 90{ 91 struct xt_set_info_target_v0 *myinfo = 92 (struct xt_set_info_target_v0 *) (*target)->data; 93 94 switch (c) { 95 case '1': /* --add-set <set> <flags> */ 96 parse_target_v0(argv, invert, flags, 97 &myinfo->add_set, "add-set"); 98 break; 99 case '2': /* --del-set <set>[:<flags>] <flags> */ 100 parse_target_v0(argv, invert, flags, 101 &myinfo->del_set, "del-set"); 102 break; 103 } 104 return 1; 105} 106 107static void 108print_target_v0(const char *prefix, const struct xt_set_info_v0 *info) 109{ 110 int i; 111 char setname[IPSET_MAXNAMELEN]; 112 113 if (info->index == IPSET_INVALID_ID) 114 return; 115 get_set_byid(setname, info->index); 116 printf(" %s %s", prefix, setname); 117 for (i = 0; i < IPSET_DIM_MAX; i++) { 118 if (!info->u.flags[i]) 119 break; 120 printf("%s%s", 121 i == 0 ? " " : ",", 122 info->u.flags[i] & IPSET_SRC ? "src" : "dst"); 123 } 124} 125 126static void 127set_target_print_v0(const void *ip, const struct xt_entry_target *target, 128 int numeric) 129{ 130 const struct xt_set_info_target_v0 *info = (const void *)target->data; 131 132 print_target_v0("add-set", &info->add_set); 133 print_target_v0("del-set", &info->del_set); 134} 135 136static void 137set_target_save_v0(const void *ip, const struct xt_entry_target *target) 138{ 139 const struct xt_set_info_target_v0 *info = (const void *)target->data; 140 141 print_target_v0("--add-set", &info->add_set); 142 print_target_v0("--del-set", &info->del_set); 143} 144 145/* Revision 1 */ 146static void 147set_target_init_v1(struct xt_entry_target *target) 148{ 149 struct xt_set_info_target_v1 *info = 150 (struct xt_set_info_target_v1 *) target->data; 151 152 info->add_set.index = 153 info->del_set.index = IPSET_INVALID_ID; 154 155} 156 157#define SET_TARGET_ADD 0x1 158#define SET_TARGET_DEL 0x2 159#define SET_TARGET_EXIST 0x4 160#define SET_TARGET_TIMEOUT 0x8 161 162static void 163parse_target(char **argv, int invert, struct xt_set_info *info, 164 const char *what) 165{ 166 if (info->dim) 167 xtables_error(PARAMETER_PROBLEM, 168 "--%s can be specified only once", what); 169 if (!argv[optind] 170 || argv[optind][0] == '-' || argv[optind][0] == '!') 171 xtables_error(PARAMETER_PROBLEM, 172 "--%s requires two args.", what); 173 174 if (strlen(optarg) > IPSET_MAXNAMELEN - 1) 175 xtables_error(PARAMETER_PROBLEM, 176 "setname `%s' too long, max %d characters.", 177 optarg, IPSET_MAXNAMELEN - 1); 178 179 get_set_byname(optarg, info); 180 parse_dirs(argv[optind], info); 181 optind++; 182} 183 184static int 185set_target_parse_v1(int c, char **argv, int invert, unsigned int *flags, 186 const void *entry, struct xt_entry_target **target) 187{ 188 struct xt_set_info_target_v1 *myinfo = 189 (struct xt_set_info_target_v1 *) (*target)->data; 190 191 switch (c) { 192 case '1': /* --add-set <set> <flags> */ 193 parse_target(argv, invert, &myinfo->add_set, "add-set"); 194 *flags |= SET_TARGET_ADD; 195 break; 196 case '2': /* --del-set <set>[:<flags>] <flags> */ 197 parse_target(argv, invert, &myinfo->del_set, "del-set"); 198 *flags |= SET_TARGET_DEL; 199 break; 200 } 201 return 1; 202} 203 204static void 205print_target(const char *prefix, const struct xt_set_info *info) 206{ 207 int i; 208 char setname[IPSET_MAXNAMELEN]; 209 210 if (info->index == IPSET_INVALID_ID) 211 return; 212 get_set_byid(setname, info->index); 213 printf(" %s %s", prefix, setname); 214 for (i = 1; i <= info->dim; i++) { 215 printf("%s%s", 216 i == 1 ? " " : ",", 217 info->flags & (1 << i) ? "src" : "dst"); 218 } 219} 220 221static void 222set_target_print_v1(const void *ip, const struct xt_entry_target *target, 223 int numeric) 224{ 225 const struct xt_set_info_target_v1 *info = (const void *)target->data; 226 227 print_target("add-set", &info->add_set); 228 print_target("del-set", &info->del_set); 229} 230 231static void 232set_target_save_v1(const void *ip, const struct xt_entry_target *target) 233{ 234 const struct xt_set_info_target_v1 *info = (const void *)target->data; 235 236 print_target("--add-set", &info->add_set); 237 print_target("--del-set", &info->del_set); 238} 239 240/* Revision 2 */ 241 242static void 243set_target_help_v2(void) 244{ 245 printf("SET target options:\n" 246 " --add-set name flags [--exist] [--timeout n]\n" 247 " --del-set name flags\n" 248 " add/del src/dst IP/port from/to named sets,\n" 249 " where flags are the comma separated list of\n" 250 " 'src' and 'dst' specifications.\n"); 251} 252 253static const struct option set_target_opts_v2[] = { 254 {.name = "add-set", .has_arg = true, .val = '1'}, 255 {.name = "del-set", .has_arg = true, .val = '2'}, 256 {.name = "exist", .has_arg = false, .val = '3'}, 257 {.name = "timeout", .has_arg = true, .val = '4'}, 258 XT_GETOPT_TABLEEND, 259}; 260 261static void 262set_target_check_v2(unsigned int flags) 263{ 264 if (!(flags & (SET_TARGET_ADD|SET_TARGET_DEL))) 265 xtables_error(PARAMETER_PROBLEM, 266 "You must specify either `--add-set' or `--del-set'"); 267 if (!(flags & SET_TARGET_ADD)) { 268 if (flags & SET_TARGET_EXIST) 269 xtables_error(PARAMETER_PROBLEM, 270 "Flag `--exist' can be used with `--add-set' only"); 271 if (flags & SET_TARGET_TIMEOUT) 272 xtables_error(PARAMETER_PROBLEM, 273 "Option `--timeout' can be used with `--add-set' only"); 274 } 275} 276 277 278static void 279set_target_init_v2(struct xt_entry_target *target) 280{ 281 struct xt_set_info_target_v2 *info = 282 (struct xt_set_info_target_v2 *) target->data; 283 284 info->add_set.index = 285 info->del_set.index = IPSET_INVALID_ID; 286 info->timeout = UINT32_MAX; 287} 288 289static int 290set_target_parse_v2(int c, char **argv, int invert, unsigned int *flags, 291 const void *entry, struct xt_entry_target **target) 292{ 293 struct xt_set_info_target_v2 *myinfo = 294 (struct xt_set_info_target_v2 *) (*target)->data; 295 unsigned int timeout; 296 297 switch (c) { 298 case '1': /* --add-set <set> <flags> */ 299 parse_target(argv, invert, &myinfo->add_set, "add-set"); 300 *flags |= SET_TARGET_ADD; 301 break; 302 case '2': /* --del-set <set>[:<flags>] <flags> */ 303 parse_target(argv, invert, &myinfo->del_set, "del-set"); 304 *flags |= SET_TARGET_DEL; 305 break; 306 case '3': 307 myinfo->flags |= IPSET_FLAG_EXIST; 308 *flags |= SET_TARGET_EXIST; 309 break; 310 case '4': 311 if (!xtables_strtoui(optarg, NULL, &timeout, 0, UINT32_MAX - 1)) 312 xtables_error(PARAMETER_PROBLEM, 313 "Invalid value for option --timeout " 314 "or out of range 0-%u", UINT32_MAX - 1); 315 myinfo->timeout = timeout; 316 *flags |= SET_TARGET_TIMEOUT; 317 break; 318 } 319 return 1; 320} 321 322static void 323set_target_print_v2(const void *ip, const struct xt_entry_target *target, 324 int numeric) 325{ 326 const struct xt_set_info_target_v2 *info = (const void *)target->data; 327 328 print_target("add-set", &info->add_set); 329 if (info->flags & IPSET_FLAG_EXIST) 330 printf(" exist"); 331 if (info->timeout != UINT32_MAX) 332 printf(" timeout %u", info->timeout); 333 print_target("del-set", &info->del_set); 334} 335 336static void 337set_target_save_v2(const void *ip, const struct xt_entry_target *target) 338{ 339 const struct xt_set_info_target_v2 *info = (const void *)target->data; 340 341 print_target("--add-set", &info->add_set); 342 if (info->flags & IPSET_FLAG_EXIST) 343 printf(" --exist"); 344 if (info->timeout != UINT32_MAX) 345 printf(" --timeout %u", info->timeout); 346 print_target("--del-set", &info->del_set); 347} 348 349static struct xtables_target set_tg_reg[] = { 350 { 351 .name = "SET", 352 .revision = 0, 353 .version = XTABLES_VERSION, 354 .family = NFPROTO_IPV4, 355 .size = XT_ALIGN(sizeof(struct xt_set_info_target_v0)), 356 .userspacesize = XT_ALIGN(sizeof(struct xt_set_info_target_v0)), 357 .help = set_target_help_v0, 358 .init = set_target_init_v0, 359 .parse = set_target_parse_v0, 360 .final_check = set_target_check_v0, 361 .print = set_target_print_v0, 362 .save = set_target_save_v0, 363 .extra_opts = set_target_opts_v0, 364 }, 365 { 366 .name = "SET", 367 .revision = 1, 368 .version = XTABLES_VERSION, 369 .family = NFPROTO_UNSPEC, 370 .size = XT_ALIGN(sizeof(struct xt_set_info_target_v1)), 371 .userspacesize = XT_ALIGN(sizeof(struct xt_set_info_target_v1)), 372 .help = set_target_help_v0, 373 .init = set_target_init_v1, 374 .parse = set_target_parse_v1, 375 .final_check = set_target_check_v0, 376 .print = set_target_print_v1, 377 .save = set_target_save_v1, 378 .extra_opts = set_target_opts_v0, 379 }, 380 { 381 .name = "SET", 382 .revision = 2, 383 .version = XTABLES_VERSION, 384 .family = NFPROTO_UNSPEC, 385 .size = XT_ALIGN(sizeof(struct xt_set_info_target_v2)), 386 .userspacesize = XT_ALIGN(sizeof(struct xt_set_info_target_v2)), 387 .help = set_target_help_v2, 388 .init = set_target_init_v2, 389 .parse = set_target_parse_v2, 390 .final_check = set_target_check_v2, 391 .print = set_target_print_v2, 392 .save = set_target_save_v2, 393 .extra_opts = set_target_opts_v2, 394 }, 395}; 396 397void _init(void) 398{ 399 xtables_register_targets(set_tg_reg, ARRAY_SIZE(set_tg_reg)); 400} 401