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> /* get_kernel_version */
      7 #include <limits.h> /* INT_MAX in ip_tables.h */
      8 #include <linux/netfilter_ipv4/ip_tables.h>
      9 #include <net/netfilter/nf_nat.h>
     10 
     11 enum {
     12 	O_TO_DEST = 0,
     13 	O_RANDOM,
     14 	O_PERSISTENT,
     15 	O_X_TO_DEST, /* hidden flag */
     16 	F_TO_DEST   = 1 << O_TO_DEST,
     17 	F_RANDOM    = 1 << O_RANDOM,
     18 	F_X_TO_DEST = 1 << O_X_TO_DEST,
     19 };
     20 
     21 /* Dest 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_multi_range mr;
     27 };
     28 
     29 static void DNAT_help(void)
     30 {
     31 	printf(
     32 "DNAT target options:\n"
     33 " --to-destination [<ipaddr>[-<ipaddr>]][:port[-port]]\n"
     34 "				Address to map destination to.\n"
     35 "[--random] [--persistent]\n");
     36 }
     37 
     38 static const struct xt_option_entry DNAT_opts[] = {
     39 	{.name = "to-destination", .id = O_TO_DEST, .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_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_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 |= IP_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 |= IP_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 DNAT_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_DEST:
    169 		if (cb->xflags & F_X_TO_DEST) {
    170 			if (!kernel_version)
    171 				get_kernel_version();
    172 			if (kernel_version > LINUX_VERSION(2, 6, 10))
    173 				xtables_error(PARAMETER_PROBLEM,
    174 					   "DNAT: Multiple --to-destination not supported");
    175 		}
    176 		*cb->target = parse_to(cb->arg, portok, info);
    177 		/* WTF do we need this for?? */
    178 		if (cb->xflags & F_RANDOM)
    179 			info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
    180 		cb->xflags |= F_X_TO_DEST;
    181 		break;
    182 	case O_RANDOM:
    183 		if (cb->xflags & F_TO_DEST)
    184 			info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
    185 		break;
    186 	case O_PERSISTENT:
    187 		info->mr.range[0].flags |= IP_NAT_RANGE_PERSISTENT;
    188 		break;
    189 	}
    190 }
    191 
    192 static void print_range(const struct nf_nat_range *r)
    193 {
    194 	if (r->flags & IP_NAT_RANGE_MAP_IPS) {
    195 		struct in_addr a;
    196 
    197 		a.s_addr = r->min_ip;
    198 		printf("%s", xtables_ipaddr_to_numeric(&a));
    199 		if (r->max_ip != r->min_ip) {
    200 			a.s_addr = r->max_ip;
    201 			printf("-%s", xtables_ipaddr_to_numeric(&a));
    202 		}
    203 	}
    204 	if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
    205 		printf(":");
    206 		printf("%hu", ntohs(r->min.tcp.port));
    207 		if (r->max.tcp.port != r->min.tcp.port)
    208 			printf("-%hu", ntohs(r->max.tcp.port));
    209 	}
    210 }
    211 
    212 static void DNAT_print(const void *ip, const struct xt_entry_target *target,
    213                        int numeric)
    214 {
    215 	const struct ipt_natinfo *info = (const void *)target;
    216 	unsigned int i = 0;
    217 
    218 	printf(" to:");
    219 	for (i = 0; i < info->mr.rangesize; i++) {
    220 		print_range(&info->mr.range[i]);
    221 		if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM)
    222 			printf(" random");
    223 		if (info->mr.range[i].flags & IP_NAT_RANGE_PERSISTENT)
    224 			printf(" persistent");
    225 	}
    226 }
    227 
    228 static void DNAT_save(const void *ip, const struct xt_entry_target *target)
    229 {
    230 	const struct ipt_natinfo *info = (const void *)target;
    231 	unsigned int i = 0;
    232 
    233 	for (i = 0; i < info->mr.rangesize; i++) {
    234 		printf(" --to-destination ");
    235 		print_range(&info->mr.range[i]);
    236 		if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM)
    237 			printf(" --random");
    238 		if (info->mr.range[i].flags & IP_NAT_RANGE_PERSISTENT)
    239 			printf(" --persistent");
    240 	}
    241 }
    242 
    243 static struct xtables_target dnat_tg_reg = {
    244 	.name		= "DNAT",
    245 	.version	= XTABLES_VERSION,
    246 	.family		= NFPROTO_IPV4,
    247 	.size		= XT_ALIGN(sizeof(struct nf_nat_multi_range)),
    248 	.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_multi_range)),
    249 	.help		= DNAT_help,
    250 	.x6_parse	= DNAT_parse,
    251 	.print		= DNAT_print,
    252 	.save		= DNAT_save,
    253 	.x6_options	= DNAT_opts,
    254 };
    255 
    256 void _init(void)
    257 {
    258 	xtables_register_target(&dnat_tg_reg);
    259 }
    260