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