libxt_iprange.c revision 73866357e4a7a0fdc1b293bf8863fee2bd56da9e
1/* Shared library add-on to iptables to add IP range matching support. */ 2#include <stdbool.h> 3#include <stdio.h> 4#include <netdb.h> 5#include <string.h> 6#include <stdlib.h> 7#include <getopt.h> 8 9#include <netinet/in.h> 10#include <xtables.h> 11#include <linux/netfilter.h> 12#include <linux/netfilter/xt_iprange.h> 13 14struct ipt_iprange { 15 /* Inclusive: network order. */ 16 __be32 min_ip, max_ip; 17}; 18 19struct ipt_iprange_info { 20 struct ipt_iprange src; 21 struct ipt_iprange dst; 22 23 /* Flags from above */ 24 uint8_t flags; 25}; 26 27enum { 28 F_SRCIP = 1 << 0, 29 F_DSTIP = 1 << 1, 30}; 31 32static void iprange_mt_help(void) 33{ 34 printf( 35"iprange match options:\n" 36"[!] --src-range ip[-ip] Match source IP in the specified range\n" 37"[!] --dst-range ip[-ip] Match destination IP in the specified range\n"); 38} 39 40static const struct option iprange_mt_opts[] = { 41 {.name = "src-range", .has_arg = true, .val = '1'}, 42 {.name = "dst-range", .has_arg = true, .val = '2'}, 43 XT_GETOPT_TABLEEND, 44}; 45 46static void 47iprange_parse_spec(const char *from, const char *to, union nf_inet_addr *range, 48 uint8_t family, const char *optname) 49{ 50 const char *spec[2] = {from, to}; 51 struct in6_addr *ia6; 52 struct in_addr *ia4; 53 unsigned int i; 54 55 memset(range, 0, sizeof(union nf_inet_addr) * 2); 56 57 if (family == NFPROTO_IPV6) { 58 for (i = 0; i < ARRAY_SIZE(spec); ++i) { 59 ia6 = xtables_numeric_to_ip6addr(spec[i]); 60 if (ia6 == NULL) 61 xtables_param_act(XTF_BAD_VALUE, "iprange", 62 optname, spec[i]); 63 range[i].in6 = *ia6; 64 } 65 } else { 66 for (i = 0; i < ARRAY_SIZE(spec); ++i) { 67 ia4 = xtables_numeric_to_ipaddr(spec[i]); 68 if (ia4 == NULL) 69 xtables_param_act(XTF_BAD_VALUE, "iprange", 70 optname, spec[i]); 71 range[i].in = *ia4; 72 } 73 } 74} 75 76static void iprange_parse_range(char *arg, union nf_inet_addr *range, 77 uint8_t family, const char *optname) 78{ 79 char *dash; 80 81 dash = strchr(arg, '-'); 82 if (dash == NULL) { 83 iprange_parse_spec(arg, arg, range, family, optname); 84 return; 85 } 86 87 *dash = '\0'; 88 iprange_parse_spec(arg, dash + 1, range, family, optname); 89 if (memcmp(&range[0], &range[1], sizeof(*range)) > 0) 90 fprintf(stderr, "xt_iprange: range %s-%s is reversed and " 91 "will never match\n", arg, dash + 1); 92} 93 94static int iprange_parse(int c, char **argv, int invert, unsigned int *flags, 95 const void *entry, struct xt_entry_match **match) 96{ 97 struct ipt_iprange_info *info = (struct ipt_iprange_info *)(*match)->data; 98 union nf_inet_addr range[2]; 99 100 switch (c) { 101 case '1': 102 if (*flags & IPRANGE_SRC) 103 xtables_error(PARAMETER_PROBLEM, 104 "iprange match: Only use --src-range ONCE!"); 105 *flags |= IPRANGE_SRC; 106 107 info->flags |= IPRANGE_SRC; 108 xtables_check_inverse(optarg, &invert, &optind, 0, argv); 109 if (invert) 110 info->flags |= IPRANGE_SRC_INV; 111 iprange_parse_range(optarg, range, NFPROTO_IPV4, "--src-range"); 112 info->src.min_ip = range[0].ip; 113 info->src.max_ip = range[1].ip; 114 break; 115 116 case '2': 117 if (*flags & IPRANGE_DST) 118 xtables_error(PARAMETER_PROBLEM, 119 "iprange match: Only use --dst-range ONCE!"); 120 *flags |= IPRANGE_DST; 121 122 info->flags |= IPRANGE_DST; 123 xtables_check_inverse(optarg, &invert, &optind, 0, argv); 124 if (invert) 125 info->flags |= IPRANGE_DST_INV; 126 127 iprange_parse_range(optarg, range, NFPROTO_IPV4, "--dst-range"); 128 info->dst.min_ip = range[0].ip; 129 info->dst.max_ip = range[1].ip; 130 break; 131 } 132 return 1; 133} 134 135static int 136iprange_mt4_parse(int c, char **argv, int invert, unsigned int *flags, 137 const void *entry, struct xt_entry_match **match) 138{ 139 struct xt_iprange_mtinfo *info = (void *)(*match)->data; 140 141 switch (c) { 142 case '1': /* --src-range */ 143 iprange_parse_range(optarg, &info->src_min, NFPROTO_IPV4, 144 "--src-range"); 145 info->flags |= IPRANGE_SRC; 146 if (invert) 147 info->flags |= IPRANGE_SRC_INV; 148 *flags |= F_SRCIP; 149 return true; 150 151 case '2': /* --dst-range */ 152 iprange_parse_range(optarg, &info->dst_min, NFPROTO_IPV4, 153 "--dst-range"); 154 info->flags |= IPRANGE_DST; 155 if (invert) 156 info->flags |= IPRANGE_DST_INV; 157 *flags |= F_DSTIP; 158 return true; 159 } 160 return false; 161} 162 163static int 164iprange_mt6_parse(int c, char **argv, int invert, unsigned int *flags, 165 const void *entry, struct xt_entry_match **match) 166{ 167 struct xt_iprange_mtinfo *info = (void *)(*match)->data; 168 169 switch (c) { 170 case '1': /* --src-range */ 171 iprange_parse_range(optarg, &info->src_min, NFPROTO_IPV6, 172 "--src-range"); 173 info->flags |= IPRANGE_SRC; 174 if (invert) 175 info->flags |= IPRANGE_SRC_INV; 176 *flags |= F_SRCIP; 177 return true; 178 179 case '2': /* --dst-range */ 180 iprange_parse_range(optarg, &info->dst_min, NFPROTO_IPV6, 181 "--dst-range"); 182 info->flags |= IPRANGE_DST; 183 if (invert) 184 info->flags |= IPRANGE_DST_INV; 185 *flags |= F_DSTIP; 186 return true; 187 } 188 return false; 189} 190 191static void iprange_mt_check(unsigned int flags) 192{ 193 if (flags == 0) 194 xtables_error(PARAMETER_PROBLEM, 195 "iprange match: You must specify `--src-range' or `--dst-range'"); 196} 197 198static void 199print_iprange(const struct ipt_iprange *range) 200{ 201 const unsigned char *byte_min, *byte_max; 202 203 byte_min = (const unsigned char *)&range->min_ip; 204 byte_max = (const unsigned char *)&range->max_ip; 205 printf(" %u.%u.%u.%u-%u.%u.%u.%u", 206 byte_min[0], byte_min[1], byte_min[2], byte_min[3], 207 byte_max[0], byte_max[1], byte_max[2], byte_max[3]); 208} 209 210static void iprange_print(const void *ip, const struct xt_entry_match *match, 211 int numeric) 212{ 213 const struct ipt_iprange_info *info = (const void *)match->data; 214 215 if (info->flags & IPRANGE_SRC) { 216 printf(" source IP range"); 217 if (info->flags & IPRANGE_SRC_INV) 218 printf(" !"); 219 print_iprange(&info->src); 220 } 221 if (info->flags & IPRANGE_DST) { 222 printf(" destination IP range"); 223 if (info->flags & IPRANGE_DST_INV) 224 printf(" !"); 225 print_iprange(&info->dst); 226 } 227} 228 229static void 230iprange_mt4_print(const void *ip, const struct xt_entry_match *match, 231 int numeric) 232{ 233 const struct xt_iprange_mtinfo *info = (const void *)match->data; 234 235 if (info->flags & IPRANGE_SRC) { 236 printf(" source IP range"); 237 if (info->flags & IPRANGE_SRC_INV) 238 printf(" !"); 239 /* 240 * ipaddr_to_numeric() uses a static buffer, so cannot 241 * combine the printf() calls. 242 */ 243 printf(" %s", xtables_ipaddr_to_numeric(&info->src_min.in)); 244 printf("-%s", xtables_ipaddr_to_numeric(&info->src_max.in)); 245 } 246 if (info->flags & IPRANGE_DST) { 247 printf(" destination IP range"); 248 if (info->flags & IPRANGE_DST_INV) 249 printf(" !"); 250 printf(" %s", xtables_ipaddr_to_numeric(&info->dst_min.in)); 251 printf("-%s", xtables_ipaddr_to_numeric(&info->dst_max.in)); 252 } 253} 254 255static void 256iprange_mt6_print(const void *ip, const struct xt_entry_match *match, 257 int numeric) 258{ 259 const struct xt_iprange_mtinfo *info = (const void *)match->data; 260 261 if (info->flags & IPRANGE_SRC) { 262 printf(" source IP range"); 263 if (info->flags & IPRANGE_SRC_INV) 264 printf(" !"); 265 /* 266 * ipaddr_to_numeric() uses a static buffer, so cannot 267 * combine the printf() calls. 268 */ 269 printf(" %s", xtables_ip6addr_to_numeric(&info->src_min.in6)); 270 printf("-%s", xtables_ip6addr_to_numeric(&info->src_max.in6)); 271 } 272 if (info->flags & IPRANGE_DST) { 273 printf(" destination IP range"); 274 if (info->flags & IPRANGE_DST_INV) 275 printf(" !"); 276 printf(" %s", xtables_ip6addr_to_numeric(&info->dst_min.in6)); 277 printf("-%s", xtables_ip6addr_to_numeric(&info->dst_max.in6)); 278 } 279} 280 281static void iprange_save(const void *ip, const struct xt_entry_match *match) 282{ 283 const struct ipt_iprange_info *info = (const void *)match->data; 284 285 if (info->flags & IPRANGE_SRC) { 286 if (info->flags & IPRANGE_SRC_INV) 287 printf(" !"); 288 printf(" --src-range"); 289 print_iprange(&info->src); 290 } 291 if (info->flags & IPRANGE_DST) { 292 if (info->flags & IPRANGE_DST_INV) 293 printf(" !"); 294 printf(" --dst-range"); 295 print_iprange(&info->dst); 296 } 297} 298 299static void iprange_mt4_save(const void *ip, const struct xt_entry_match *match) 300{ 301 const struct xt_iprange_mtinfo *info = (const void *)match->data; 302 303 if (info->flags & IPRANGE_SRC) { 304 if (info->flags & IPRANGE_SRC_INV) 305 printf(" !"); 306 printf(" --src-range %s", xtables_ipaddr_to_numeric(&info->src_min.in)); 307 printf("-%s", xtables_ipaddr_to_numeric(&info->src_max.in)); 308 } 309 if (info->flags & IPRANGE_DST) { 310 if (info->flags & IPRANGE_DST_INV) 311 printf(" !"); 312 printf(" --dst-range %s", xtables_ipaddr_to_numeric(&info->dst_min.in)); 313 printf("-%s", xtables_ipaddr_to_numeric(&info->dst_max.in)); 314 } 315} 316 317static void iprange_mt6_save(const void *ip, const struct xt_entry_match *match) 318{ 319 const struct xt_iprange_mtinfo *info = (const void *)match->data; 320 321 if (info->flags & IPRANGE_SRC) { 322 if (info->flags & IPRANGE_SRC_INV) 323 printf(" !"); 324 printf(" --src-range %s", xtables_ip6addr_to_numeric(&info->src_min.in6)); 325 printf("-%s", xtables_ip6addr_to_numeric(&info->src_max.in6)); 326 } 327 if (info->flags & IPRANGE_DST) { 328 if (info->flags & IPRANGE_DST_INV) 329 printf(" !"); 330 printf(" --dst-range %s", xtables_ip6addr_to_numeric(&info->dst_min.in6)); 331 printf("-%s", xtables_ip6addr_to_numeric(&info->dst_max.in6)); 332 } 333} 334 335static struct xtables_match iprange_mt_reg[] = { 336 { 337 .version = XTABLES_VERSION, 338 .name = "iprange", 339 .revision = 0, 340 .family = NFPROTO_IPV4, 341 .size = XT_ALIGN(sizeof(struct ipt_iprange_info)), 342 .userspacesize = XT_ALIGN(sizeof(struct ipt_iprange_info)), 343 .help = iprange_mt_help, 344 .parse = iprange_parse, 345 .final_check = iprange_mt_check, 346 .print = iprange_print, 347 .save = iprange_save, 348 .extra_opts = iprange_mt_opts, 349 }, 350 { 351 .version = XTABLES_VERSION, 352 .name = "iprange", 353 .revision = 1, 354 .family = NFPROTO_IPV4, 355 .size = XT_ALIGN(sizeof(struct xt_iprange_mtinfo)), 356 .userspacesize = XT_ALIGN(sizeof(struct xt_iprange_mtinfo)), 357 .help = iprange_mt_help, 358 .parse = iprange_mt4_parse, 359 .final_check = iprange_mt_check, 360 .print = iprange_mt4_print, 361 .save = iprange_mt4_save, 362 .extra_opts = iprange_mt_opts, 363 }, 364 { 365 .version = XTABLES_VERSION, 366 .name = "iprange", 367 .revision = 1, 368 .family = NFPROTO_IPV6, 369 .size = XT_ALIGN(sizeof(struct xt_iprange_mtinfo)), 370 .userspacesize = XT_ALIGN(sizeof(struct xt_iprange_mtinfo)), 371 .help = iprange_mt_help, 372 .parse = iprange_mt6_parse, 373 .final_check = iprange_mt_check, 374 .print = iprange_mt6_print, 375 .save = iprange_mt6_save, 376 .extra_opts = iprange_mt_opts, 377 }, 378}; 379 380void _init(void) 381{ 382 xtables_register_matches(iprange_mt_reg, ARRAY_SIZE(iprange_mt_reg)); 383} 384