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