libipt_SNAT.c revision 58e9118dc61c9ff656c0140c429f0fa892c36ac5
1/* Shared library add-on to iptables to add source-NAT support. */ 2#include <stdbool.h> 3#include <stdio.h> 4#include <netdb.h> 5#include <string.h> 6#include <stdlib.h> 7#include <getopt.h> 8#include <xtables.h> 9#include <iptables.h> 10#include <limits.h> /* INT_MAX in ip_tables.h */ 11#include <linux/netfilter_ipv4/ip_tables.h> 12#include <net/netfilter/nf_nat.h> 13 14#define IPT_SNAT_OPT_SOURCE 0x01 15#define IPT_SNAT_OPT_RANDOM 0x02 16 17/* Source NAT data consists of a multi-range, indicating where to map 18 to. */ 19struct ipt_natinfo 20{ 21 struct xt_entry_target t; 22 struct nf_nat_multi_range mr; 23}; 24 25static void SNAT_help(void) 26{ 27 printf( 28"SNAT target options:\n" 29" --to-source [<ipaddr>[-<ipaddr>]][:port[-port]]\n" 30" Address to map source to.\n" 31"[--random] [--persistent]\n"); 32} 33 34static const struct option SNAT_opts[] = { 35 {.name = "to-source", .has_arg = true, .val = '1'}, 36 {.name = "random", .has_arg = false, .val = '2'}, 37 {.name = "persistent", .has_arg = false, .val = '3'}, 38 XT_GETOPT_TABLEEND, 39}; 40 41static struct ipt_natinfo * 42append_range(struct ipt_natinfo *info, const struct nf_nat_range *range) 43{ 44 unsigned int size; 45 46 /* One rangesize already in struct ipt_natinfo */ 47 size = XT_ALIGN(sizeof(*info) + info->mr.rangesize * sizeof(*range)); 48 49 info = realloc(info, size); 50 if (!info) 51 xtables_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 nf_nat_range range; 65 char *colon, *dash, *error; 66 const 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 xtables_error(PARAMETER_PROBLEM, 76 "Need TCP, UDP, SCTP or DCCP 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 xtables_error(PARAMETER_PROBLEM, 83 "Port `%s' not valid\n", colon+1); 84 85 error = strchr(colon+1, ':'); 86 if (error) 87 xtables_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 xtables_error(PARAMETER_PROBLEM, 101 "Port `%s' not valid\n", dash+1); 102 if (maxport < port) 103 /* People are stupid. */ 104 xtables_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 = xtables_numeric_to_ipaddr(arg); 124 if (!ip) 125 xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n", 126 arg); 127 range.min_ip = ip->s_addr; 128 if (dash) { 129 ip = xtables_numeric_to_ipaddr(dash+1); 130 if (!ip) 131 xtables_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 140static int SNAT_parse(int c, char **argv, int invert, unsigned int *flags, 141 const void *e, struct xt_entry_target **target) 142{ 143 const struct ipt_entry *entry = e; 144 struct ipt_natinfo *info = (void *)*target; 145 int portok; 146 147 if (entry->ip.proto == IPPROTO_TCP 148 || entry->ip.proto == IPPROTO_UDP 149 || entry->ip.proto == IPPROTO_SCTP 150 || entry->ip.proto == IPPROTO_DCCP 151 || entry->ip.proto == IPPROTO_ICMP) 152 portok = 1; 153 else 154 portok = 0; 155 156 switch (c) { 157 case '1': 158 if (xtables_check_inverse(optarg, &invert, NULL, 0, argv)) 159 xtables_error(PARAMETER_PROBLEM, 160 "Unexpected `!' after --to-source"); 161 162 if (*flags & IPT_SNAT_OPT_SOURCE) { 163 if (!kernel_version) 164 get_kernel_version(); 165 if (kernel_version > LINUX_VERSION(2, 6, 10)) 166 xtables_error(PARAMETER_PROBLEM, 167 "Multiple --to-source not supported"); 168 } 169 *target = parse_to(optarg, portok, info); 170 /* WTF do we need this for?? */ 171 if (*flags & IPT_SNAT_OPT_RANDOM) 172 info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM; 173 *flags |= IPT_SNAT_OPT_SOURCE; 174 return 1; 175 176 case '2': 177 if (*flags & IPT_SNAT_OPT_SOURCE) { 178 info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM; 179 *flags |= IPT_SNAT_OPT_RANDOM; 180 } else 181 *flags |= IPT_SNAT_OPT_RANDOM; 182 return 1; 183 184 case '3': 185 info->mr.range[0].flags |= IP_NAT_RANGE_PERSISTENT; 186 return 1; 187 } 188 return 0; 189} 190 191static void SNAT_check(unsigned int flags) 192{ 193 if (!(flags & IPT_SNAT_OPT_SOURCE)) 194 xtables_error(PARAMETER_PROBLEM, 195 "You must specify --to-source"); 196} 197 198static void print_range(const struct nf_nat_range *r) 199{ 200 if (r->flags & IP_NAT_RANGE_MAP_IPS) { 201 struct in_addr a; 202 203 a.s_addr = r->min_ip; 204 printf("%s", xtables_ipaddr_to_numeric(&a)); 205 if (r->max_ip != r->min_ip) { 206 a.s_addr = r->max_ip; 207 printf("-%s", xtables_ipaddr_to_numeric(&a)); 208 } 209 } 210 if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) { 211 printf(":"); 212 printf("%hu", ntohs(r->min.tcp.port)); 213 if (r->max.tcp.port != r->min.tcp.port) 214 printf("-%hu", ntohs(r->max.tcp.port)); 215 } 216} 217 218static void SNAT_print(const void *ip, const struct xt_entry_target *target, 219 int numeric) 220{ 221 const struct ipt_natinfo *info = (const void *)target; 222 unsigned int i = 0; 223 224 printf(" to:"); 225 for (i = 0; i < info->mr.rangesize; i++) { 226 print_range(&info->mr.range[i]); 227 if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM) 228 printf(" random"); 229 if (info->mr.range[i].flags & IP_NAT_RANGE_PERSISTENT) 230 printf(" persistent"); 231 } 232} 233 234static void SNAT_save(const void *ip, const struct xt_entry_target *target) 235{ 236 const struct ipt_natinfo *info = (const void *)target; 237 unsigned int i = 0; 238 239 for (i = 0; i < info->mr.rangesize; i++) { 240 printf(" --to-source "); 241 print_range(&info->mr.range[i]); 242 if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM) 243 printf(" --random"); 244 if (info->mr.range[i].flags & IP_NAT_RANGE_PERSISTENT) 245 printf(" --persistent"); 246 } 247} 248 249static struct xtables_target snat_tg_reg = { 250 .name = "SNAT", 251 .version = XTABLES_VERSION, 252 .family = NFPROTO_IPV4, 253 .size = XT_ALIGN(sizeof(struct nf_nat_multi_range)), 254 .userspacesize = XT_ALIGN(sizeof(struct nf_nat_multi_range)), 255 .help = SNAT_help, 256 .parse = SNAT_parse, 257 .final_check = SNAT_check, 258 .print = SNAT_print, 259 .save = SNAT_save, 260 .extra_opts = SNAT_opts, 261}; 262 263void _init(void) 264{ 265 xtables_register_target(&snat_tg_reg); 266} 267