libipt_SNAT.c revision 18992db3bfdb3b695cae12b53434f560cbf8e2ae
1/* Shared library add-on to iptables to add source-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_SNAT_OPT_SOURCE 0x01 12#define IPT_SNAT_OPT_RANDOM 0x02 13 14/* Source 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"SNAT v%s options:\n" 28" --to-source <ipaddr>[-<ipaddr>][:port-port]\n" 29" Address to map source to.\n" 30"[--random]\n" 31"\n", 32IPTABLES_VERSION); 33} 34 35static struct option opts[] = { 36 { "to-source", 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-source"); 163 164 if (*flags & IPT_SNAT_OPT_SOURCE) { 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-source not supported"); 170 } 171 *target = parse_to(optarg, portok, info); 172 /* WTF do we need this for?? */ 173 if (*flags & IPT_SNAT_OPT_RANDOM) 174 info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM; 175 *flags |= IPT_SNAT_OPT_SOURCE; 176 return 1; 177 178 case '2': 179 if (*flags & IPT_SNAT_OPT_SOURCE) { 180 info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM; 181 *flags |= IPT_SNAT_OPT_RANDOM; 182 } else 183 *flags |= IPT_SNAT_OPT_RANDOM; 184 return 1; 185 186 default: 187 return 0; 188 } 189} 190 191/* Final check; must have specfied --to-source. */ 192static void final_check(unsigned int flags) 193{ 194 if (!(flags & IPT_SNAT_OPT_SOURCE)) 195 exit_error(PARAMETER_PROBLEM, 196 "You must specify --to-source"); 197} 198 199static void print_range(const struct ip_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", addr_to_dotted(&a)); 206 if (r->max_ip != r->min_ip) { 207 a.s_addr = r->max_ip; 208 printf("-%s", addr_to_dotted(&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 219/* Prints out the targinfo. */ 220static void 221print(const void *ip, 222 const struct xt_entry_target *target, 223 int numeric) 224{ 225 struct ipt_natinfo *info = (void *)target; 226 unsigned int i = 0; 227 228 printf("to:"); 229 for (i = 0; i < info->mr.rangesize; i++) { 230 print_range(&info->mr.range[i]); 231 printf(" "); 232 if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM) 233 printf("random "); 234 } 235} 236 237/* Saves the union ipt_targinfo in parsable form to stdout. */ 238static void 239save(const void *ip, const struct xt_entry_target *target) 240{ 241 struct ipt_natinfo *info = (void *)target; 242 unsigned int i = 0; 243 244 for (i = 0; i < info->mr.rangesize; i++) { 245 printf("--to-source "); 246 print_range(&info->mr.range[i]); 247 printf(" "); 248 if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM) 249 printf("--random "); 250 } 251} 252 253static struct iptables_target snat = { 254 .name = "SNAT", 255 .version = IPTABLES_VERSION, 256 .size = IPT_ALIGN(sizeof(struct ip_nat_multi_range)), 257 .userspacesize = IPT_ALIGN(sizeof(struct ip_nat_multi_range)), 258 .help = &help, 259 .parse = &parse, 260 .final_check = &final_check, 261 .print = &print, 262 .save = &save, 263 .extra_opts = opts 264}; 265 266void _init(void) 267{ 268 register_target(&snat); 269} 270