libipt_DNAT.c revision 4008138e2b5248940265b160fae001d8954fae21
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. */ 14struct 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. */ 21static void 22help(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", 29IPTABLES_VERSION); 30} 31 32static struct option opts[] = { 33 { "to-destination", 1, 0, '1' }, 34 { 0 } 35}; 36 37static struct ipt_natinfo * 38append_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. */ 57static struct ipt_entry_target * 58parse_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 */ 138static int 139parse(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. */ 176static void final_check(unsigned int flags) 177{ 178 if (!flags) 179 exit_error(PARAMETER_PROBLEM, 180 "You must specify --to-destination"); 181} 182 183static 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. */ 204static void 205print(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. */ 220static void 221save(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 233static 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 247void ipt_DNAT_init(void) 248{ 249 register_target(&dnat); 250} 251