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