| /* Shared library add-on to iptables to add IP range matching support. */ |
| #include <stdio.h> |
| #include <netdb.h> |
| #include <string.h> |
| #include <stdlib.h> |
| #include <getopt.h> |
| |
| #include <netinet/in.h> |
| #include <xtables.h> |
| #include <linux/netfilter.h> |
| #include <linux/netfilter/xt_iprange.h> |
| #include <linux/netfilter_ipv4/ipt_iprange.h> |
| |
| enum { |
| F_SRCIP = 1 << 0, |
| F_DSTIP = 1 << 1, |
| }; |
| |
| static void iprange_mt_help(void) |
| { |
| printf( |
| "iprange match options:\n" |
| "[!] --src-range ip-ip Match source IP in the specified range\n" |
| "[!] --dst-range ip-ip Match destination IP in the specified range\n"); |
| } |
| |
| static const struct option iprange_mt_opts[] = { |
| {.name = "src-range", .has_arg = true, .val = '1'}, |
| {.name = "dst-range", .has_arg = true, .val = '2'}, |
| { .name = NULL } |
| }; |
| |
| static void |
| parse_iprange(char *arg, struct ipt_iprange *range) |
| { |
| char *dash; |
| const struct in_addr *ip; |
| |
| dash = strchr(arg, '-'); |
| if (dash != NULL) |
| *dash = '\0'; |
| |
| ip = xtables_numeric_to_ipaddr(arg); |
| if (!ip) |
| xtables_error(PARAMETER_PROBLEM, "iprange match: Bad IP address \"%s\"\n", |
| arg); |
| range->min_ip = ip->s_addr; |
| |
| if (dash != NULL) { |
| ip = xtables_numeric_to_ipaddr(dash+1); |
| if (!ip) |
| xtables_error(PARAMETER_PROBLEM, "iprange match: Bad IP address \"%s\"\n", |
| dash+1); |
| range->max_ip = ip->s_addr; |
| } else { |
| range->max_ip = range->min_ip; |
| } |
| } |
| |
| static int iprange_parse(int c, char **argv, int invert, unsigned int *flags, |
| const void *entry, struct xt_entry_match **match) |
| { |
| struct ipt_iprange_info *info = (struct ipt_iprange_info *)(*match)->data; |
| |
| switch (c) { |
| case '1': |
| if (*flags & IPRANGE_SRC) |
| xtables_error(PARAMETER_PROBLEM, |
| "iprange match: Only use --src-range ONCE!"); |
| *flags |= IPRANGE_SRC; |
| |
| info->flags |= IPRANGE_SRC; |
| xtables_check_inverse(optarg, &invert, &optind, 0); |
| if (invert) |
| info->flags |= IPRANGE_SRC_INV; |
| parse_iprange(optarg, &info->src); |
| |
| break; |
| |
| case '2': |
| if (*flags & IPRANGE_DST) |
| xtables_error(PARAMETER_PROBLEM, |
| "iprange match: Only use --dst-range ONCE!"); |
| *flags |= IPRANGE_DST; |
| |
| info->flags |= IPRANGE_DST; |
| xtables_check_inverse(optarg, &invert, &optind, 0); |
| if (invert) |
| info->flags |= IPRANGE_DST_INV; |
| |
| parse_iprange(optarg, &info->dst); |
| |
| break; |
| |
| default: |
| return 0; |
| } |
| return 1; |
| } |
| |
| static int |
| iprange_mt4_parse(int c, char **argv, int invert, unsigned int *flags, |
| const void *entry, struct xt_entry_match **match) |
| { |
| struct xt_iprange_mtinfo *info = (void *)(*match)->data; |
| const struct in_addr *ia; |
| char *end; |
| |
| switch (c) { |
| case '1': /* --src-range */ |
| end = strchr(optarg, '-'); |
| if (end == NULL) |
| xtables_param_act(XTF_BAD_VALUE, "iprange", "--src-range", optarg); |
| *end = '\0'; |
| ia = xtables_numeric_to_ipaddr(optarg); |
| if (ia == NULL) |
| xtables_param_act(XTF_BAD_VALUE, "iprange", "--src-range", optarg); |
| memcpy(&info->src_min.in, ia, sizeof(*ia)); |
| ia = xtables_numeric_to_ipaddr(end+1); |
| if (ia == NULL) |
| xtables_param_act(XTF_BAD_VALUE, "iprange", "--src-range", end + 1); |
| memcpy(&info->src_max.in, ia, sizeof(*ia)); |
| info->flags |= IPRANGE_SRC; |
| if (invert) |
| info->flags |= IPRANGE_SRC_INV; |
| *flags |= F_SRCIP; |
| return true; |
| |
| case '2': /* --dst-range */ |
| end = strchr(optarg, '-'); |
| if (end == NULL) |
| xtables_param_act(XTF_BAD_VALUE, "iprange", "--dst-range", optarg); |
| *end = '\0'; |
| ia = xtables_numeric_to_ipaddr(optarg); |
| if (ia == NULL) |
| xtables_param_act(XTF_BAD_VALUE, "iprange", "--dst-range", optarg); |
| memcpy(&info->dst_min.in, ia, sizeof(*ia)); |
| ia = xtables_numeric_to_ipaddr(end + 1); |
| if (ia == NULL) |
| xtables_param_act(XTF_BAD_VALUE, "iprange", "--dst-range", end + 1); |
| memcpy(&info->dst_max.in, ia, sizeof(*ia)); |
| info->flags |= IPRANGE_DST; |
| if (invert) |
| info->flags |= IPRANGE_DST_INV; |
| *flags |= F_DSTIP; |
| return true; |
| } |
| return false; |
| } |
| |
| static int |
| iprange_mt6_parse(int c, char **argv, int invert, unsigned int *flags, |
| const void *entry, struct xt_entry_match **match) |
| { |
| struct xt_iprange_mtinfo *info = (void *)(*match)->data; |
| const struct in6_addr *ia; |
| char *end; |
| |
| switch (c) { |
| case '1': /* --src-range */ |
| end = strchr(optarg, '-'); |
| if (end == NULL) |
| xtables_param_act(XTF_BAD_VALUE, "iprange", "--src-range", optarg); |
| *end = '\0'; |
| ia = xtables_numeric_to_ip6addr(optarg); |
| if (ia == NULL) |
| xtables_param_act(XTF_BAD_VALUE, "iprange", "--src-range", optarg); |
| memcpy(&info->src_min.in, ia, sizeof(*ia)); |
| ia = xtables_numeric_to_ip6addr(end+1); |
| if (ia == NULL) |
| xtables_param_act(XTF_BAD_VALUE, "iprange", "--src-range", end + 1); |
| memcpy(&info->src_max.in, ia, sizeof(*ia)); |
| info->flags |= IPRANGE_SRC; |
| if (invert) |
| info->flags |= IPRANGE_SRC_INV; |
| *flags |= F_SRCIP; |
| return true; |
| |
| case '2': /* --dst-range */ |
| end = strchr(optarg, '-'); |
| if (end == NULL) |
| xtables_param_act(XTF_BAD_VALUE, "iprange", "--dst-range", optarg); |
| *end = '\0'; |
| ia = xtables_numeric_to_ip6addr(optarg); |
| if (ia == NULL) |
| xtables_param_act(XTF_BAD_VALUE, "iprange", "--dst-range", optarg); |
| memcpy(&info->dst_min.in, ia, sizeof(*ia)); |
| ia = xtables_numeric_to_ip6addr(end + 1); |
| if (ia == NULL) |
| xtables_param_act(XTF_BAD_VALUE, "iprange", "--dst-range", end + 1); |
| memcpy(&info->dst_max.in, ia, sizeof(*ia)); |
| info->flags |= IPRANGE_DST; |
| if (invert) |
| info->flags |= IPRANGE_DST_INV; |
| *flags |= F_DSTIP; |
| return true; |
| } |
| return false; |
| } |
| |
| static void iprange_mt_check(unsigned int flags) |
| { |
| if (flags == 0) |
| xtables_error(PARAMETER_PROBLEM, |
| "iprange match: You must specify `--src-range' or `--dst-range'"); |
| } |
| |
| static void |
| print_iprange(const struct ipt_iprange *range) |
| { |
| const unsigned char *byte_min, *byte_max; |
| |
| byte_min = (const unsigned char *)&range->min_ip; |
| byte_max = (const unsigned char *)&range->max_ip; |
| printf("%u.%u.%u.%u-%u.%u.%u.%u ", |
| byte_min[0], byte_min[1], byte_min[2], byte_min[3], |
| byte_max[0], byte_max[1], byte_max[2], byte_max[3]); |
| } |
| |
| static void iprange_print(const void *ip, const struct xt_entry_match *match, |
| int numeric) |
| { |
| const struct ipt_iprange_info *info = (const void *)match->data; |
| |
| if (info->flags & IPRANGE_SRC) { |
| printf("source IP range "); |
| if (info->flags & IPRANGE_SRC_INV) |
| printf("! "); |
| print_iprange(&info->src); |
| } |
| if (info->flags & IPRANGE_DST) { |
| printf("destination IP range "); |
| if (info->flags & IPRANGE_DST_INV) |
| printf("! "); |
| print_iprange(&info->dst); |
| } |
| } |
| |
| static void |
| iprange_mt4_print(const void *ip, const struct xt_entry_match *match, |
| int numeric) |
| { |
| const struct xt_iprange_mtinfo *info = (const void *)match->data; |
| |
| if (info->flags & IPRANGE_SRC) { |
| printf("source IP range "); |
| if (info->flags & IPRANGE_SRC_INV) |
| printf("! "); |
| /* |
| * ipaddr_to_numeric() uses a static buffer, so cannot |
| * combine the printf() calls. |
| */ |
| printf("%s", xtables_ipaddr_to_numeric(&info->src_min.in)); |
| printf("-%s ", xtables_ipaddr_to_numeric(&info->src_max.in)); |
| } |
| if (info->flags & IPRANGE_DST) { |
| printf("destination IP range "); |
| if (info->flags & IPRANGE_DST_INV) |
| printf("! "); |
| printf("%s", xtables_ipaddr_to_numeric(&info->dst_min.in)); |
| printf("-%s ", xtables_ipaddr_to_numeric(&info->dst_max.in)); |
| } |
| } |
| |
| static void |
| iprange_mt6_print(const void *ip, const struct xt_entry_match *match, |
| int numeric) |
| { |
| const struct xt_iprange_mtinfo *info = (const void *)match->data; |
| |
| if (info->flags & IPRANGE_SRC) { |
| printf("source IP range "); |
| if (info->flags & IPRANGE_SRC_INV) |
| printf("! "); |
| /* |
| * ipaddr_to_numeric() uses a static buffer, so cannot |
| * combine the printf() calls. |
| */ |
| printf("%s", xtables_ip6addr_to_numeric(&info->src_min.in6)); |
| printf("-%s ", xtables_ip6addr_to_numeric(&info->src_max.in6)); |
| } |
| if (info->flags & IPRANGE_DST) { |
| printf("destination IP range "); |
| if (info->flags & IPRANGE_DST_INV) |
| printf("! "); |
| printf("%s", xtables_ip6addr_to_numeric(&info->dst_min.in6)); |
| printf("-%s ", xtables_ip6addr_to_numeric(&info->dst_max.in6)); |
| } |
| } |
| |
| static void iprange_save(const void *ip, const struct xt_entry_match *match) |
| { |
| const struct ipt_iprange_info *info = (const void *)match->data; |
| |
| if (info->flags & IPRANGE_SRC) { |
| if (info->flags & IPRANGE_SRC_INV) |
| printf("! "); |
| printf("--src-range "); |
| print_iprange(&info->src); |
| if (info->flags & IPRANGE_DST) |
| fputc(' ', stdout); |
| } |
| if (info->flags & IPRANGE_DST) { |
| if (info->flags & IPRANGE_DST_INV) |
| printf("! "); |
| printf("--dst-range "); |
| print_iprange(&info->dst); |
| } |
| } |
| |
| static void iprange_mt4_save(const void *ip, const struct xt_entry_match *match) |
| { |
| const struct xt_iprange_mtinfo *info = (const void *)match->data; |
| |
| if (info->flags & IPRANGE_SRC) { |
| if (info->flags & IPRANGE_SRC_INV) |
| printf("! "); |
| printf("--src-range %s", xtables_ipaddr_to_numeric(&info->src_min.in)); |
| printf("-%s ", xtables_ipaddr_to_numeric(&info->src_max.in)); |
| } |
| if (info->flags & IPRANGE_DST) { |
| if (info->flags & IPRANGE_DST_INV) |
| printf("! "); |
| printf("--dst-range %s", xtables_ipaddr_to_numeric(&info->dst_min.in)); |
| printf("-%s ", xtables_ipaddr_to_numeric(&info->dst_max.in)); |
| } |
| } |
| |
| static void iprange_mt6_save(const void *ip, const struct xt_entry_match *match) |
| { |
| const struct xt_iprange_mtinfo *info = (const void *)match->data; |
| |
| if (info->flags & IPRANGE_SRC) { |
| if (info->flags & IPRANGE_SRC_INV) |
| printf("! "); |
| printf("--src-range %s", xtables_ip6addr_to_numeric(&info->src_min.in6)); |
| printf("-%s ", xtables_ip6addr_to_numeric(&info->src_max.in6)); |
| } |
| if (info->flags & IPRANGE_DST) { |
| if (info->flags & IPRANGE_DST_INV) |
| printf("! "); |
| printf("--dst-range %s", xtables_ip6addr_to_numeric(&info->dst_min.in6)); |
| printf("-%s ", xtables_ip6addr_to_numeric(&info->dst_max.in6)); |
| } |
| } |
| |
| static struct xtables_match iprange_mt_reg[] = { |
| { |
| .version = XTABLES_VERSION, |
| .name = "iprange", |
| .revision = 0, |
| .family = NFPROTO_IPV4, |
| .size = XT_ALIGN(sizeof(struct ipt_iprange_info)), |
| .userspacesize = XT_ALIGN(sizeof(struct ipt_iprange_info)), |
| .help = iprange_mt_help, |
| .parse = iprange_parse, |
| .final_check = iprange_mt_check, |
| .print = iprange_print, |
| .save = iprange_save, |
| .extra_opts = iprange_mt_opts, |
| }, |
| { |
| .version = XTABLES_VERSION, |
| .name = "iprange", |
| .revision = 1, |
| .family = NFPROTO_IPV4, |
| .size = XT_ALIGN(sizeof(struct xt_iprange_mtinfo)), |
| .userspacesize = XT_ALIGN(sizeof(struct xt_iprange_mtinfo)), |
| .help = iprange_mt_help, |
| .parse = iprange_mt4_parse, |
| .final_check = iprange_mt_check, |
| .print = iprange_mt4_print, |
| .save = iprange_mt4_save, |
| .extra_opts = iprange_mt_opts, |
| }, |
| { |
| .version = XTABLES_VERSION, |
| .name = "iprange", |
| .revision = 1, |
| .family = NFPROTO_IPV6, |
| .size = XT_ALIGN(sizeof(struct xt_iprange_mtinfo)), |
| .userspacesize = XT_ALIGN(sizeof(struct xt_iprange_mtinfo)), |
| .help = iprange_mt_help, |
| .parse = iprange_mt6_parse, |
| .final_check = iprange_mt_check, |
| .print = iprange_mt6_print, |
| .save = iprange_mt6_save, |
| .extra_opts = iprange_mt_opts, |
| }, |
| }; |
| |
| void _init(void) |
| { |
| xtables_register_matches(iprange_mt_reg, ARRAY_SIZE(iprange_mt_reg)); |
| } |