Home | History | Annotate | Download | only in extensions
      1 /* Shared library add-on to iptables to add destination-NAT support. */
      2 #include <stdio.h>
      3 #include <netdb.h>
      4 #include <string.h>
      5 #include <stdlib.h>
      6 #include <getopt.h>
      7 #include <iptables.h>
      8 #include <linux/netfilter_ipv4/ip_tables.h>
      9 #include <linux/netfilter_ipv4/ip_nat_rule.h>
     10 #include <netinet/in.h>
     11 
     12 /* Dest NAT data consists of a multi-range, indicating where to map
     13    to. */
     14 struct ipt_natinfo
     15 {
     16 	struct ipt_entry_target t;
     17 	struct ip_nat_multi_range mr;
     18 };
     19 
     20 /* Function which prints out usage message. */
     21 static void
     22 help(void)
     23 {
     24 	printf(
     25 "DNAT v%s options:\n"
     26 " --to-destination <ipaddr>[-<ipaddr>][:port-port]\n"
     27 "				Address to map destination to.\n"
     28 "				(You can use this more than once)\n\n",
     29 IPTABLES_VERSION);
     30 }
     31 
     32 static struct option opts[] = {
     33 	{ "to-destination", 1, 0, '1' },
     34 	{ 0 }
     35 };
     36 
     37 static struct ipt_natinfo *
     38 append_range(struct ipt_natinfo *info, const struct ip_nat_range *range)
     39 {
     40 	unsigned int size;
     41 
     42 	/* One rangesize already in struct ipt_natinfo */
     43 	size = IPT_ALIGN(sizeof(*info) + info->mr.rangesize * sizeof(*range));
     44 
     45 	info = realloc(info, size);
     46 	if (!info)
     47 		exit_error(OTHER_PROBLEM, "Out of memory\n");
     48 
     49 	info->t.u.target_size = size;
     50 	info->mr.range[info->mr.rangesize] = *range;
     51 	info->mr.rangesize++;
     52 
     53 	return info;
     54 }
     55 
     56 /* Ranges expected in network order. */
     57 static struct ipt_entry_target *
     58 parse_to(char *arg, int portok, struct ipt_natinfo *info)
     59 {
     60 	struct ip_nat_range range;
     61 	char *colon, *dash, *error;
     62 	struct in_addr *ip;
     63 
     64 	memset(&range, 0, sizeof(range));
     65 	colon = strchr(arg, ':');
     66 
     67 	if (colon) {
     68 		int port;
     69 
     70 		if (!portok)
     71 			exit_error(PARAMETER_PROBLEM,
     72 				   "Need TCP or UDP with port specification");
     73 
     74 		range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
     75 
     76 		port = atoi(colon+1);
     77 		if (port <= 0 || port > 65535)
     78 			exit_error(PARAMETER_PROBLEM,
     79 				   "Port `%s' not valid\n", colon+1);
     80 
     81 		error = strchr(colon+1, ':');
     82 		if (error)
     83 			exit_error(PARAMETER_PROBLEM,
     84 				   "Invalid port:port syntax - use dash\n");
     85 
     86 		dash = strchr(colon, '-');
     87 		if (!dash) {
     88 			range.min.tcp.port
     89 				= range.max.tcp.port
     90 				= htons(port);
     91 		} else {
     92 			int maxport;
     93 
     94 			maxport = atoi(dash + 1);
     95 			if (maxport <= 0 || maxport > 65535)
     96 				exit_error(PARAMETER_PROBLEM,
     97 					   "Port `%s' not valid\n", dash+1);
     98 			if (maxport < port)
     99 				/* People are stupid. */
    100 				exit_error(PARAMETER_PROBLEM,
    101 					   "Port range `%s' funky\n", colon+1);
    102 			range.min.tcp.port = htons(port);
    103 			range.max.tcp.port = htons(maxport);
    104 		}
    105 		/* Starts with a colon? No IP info...*/
    106 		if (colon == arg)
    107 			return &(append_range(info, &range)->t);
    108 		*colon = '\0';
    109 	}
    110 
    111 	range.flags |= IP_NAT_RANGE_MAP_IPS;
    112 	dash = strchr(arg, '-');
    113 	if (colon && dash && dash > colon)
    114 		dash = NULL;
    115 
    116 	if (dash)
    117 		*dash = '\0';
    118 
    119 	ip = dotted_to_addr(arg);
    120 	if (!ip)
    121 		exit_error(PARAMETER_PROBLEM, "Bad IP address `%s'\n",
    122 			   arg);
    123 	range.min_ip = ip->s_addr;
    124 	if (dash) {
    125 		ip = dotted_to_addr(dash+1);
    126 		if (!ip)
    127 			exit_error(PARAMETER_PROBLEM, "Bad IP address `%s'\n",
    128 				   dash+1);
    129 		range.max_ip = ip->s_addr;
    130 	} else
    131 		range.max_ip = range.min_ip;
    132 
    133 	return &(append_range(info, &range)->t);
    134 }
    135 
    136 /* Function which parses command options; returns true if it
    137    ate an option */
    138 static int
    139 parse(int c, char **argv, int invert, unsigned int *flags,
    140       const struct ipt_entry *entry,
    141       struct ipt_entry_target **target)
    142 {
    143 	struct ipt_natinfo *info = (void *)*target;
    144 	int portok;
    145 
    146 	if (entry->ip.proto == IPPROTO_TCP
    147 	    || entry->ip.proto == IPPROTO_UDP
    148 	    || entry->ip.proto == IPPROTO_ICMP)
    149 		portok = 1;
    150 	else
    151 		portok = 0;
    152 
    153 	switch (c) {
    154 	case '1':
    155 		if (check_inverse(optarg, &invert, NULL, 0))
    156 			exit_error(PARAMETER_PROBLEM,
    157 				   "Unexpected `!' after --to-destination");
    158 
    159 		if (*flags) {
    160 			if (!kernel_version)
    161 				get_kernel_version();
    162 			if (kernel_version > LINUX_VERSION(2, 6, 10))
    163 				exit_error(PARAMETER_PROBLEM,
    164 					   "Multiple --to-destination not supported");
    165 		}
    166 		*target = parse_to(optarg, portok, info);
    167 		*flags = 1;
    168 		return 1;
    169 
    170 	default:
    171 		return 0;
    172 	}
    173 }
    174 
    175 /* Final check; must have specfied --to-source. */
    176 static void final_check(unsigned int flags)
    177 {
    178 	if (!flags)
    179 		exit_error(PARAMETER_PROBLEM,
    180 			   "You must specify --to-destination");
    181 }
    182 
    183 static void print_range(const struct ip_nat_range *r)
    184 {
    185 	if (r->flags & IP_NAT_RANGE_MAP_IPS) {
    186 		struct in_addr a;
    187 
    188 		a.s_addr = r->min_ip;
    189 		printf("%s", addr_to_dotted(&a));
    190 		if (r->max_ip != r->min_ip) {
    191 			a.s_addr = r->max_ip;
    192 			printf("-%s", addr_to_dotted(&a));
    193 		}
    194 	}
    195 	if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
    196 		printf(":");
    197 		printf("%hu", ntohs(r->min.tcp.port));
    198 		if (r->max.tcp.port != r->min.tcp.port)
    199 			printf("-%hu", ntohs(r->max.tcp.port));
    200 	}
    201 }
    202 
    203 /* Prints out the targinfo. */
    204 static void
    205 print(const struct ipt_ip *ip,
    206       const struct ipt_entry_target *target,
    207       int numeric)
    208 {
    209 	struct ipt_natinfo *info = (void *)target;
    210 	unsigned int i = 0;
    211 
    212 	printf("to:");
    213 	for (i = 0; i < info->mr.rangesize; i++) {
    214 		print_range(&info->mr.range[i]);
    215 		printf(" ");
    216 	}
    217 }
    218 
    219 /* Saves the union ipt_targinfo in parsable form to stdout. */
    220 static void
    221 save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
    222 {
    223 	struct ipt_natinfo *info = (void *)target;
    224 	unsigned int i = 0;
    225 
    226 	for (i = 0; i < info->mr.rangesize; i++) {
    227 		printf("--to-destination ");
    228 		print_range(&info->mr.range[i]);
    229 		printf(" ");
    230 	}
    231 }
    232 
    233 static struct iptables_target dnat = {
    234 	.next		= NULL,
    235 	.name		= "DNAT",
    236 	.version	= IPTABLES_VERSION,
    237 	.size		= IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
    238 	.userspacesize	= IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
    239 	.help		= &help,
    240 	.parse		= &parse,
    241 	.final_check	= &final_check,
    242 	.print		= &print,
    243 	.save		= &save,
    244 	.extra_opts	= opts
    245 };
    246 
    247 void ipt_DNAT_init(void)
    248 {
    249 	register_target(&dnat);
    250 }
    251