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