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