Home | History | Annotate | Download | only in extensions
      1 /*
      2  * Copyright (c) 2011 Patrick McHardy <kaber (at) trash.net>
      3  *
      4  * Based on Rusty Russell's IPv4 SNAT target. Development of IPv6 NAT
      5  * funded by Astaro.
      6  */
      7 
      8 #include <stdio.h>
      9 #include <netdb.h>
     10 #include <string.h>
     11 #include <stdlib.h>
     12 #include <xtables.h>
     13 #include <iptables.h>
     14 #include <limits.h> /* INT_MAX in ip_tables.h */
     15 #include <linux/netfilter_ipv6/ip6_tables.h>
     16 #include <linux/netfilter/nf_nat.h>
     17 
     18 enum {
     19 	O_TO_SRC = 0,
     20 	O_RANDOM,
     21 	O_RANDOM_FULLY,
     22 	O_PERSISTENT,
     23 	O_X_TO_SRC,
     24 	F_TO_SRC       = 1 << O_TO_SRC,
     25 	F_RANDOM       = 1 << O_RANDOM,
     26 	F_RANDOM_FULLY = 1 << O_RANDOM_FULLY,
     27 	F_X_TO_SRC     = 1 << O_X_TO_SRC,
     28 };
     29 
     30 static void SNAT_help(void)
     31 {
     32 	printf(
     33 "SNAT target options:\n"
     34 " --to-source [<ipaddr>[-<ipaddr>]][:port[-port]]\n"
     35 "				Address to map source to.\n"
     36 "[--random] [--random-fully] [--persistent]\n");
     37 }
     38 
     39 static const struct xt_option_entry SNAT_opts[] = {
     40 	{.name = "to-source", .id = O_TO_SRC, .type = XTTYPE_STRING,
     41 	 .flags = XTOPT_MAND | XTOPT_MULTI},
     42 	{.name = "random", .id = O_RANDOM, .type = XTTYPE_NONE},
     43 	{.name = "random-fully", .id = O_RANDOM_FULLY, .type = XTTYPE_NONE},
     44 	{.name = "persistent", .id = O_PERSISTENT, .type = XTTYPE_NONE},
     45 	XTOPT_TABLEEND,
     46 };
     47 
     48 /* Ranges expected in network order. */
     49 static void
     50 parse_to(const char *orig_arg, int portok, struct nf_nat_range *range)
     51 {
     52 	char *arg, *start, *end = NULL, *colon = NULL, *dash, *error;
     53 	const struct in6_addr *ip;
     54 
     55 	arg = strdup(orig_arg);
     56 	if (arg == NULL)
     57 		xtables_error(RESOURCE_PROBLEM, "strdup");
     58 
     59 	start = strchr(arg, '[');
     60 	if (start == NULL) {
     61 		start = arg;
     62 		/* Lets assume one colon is port information. Otherwise its an IPv6 address */
     63 		colon = strchr(arg, ':');
     64 		if (colon && strchr(colon+1, ':'))
     65 			colon = NULL;
     66 	}
     67 	else {
     68 		start++;
     69 		end = strchr(start, ']');
     70 		if (end == NULL)
     71 			xtables_error(PARAMETER_PROBLEM,
     72 				      "Invalid address format");
     73 
     74 		*end = '\0';
     75 		colon = strchr(end + 1, ':');
     76 	}
     77 
     78 	if (colon) {
     79 		int port;
     80 
     81 		if (!portok)
     82 			xtables_error(PARAMETER_PROBLEM,
     83 				   "Need TCP, UDP, SCTP or DCCP with port specification");
     84 
     85 		range->flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
     86 
     87 		port = atoi(colon+1);
     88 		if (port <= 0 || port > 65535)
     89 			xtables_error(PARAMETER_PROBLEM,
     90 				   "Port `%s' not valid\n", colon+1);
     91 
     92 		error = strchr(colon+1, ':');
     93 		if (error)
     94 			xtables_error(PARAMETER_PROBLEM,
     95 				   "Invalid port:port syntax - use dash\n");
     96 
     97 		dash = strchr(colon, '-');
     98 		if (!dash) {
     99 			range->min_proto.tcp.port
    100 				= range->max_proto.tcp.port
    101 				= htons(port);
    102 		} else {
    103 			int maxport;
    104 
    105 			maxport = atoi(dash + 1);
    106 			if (maxport <= 0 || maxport > 65535)
    107 				xtables_error(PARAMETER_PROBLEM,
    108 					   "Port `%s' not valid\n", dash+1);
    109 			if (maxport < port)
    110 				/* People are stupid. */
    111 				xtables_error(PARAMETER_PROBLEM,
    112 					   "Port range `%s' funky\n", colon+1);
    113 			range->min_proto.tcp.port = htons(port);
    114 			range->max_proto.tcp.port = htons(maxport);
    115 		}
    116 		/* Starts with colon or [] colon? No IP info...*/
    117 		if (colon == arg || colon == arg+2) {
    118 			free(arg);
    119 			return;
    120 		}
    121 		*colon = '\0';
    122 	}
    123 
    124 	range->flags |= NF_NAT_RANGE_MAP_IPS;
    125 	dash = strchr(start, '-');
    126 	if (colon && dash && dash > colon)
    127 		dash = NULL;
    128 
    129 	if (dash)
    130 		*dash = '\0';
    131 
    132 	ip = xtables_numeric_to_ip6addr(start);
    133 	if (!ip)
    134 		xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
    135 			      start);
    136 	range->min_addr.in6 = *ip;
    137 	if (dash) {
    138 		ip = xtables_numeric_to_ip6addr(dash + 1);
    139 		if (!ip)
    140 			xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
    141 				      dash+1);
    142 		range->max_addr.in6 = *ip;
    143 	} else
    144 		range->max_addr = range->min_addr;
    145 
    146 	free(arg);
    147 	return;
    148 }
    149 
    150 static void SNAT_parse(struct xt_option_call *cb)
    151 {
    152 	const struct ip6t_entry *entry = cb->xt_entry;
    153 	struct nf_nat_range *range = cb->data;
    154 	int portok;
    155 
    156 	if (entry->ipv6.proto == IPPROTO_TCP ||
    157 	    entry->ipv6.proto == IPPROTO_UDP ||
    158 	    entry->ipv6.proto == IPPROTO_SCTP ||
    159 	    entry->ipv6.proto == IPPROTO_DCCP ||
    160 	    entry->ipv6.proto == IPPROTO_ICMP)
    161 		portok = 1;
    162 	else
    163 		portok = 0;
    164 
    165 	xtables_option_parse(cb);
    166 	switch (cb->entry->id) {
    167 	case O_TO_SRC:
    168 		if (cb->xflags & F_X_TO_SRC) {
    169 			if (!kernel_version)
    170 				get_kernel_version();
    171 			if (kernel_version > LINUX_VERSION(2, 6, 10))
    172 				xtables_error(PARAMETER_PROBLEM,
    173 					   "SNAT: Multiple --to-source not supported");
    174 		}
    175 		parse_to(cb->arg, portok, range);
    176 		break;
    177 	case O_PERSISTENT:
    178 		range->flags |= NF_NAT_RANGE_PERSISTENT;
    179 		break;
    180 	}
    181 }
    182 
    183 static void SNAT_fcheck(struct xt_fcheck_call *cb)
    184 {
    185 	static const unsigned int f = F_TO_SRC | F_RANDOM;
    186 	static const unsigned int r = F_TO_SRC | F_RANDOM_FULLY;
    187 	struct nf_nat_range *range = cb->data;
    188 
    189 	if ((cb->xflags & f) == f)
    190 		range->flags |= NF_NAT_RANGE_PROTO_RANDOM;
    191 	if ((cb->xflags & r) == r)
    192 		range->flags |= NF_NAT_RANGE_PROTO_RANDOM_FULLY;
    193 }
    194 
    195 static void print_range(const struct nf_nat_range *range)
    196 {
    197 	if (range->flags & NF_NAT_RANGE_MAP_IPS) {
    198 		if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED)
    199 			printf("[");
    200 		printf("%s", xtables_ip6addr_to_numeric(&range->min_addr.in6));
    201 		if (memcmp(&range->min_addr, &range->max_addr,
    202 			   sizeof(range->min_addr)))
    203 			printf("-%s", xtables_ip6addr_to_numeric(&range->max_addr.in6));
    204 		if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED)
    205 			printf("]");
    206 	}
    207 	if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
    208 		printf(":");
    209 		printf("%hu", ntohs(range->min_proto.tcp.port));
    210 		if (range->max_proto.tcp.port != range->min_proto.tcp.port)
    211 			printf("-%hu", ntohs(range->max_proto.tcp.port));
    212 	}
    213 }
    214 
    215 static void SNAT_print(const void *ip, const struct xt_entry_target *target,
    216                        int numeric)
    217 {
    218 	const struct nf_nat_range *range = (const void *)target->data;
    219 
    220 	printf(" to:");
    221 	print_range(range);
    222 	if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
    223 		printf(" random");
    224 	if (range->flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY)
    225 		printf(" random-fully");
    226 	if (range->flags & NF_NAT_RANGE_PERSISTENT)
    227 		printf(" persistent");
    228 }
    229 
    230 static void SNAT_save(const void *ip, const struct xt_entry_target *target)
    231 {
    232 	const struct nf_nat_range *range = (const void *)target->data;
    233 
    234 	printf(" --to-source ");
    235 	print_range(range);
    236 	if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
    237 		printf(" --random");
    238 	if (range->flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY)
    239 		printf(" --random-fully");
    240 	if (range->flags & NF_NAT_RANGE_PERSISTENT)
    241 		printf(" --persistent");
    242 }
    243 
    244 static void print_range_xlate(const struct nf_nat_range *range,
    245 			      struct xt_xlate *xl)
    246 {
    247 	bool proto_specified = range->flags & NF_NAT_RANGE_PROTO_SPECIFIED;
    248 
    249 	if (range->flags & NF_NAT_RANGE_MAP_IPS) {
    250 		xt_xlate_add(xl, "%s%s%s",
    251 			     proto_specified ? "[" : "",
    252 			     xtables_ip6addr_to_numeric(&range->min_addr.in6),
    253 			     proto_specified ? "]" : "");
    254 
    255 		if (memcmp(&range->min_addr, &range->max_addr,
    256 			   sizeof(range->min_addr))) {
    257 			xt_xlate_add(xl, "-%s%s%s",
    258 				     proto_specified ? "[" : "",
    259 				     xtables_ip6addr_to_numeric(&range->max_addr.in6),
    260 				     proto_specified ? "]" : "");
    261 		}
    262 	}
    263 	if (proto_specified) {
    264 		xt_xlate_add(xl, ":%hu", ntohs(range->min_proto.tcp.port));
    265 
    266 		if (range->max_proto.tcp.port != range->min_proto.tcp.port)
    267 			xt_xlate_add(xl, "-%hu",
    268 				   ntohs(range->max_proto.tcp.port));
    269 	}
    270 }
    271 
    272 static int SNAT_xlate(struct xt_xlate *xl,
    273 		      const struct xt_xlate_tg_params *params)
    274 {
    275 	const struct nf_nat_range *range = (const void *)params->target->data;
    276 	bool sep_need = false;
    277 	const char *sep = " ";
    278 
    279 	xt_xlate_add(xl, "snat to ");
    280 	print_range_xlate(range, xl);
    281 	if (range->flags & NF_NAT_RANGE_PROTO_RANDOM) {
    282 		xt_xlate_add(xl, " random");
    283 		sep_need = true;
    284 	}
    285 	if (range->flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY) {
    286 		if (sep_need)
    287 			sep = ",";
    288 		xt_xlate_add(xl, "%sfully-random", sep);
    289 		sep_need = true;
    290 	}
    291 	if (range->flags & NF_NAT_RANGE_PERSISTENT) {
    292 		if (sep_need)
    293 			sep = ",";
    294 		xt_xlate_add(xl, "%spersistent", sep);
    295 	}
    296 
    297 	return 1;
    298 }
    299 
    300 static struct xtables_target snat_tg_reg = {
    301 	.name		= "SNAT",
    302 	.version	= XTABLES_VERSION,
    303 	.family		= NFPROTO_IPV6,
    304 	.revision	= 1,
    305 	.size		= XT_ALIGN(sizeof(struct nf_nat_range)),
    306 	.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_range)),
    307 	.help		= SNAT_help,
    308 	.x6_parse	= SNAT_parse,
    309 	.x6_fcheck	= SNAT_fcheck,
    310 	.print		= SNAT_print,
    311 	.save		= SNAT_save,
    312 	.x6_options	= SNAT_opts,
    313 	.xlate		= SNAT_xlate,
    314 };
    315 
    316 void _init(void)
    317 {
    318 	xtables_register_target(&snat_tg_reg);
    319 }
    320