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