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 ipt_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 ipt_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 struct ipt_entry *entry, 145 struct ipt_entry_target **target) 146{ 147 struct ipt_natinfo *info = (void *)*target; 148 int portok; 149 150 if (entry->ip.proto == IPPROTO_TCP 151 || entry->ip.proto == IPPROTO_UDP 152 || entry->ip.proto == IPPROTO_ICMP) 153 portok = 1; 154 else 155 portok = 0; 156 157 switch (c) { 158 case '1': 159 if (check_inverse(optarg, &invert, NULL, 0)) 160 exit_error(PARAMETER_PROBLEM, 161 "Unexpected `!' after --to-destination"); 162 163 if (*flags) { 164 if (!kernel_version) 165 get_kernel_version(); 166 if (kernel_version > LINUX_VERSION(2, 6, 10)) 167 exit_error(PARAMETER_PROBLEM, 168 "Multiple --to-destination not supported"); 169 } 170 *target = parse_to(optarg, portok, info); 171 /* WTF do we need this for?? */ 172 if (*flags & IPT_DNAT_OPT_RANDOM) 173 info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM; 174 *flags |= IPT_DNAT_OPT_DEST; 175 return 1; 176 177 case '2': 178 if (*flags & IPT_DNAT_OPT_DEST) { 179 info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM; 180 *flags |= IPT_DNAT_OPT_RANDOM; 181 } else 182 *flags |= IPT_DNAT_OPT_RANDOM; 183 default: 184 return 0; 185 } 186} 187 188/* Final check; must have specfied --to-source. */ 189static void final_check(unsigned int flags) 190{ 191 if (!flags) 192 exit_error(PARAMETER_PROBLEM, 193 "You must specify --to-destination"); 194} 195 196static void print_range(const struct ip_nat_range *r) 197{ 198 if (r->flags & IP_NAT_RANGE_MAP_IPS) { 199 struct in_addr a; 200 201 a.s_addr = r->min_ip; 202 printf("%s", addr_to_dotted(&a)); 203 if (r->max_ip != r->min_ip) { 204 a.s_addr = r->max_ip; 205 printf("-%s", addr_to_dotted(&a)); 206 } 207 } 208 if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) { 209 printf(":"); 210 printf("%hu", ntohs(r->min.tcp.port)); 211 if (r->max.tcp.port != r->min.tcp.port) 212 printf("-%hu", ntohs(r->max.tcp.port)); 213 } 214} 215 216/* Prints out the targinfo. */ 217static void 218print(const struct ipt_ip *ip, 219 const struct ipt_entry_target *target, 220 int numeric) 221{ 222 struct ipt_natinfo *info = (void *)target; 223 unsigned int i = 0; 224 225 printf("to:"); 226 for (i = 0; i < info->mr.rangesize; i++) { 227 print_range(&info->mr.range[i]); 228 printf(" "); 229 if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM) 230 printf("random "); 231 } 232} 233 234/* Saves the union ipt_targinfo in parsable form to stdout. */ 235static void 236save(const struct ipt_ip *ip, const struct ipt_entry_target *target) 237{ 238 struct ipt_natinfo *info = (void *)target; 239 unsigned int i = 0; 240 241 for (i = 0; i < info->mr.rangesize; i++) { 242 printf("--to-destination "); 243 print_range(&info->mr.range[i]); 244 printf(" "); 245 if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM) 246 printf("--random "); 247 } 248} 249 250static struct iptables_target dnat = { 251 .next = NULL, 252 .name = "DNAT", 253 .version = IPTABLES_VERSION, 254 .size = IPT_ALIGN(sizeof(struct ip_nat_multi_range)), 255 .userspacesize = IPT_ALIGN(sizeof(struct ip_nat_multi_range)), 256 .help = &help, 257 .parse = &parse, 258 .final_check = &final_check, 259 .print = &print, 260 .save = &save, 261 .extra_opts = opts 262}; 263 264void _init(void) 265{ 266 register_target(&dnat); 267} 268