libipt_SNAT.c revision e656e265bc67a55f6e51aa07118f96c058a97798
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 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"SNAT v%s options:\n" 28" --to-source <ipaddr>[-<ipaddr>][:port-port]" 29"[--random]" 30"\n" 31" Address to map source to.\n" 32" (You can use this more than once)\n\n", 33IPTABLES_VERSION); 34} 35 36static struct option opts[] = { 37 { "to-source", 1, 0, '1' }, 38 { "random", 0, 0, '2' }, 39 { 0 } 40}; 41 42static struct ipt_natinfo * 43append_range(struct ipt_natinfo *info, const struct ip_nat_range *range) 44{ 45 unsigned int size; 46 47 /* One rangesize already in struct ipt_natinfo */ 48 size = IPT_ALIGN(sizeof(*info) + info->mr.rangesize * sizeof(*range)); 49 50 info = realloc(info, size); 51 if (!info) 52 exit_error(OTHER_PROBLEM, "Out of memory\n"); 53 54 info->t.u.target_size = size; 55 info->mr.range[info->mr.rangesize] = *range; 56 info->mr.rangesize++; 57 58 return info; 59} 60 61/* Ranges expected in network order. */ 62static struct ipt_entry_target * 63parse_to(char *arg, int portok, struct ipt_natinfo *info) 64{ 65 struct ip_nat_range range; 66 char *colon, *dash, *error; 67 struct in_addr *ip; 68 69 memset(&range, 0, sizeof(range)); 70 colon = strchr(arg, ':'); 71 72 if (colon) { 73 int port; 74 75 if (!portok) 76 exit_error(PARAMETER_PROBLEM, 77 "Need TCP or UDP with port specification"); 78 79 range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED; 80 81 port = atoi(colon+1); 82 if (port <= 0 || port > 65535) 83 exit_error(PARAMETER_PROBLEM, 84 "Port `%s' not valid\n", colon+1); 85 86 error = strchr(colon+1, ':'); 87 if (error) 88 exit_error(PARAMETER_PROBLEM, 89 "Invalid port:port syntax - use dash\n"); 90 91 dash = strchr(colon, '-'); 92 if (!dash) { 93 range.min.tcp.port 94 = range.max.tcp.port 95 = htons(port); 96 } else { 97 int maxport; 98 99 maxport = atoi(dash + 1); 100 if (maxport <= 0 || maxport > 65535) 101 exit_error(PARAMETER_PROBLEM, 102 "Port `%s' not valid\n", dash+1); 103 if (maxport < port) 104 /* People are stupid. */ 105 exit_error(PARAMETER_PROBLEM, 106 "Port range `%s' funky\n", colon+1); 107 range.min.tcp.port = htons(port); 108 range.max.tcp.port = htons(maxport); 109 } 110 /* Starts with a colon? No IP info...*/ 111 if (colon == arg) 112 return &(append_range(info, &range)->t); 113 *colon = '\0'; 114 } 115 116 range.flags |= IP_NAT_RANGE_MAP_IPS; 117 dash = strchr(arg, '-'); 118 if (colon && dash && dash > colon) 119 dash = NULL; 120 121 if (dash) 122 *dash = '\0'; 123 124 ip = dotted_to_addr(arg); 125 if (!ip) 126 exit_error(PARAMETER_PROBLEM, "Bad IP address `%s'\n", 127 arg); 128 range.min_ip = ip->s_addr; 129 if (dash) { 130 ip = dotted_to_addr(dash+1); 131 if (!ip) 132 exit_error(PARAMETER_PROBLEM, "Bad IP address `%s'\n", 133 dash+1); 134 range.max_ip = ip->s_addr; 135 } else 136 range.max_ip = range.min_ip; 137 138 return &(append_range(info, &range)->t); 139} 140 141/* Function which parses command options; returns true if it 142 ate an option */ 143static int 144parse(int c, char **argv, int invert, unsigned int *flags, 145 const struct ipt_entry *entry, 146 struct ipt_entry_target **target) 147{ 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 if (r->flags & IP_NAT_RANGE_PROTO_RANDOM) 218 printf(" random"); 219} 220 221/* Prints out the targinfo. */ 222static void 223print(const struct ipt_ip *ip, 224 const struct ipt_entry_target *target, 225 int numeric) 226{ 227 struct ipt_natinfo *info = (void *)target; 228 unsigned int i = 0; 229 230 printf("to:"); 231 for (i = 0; i < info->mr.rangesize; i++) { 232 print_range(&info->mr.range[i]); 233 printf(" "); 234 } 235} 236 237/* Saves the union ipt_targinfo in parsable form to stdout. */ 238static void 239save(const struct ipt_ip *ip, const struct ipt_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 } 249} 250 251static struct iptables_target snat = { 252 .next = NULL, 253 .name = "SNAT", 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(&snat); 268} 269