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_PERSISTENT,
     22 	O_X_TO_SRC,
     23 	F_TO_SRC   = 1 << O_TO_SRC,
     24 	F_RANDOM   = 1 << O_RANDOM,
     25 	F_X_TO_SRC = 1 << O_X_TO_SRC,
     26 };
     27 
     28 static void SNAT_help(void)
     29 {
     30 	printf(
     31 "SNAT target options:\n"
     32 " --to-source [<ipaddr>[-<ipaddr>]][:port[-port]]\n"
     33 "				Address to map source to.\n"
     34 "[--random] [--persistent]\n");
     35 }
     36 
     37 static const struct xt_option_entry SNAT_opts[] = {
     38 	{.name = "to-source", .id = O_TO_SRC, .type = XTTYPE_STRING,
     39 	 .flags = XTOPT_MAND | XTOPT_MULTI},
     40 	{.name = "random", .id = O_RANDOM, .type = XTTYPE_NONE},
     41 	{.name = "persistent", .id = O_PERSISTENT, .type = XTTYPE_NONE},
     42 	XTOPT_TABLEEND,
     43 };
     44 
     45 /* Ranges expected in network order. */
     46 static void
     47 parse_to(const char *orig_arg, int portok, struct nf_nat_range *range)
     48 {
     49 	char *arg, *start, *end = NULL, *colon = NULL, *dash, *error;
     50 	const struct in6_addr *ip;
     51 
     52 	arg = strdup(orig_arg);
     53 	if (arg == NULL)
     54 		xtables_error(RESOURCE_PROBLEM, "strdup");
     55 
     56 	start = strchr(arg, '[');
     57 	if (start == NULL) {
     58 		start = arg;
     59 		/* Lets assume one colon is port information. Otherwise its an IPv6 address */
     60 		colon = strchr(arg, ':');
     61 		if (colon && strchr(colon+1, ':'))
     62 			colon = NULL;
     63 	}
     64 	else {
     65 		start++;
     66 		end = strchr(start, ']');
     67 		if (end == NULL)
     68 			xtables_error(PARAMETER_PROBLEM,
     69 				      "Invalid address format");
     70 
     71 		*end = '\0';
     72 		colon = strchr(end + 1, ':');
     73 	}
     74 
     75 	if (colon) {
     76 		int port;
     77 
     78 		if (!portok)
     79 			xtables_error(PARAMETER_PROBLEM,
     80 				   "Need TCP, UDP, SCTP or DCCP with port specification");
     81 
     82 		range->flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
     83 
     84 		port = atoi(colon+1);
     85 		if (port <= 0 || port > 65535)
     86 			xtables_error(PARAMETER_PROBLEM,
     87 				   "Port `%s' not valid\n", colon+1);
     88 
     89 		error = strchr(colon+1, ':');
     90 		if (error)
     91 			xtables_error(PARAMETER_PROBLEM,
     92 				   "Invalid port:port syntax - use dash\n");
     93 
     94 		dash = strchr(colon, '-');
     95 		if (!dash) {
     96 			range->min_proto.tcp.port
     97 				= range->max_proto.tcp.port
     98 				= htons(port);
     99 		} else {
    100 			int maxport;
    101 
    102 			maxport = atoi(dash + 1);
    103 			if (maxport <= 0 || maxport > 65535)
    104 				xtables_error(PARAMETER_PROBLEM,
    105 					   "Port `%s' not valid\n", dash+1);
    106 			if (maxport < port)
    107 				/* People are stupid. */
    108 				xtables_error(PARAMETER_PROBLEM,
    109 					   "Port range `%s' funky\n", colon+1);
    110 			range->min_proto.tcp.port = htons(port);
    111 			range->max_proto.tcp.port = htons(maxport);
    112 		}
    113 		/* Starts with colon or [] colon? No IP info...*/
    114 		if (colon == arg || colon == arg+2) {
    115 			free(arg);
    116 			return;
    117 		}
    118 		*colon = '\0';
    119 	}
    120 
    121 	range->flags |= NF_NAT_RANGE_MAP_IPS;
    122 	dash = strchr(start, '-');
    123 	if (colon && dash && dash > colon)
    124 		dash = NULL;
    125 
    126 	if (dash)
    127 		*dash = '\0';
    128 
    129 	ip = xtables_numeric_to_ip6addr(start);
    130 	if (!ip)
    131 		xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
    132 			      start);
    133 	range->min_addr.in6 = *ip;
    134 	if (dash) {
    135 		ip = xtables_numeric_to_ip6addr(dash + 1);
    136 		if (!ip)
    137 			xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
    138 				      dash+1);
    139 		range->max_addr.in6 = *ip;
    140 	} else
    141 		range->max_addr = range->min_addr;
    142 
    143 	free(arg);
    144 	return;
    145 }
    146 
    147 static void SNAT_parse(struct xt_option_call *cb)
    148 {
    149 	const struct ip6t_entry *entry = cb->xt_entry;
    150 	struct nf_nat_range *range = cb->data;
    151 	int portok;
    152 
    153 	if (entry->ipv6.proto == IPPROTO_TCP ||
    154 	    entry->ipv6.proto == IPPROTO_UDP ||
    155 	    entry->ipv6.proto == IPPROTO_SCTP ||
    156 	    entry->ipv6.proto == IPPROTO_DCCP ||
    157 	    entry->ipv6.proto == IPPROTO_ICMP)
    158 		portok = 1;
    159 	else
    160 		portok = 0;
    161 
    162 	xtables_option_parse(cb);
    163 	switch (cb->entry->id) {
    164 	case O_TO_SRC:
    165 		if (cb->xflags & F_X_TO_SRC) {
    166 			if (!kernel_version)
    167 				get_kernel_version();
    168 			if (kernel_version > LINUX_VERSION(2, 6, 10))
    169 				xtables_error(PARAMETER_PROBLEM,
    170 					   "SNAT: Multiple --to-source not supported");
    171 		}
    172 		parse_to(cb->arg, portok, range);
    173 		break;
    174 	case O_PERSISTENT:
    175 		range->flags |= NF_NAT_RANGE_PERSISTENT;
    176 		break;
    177 	}
    178 }
    179 
    180 static void SNAT_fcheck(struct xt_fcheck_call *cb)
    181 {
    182 	static const unsigned int f = F_TO_SRC | F_RANDOM;
    183 	struct nf_nat_range *range = cb->data;
    184 
    185 	if ((cb->xflags & f) == f)
    186 		range->flags |= NF_NAT_RANGE_PROTO_RANDOM;
    187 }
    188 
    189 static void print_range(const struct nf_nat_range *range)
    190 {
    191 	if (range->flags & NF_NAT_RANGE_MAP_IPS) {
    192 		if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED)
    193 			printf("[");
    194 		printf("%s", xtables_ip6addr_to_numeric(&range->min_addr.in6));
    195 		if (memcmp(&range->min_addr, &range->max_addr,
    196 			   sizeof(range->min_addr)))
    197 			printf("-%s", xtables_ip6addr_to_numeric(&range->max_addr.in6));
    198 		if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED)
    199 			printf("]");
    200 	}
    201 	if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
    202 		printf(":");
    203 		printf("%hu", ntohs(range->min_proto.tcp.port));
    204 		if (range->max_proto.tcp.port != range->min_proto.tcp.port)
    205 			printf("-%hu", ntohs(range->max_proto.tcp.port));
    206 	}
    207 }
    208 
    209 static void SNAT_print(const void *ip, const struct xt_entry_target *target,
    210                        int numeric)
    211 {
    212 	const struct nf_nat_range *range = (const void *)target->data;
    213 
    214 	printf(" to:");
    215 	print_range(range);
    216 	if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
    217 		printf(" random");
    218 	if (range->flags & NF_NAT_RANGE_PERSISTENT)
    219 		printf(" persistent");
    220 }
    221 
    222 static void SNAT_save(const void *ip, const struct xt_entry_target *target)
    223 {
    224 	const struct nf_nat_range *range = (const void *)target->data;
    225 
    226 	printf(" --to-source ");
    227 	print_range(range);
    228 	if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
    229 		printf(" --random");
    230 	if (range->flags & NF_NAT_RANGE_PERSISTENT)
    231 		printf(" --persistent");
    232 }
    233 
    234 static struct xtables_target snat_tg_reg = {
    235 	.name		= "SNAT",
    236 	.version	= XTABLES_VERSION,
    237 	.family		= NFPROTO_IPV6,
    238 	.revision	= 1,
    239 	.size		= XT_ALIGN(sizeof(struct nf_nat_range)),
    240 	.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_range)),
    241 	.help		= SNAT_help,
    242 	.x6_parse	= SNAT_parse,
    243 	.x6_fcheck	= SNAT_fcheck,
    244 	.print		= SNAT_print,
    245 	.save		= SNAT_save,
    246 	.x6_options	= SNAT_opts,
    247 };
    248 
    249 void _init(void)
    250 {
    251 	xtables_register_target(&snat_tg_reg);
    252 }
    253