libipt_SNAT.c revision 9f4a637ee5856e8f260e3f3867782ed5584e00f9
1#include <stdio.h> 2#include <netdb.h> 3#include <string.h> 4#include <stdlib.h> 5#include <xtables.h> 6#include <iptables.h> 7#include <limits.h> /* INT_MAX in ip_tables.h */ 8#include <linux/netfilter_ipv4/ip_tables.h> 9#include <net/netfilter/nf_nat.h> 10 11enum { 12 O_TO_SRC = 0, 13 O_RANDOM, 14 O_PERSISTENT, 15 F_TO_SRC = 1 << O_TO_SRC, 16 F_RANDOM = 1 << O_RANDOM, 17}; 18 19/* Source NAT data consists of a multi-range, indicating where to map 20 to. */ 21struct ipt_natinfo 22{ 23 struct xt_entry_target t; 24 struct nf_nat_multi_range mr; 25}; 26 27static void SNAT_help(void) 28{ 29 printf( 30"SNAT target options:\n" 31" --to-source <ipaddr>[-<ipaddr>][:port-port]\n" 32" Address to map source to.\n" 33"[--random] [--persistent]\n"); 34} 35 36static const struct xt_option_entry SNAT_opts[] = { 37 {.name = "to-source", .id = O_TO_SRC, .type = XTTYPE_STRING, 38 .flags = XTOPT_MAND | XTOPT_MULTI}, 39 {.name = "random", .id = O_RANDOM, .type = XTTYPE_NONE}, 40 {.name = "persistent", .id = O_PERSISTENT, .type = XTTYPE_NONE}, 41 XTOPT_TABLEEND, 42}; 43 44static struct ipt_natinfo * 45append_range(struct ipt_natinfo *info, const struct nf_nat_range *range) 46{ 47 unsigned int size; 48 49 /* One rangesize already in struct ipt_natinfo */ 50 size = XT_ALIGN(sizeof(*info) + info->mr.rangesize * sizeof(*range)); 51 52 info = realloc(info, size); 53 if (!info) 54 xtables_error(OTHER_PROBLEM, "Out of memory\n"); 55 56 info->t.u.target_size = size; 57 info->mr.range[info->mr.rangesize] = *range; 58 info->mr.rangesize++; 59 60 return info; 61} 62 63/* Ranges expected in network order. */ 64static struct xt_entry_target * 65parse_to(const char *orig_arg, int portok, struct ipt_natinfo *info) 66{ 67 struct nf_nat_range range; 68 char *arg, *colon, *dash, *error; 69 const struct in_addr *ip; 70 71 arg = strdup(orig_arg); 72 if (arg == NULL) 73 xtables_error(RESOURCE_PROBLEM, "strdup"); 74 memset(&range, 0, sizeof(range)); 75 colon = strchr(arg, ':'); 76 77 if (colon) { 78 int port; 79 80 if (!portok) 81 xtables_error(PARAMETER_PROBLEM, 82 "Need TCP, UDP, SCTP or DCCP with port specification"); 83 84 range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED; 85 86 port = atoi(colon+1); 87 if (port <= 0 || port > 65535) 88 xtables_error(PARAMETER_PROBLEM, 89 "Port `%s' not valid\n", colon+1); 90 91 error = strchr(colon+1, ':'); 92 if (error) 93 xtables_error(PARAMETER_PROBLEM, 94 "Invalid port:port syntax - use dash\n"); 95 96 dash = strchr(colon, '-'); 97 if (!dash) { 98 range.min.tcp.port 99 = range.max.tcp.port 100 = htons(port); 101 } else { 102 int maxport; 103 104 maxport = atoi(dash + 1); 105 if (maxport <= 0 || maxport > 65535) 106 xtables_error(PARAMETER_PROBLEM, 107 "Port `%s' not valid\n", dash+1); 108 if (maxport < port) 109 /* People are stupid. */ 110 xtables_error(PARAMETER_PROBLEM, 111 "Port range `%s' funky\n", colon+1); 112 range.min.tcp.port = htons(port); 113 range.max.tcp.port = htons(maxport); 114 } 115 /* Starts with a colon? No IP info...*/ 116 if (colon == arg) { 117 free(arg); 118 return &(append_range(info, &range)->t); 119 } 120 *colon = '\0'; 121 } 122 123 range.flags |= IP_NAT_RANGE_MAP_IPS; 124 dash = strchr(arg, '-'); 125 if (colon && dash && dash > colon) 126 dash = NULL; 127 128 if (dash) 129 *dash = '\0'; 130 131 ip = xtables_numeric_to_ipaddr(arg); 132 if (!ip) 133 xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n", 134 arg); 135 range.min_ip = ip->s_addr; 136 if (dash) { 137 ip = xtables_numeric_to_ipaddr(dash+1); 138 if (!ip) 139 xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n", 140 dash+1); 141 range.max_ip = ip->s_addr; 142 } else 143 range.max_ip = range.min_ip; 144 145 free(arg); 146 return &(append_range(info, &range)->t); 147} 148 149static void SNAT_parse(struct xt_option_call *cb) 150{ 151 const struct ipt_entry *entry = cb->xt_entry; 152 struct ipt_natinfo *info = (void *)(*cb->target); 153 int portok; 154 155 if (entry->ip.proto == IPPROTO_TCP 156 || entry->ip.proto == IPPROTO_UDP 157 || entry->ip.proto == IPPROTO_SCTP 158 || entry->ip.proto == IPPROTO_DCCP 159 || entry->ip.proto == IPPROTO_ICMP) 160 portok = 1; 161 else 162 portok = 0; 163 164 xtables_option_parse(cb); 165 switch (cb->entry->id) { 166 case O_TO_SRC: 167 if (cb->xflags & F_TO_SRC) { 168 if (!kernel_version) 169 get_kernel_version(); 170 if (kernel_version > LINUX_VERSION(2, 6, 10)) 171 xtables_error(PARAMETER_PROBLEM, 172 "Multiple --to-source not supported"); 173 } 174 *cb->target = parse_to(cb->arg, portok, info); 175 /* WTF do we need this for?? */ 176 if (cb->xflags & F_RANDOM) 177 info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM; 178 break; 179 case O_RANDOM: 180 if (cb->xflags & F_TO_SRC) 181 info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM; 182 break; 183 case O_PERSISTENT: 184 info->mr.range[0].flags |= IP_NAT_RANGE_PERSISTENT; 185 break; 186 } 187} 188 189static void print_range(const struct nf_nat_range *r) 190{ 191 if (r->flags & IP_NAT_RANGE_MAP_IPS) { 192 struct in_addr a; 193 194 a.s_addr = r->min_ip; 195 printf("%s", xtables_ipaddr_to_numeric(&a)); 196 if (r->max_ip != r->min_ip) { 197 a.s_addr = r->max_ip; 198 printf("-%s", xtables_ipaddr_to_numeric(&a)); 199 } 200 } 201 if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) { 202 printf(":"); 203 printf("%hu", ntohs(r->min.tcp.port)); 204 if (r->max.tcp.port != r->min.tcp.port) 205 printf("-%hu", ntohs(r->max.tcp.port)); 206 } 207} 208 209static void SNAT_print(const void *ip, const struct xt_entry_target *target, 210 int numeric) 211{ 212 const struct ipt_natinfo *info = (const void *)target; 213 unsigned int i = 0; 214 215 printf(" to:"); 216 for (i = 0; i < info->mr.rangesize; i++) { 217 print_range(&info->mr.range[i]); 218 if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM) 219 printf(" random"); 220 if (info->mr.range[i].flags & IP_NAT_RANGE_PERSISTENT) 221 printf(" persistent"); 222 } 223} 224 225static void SNAT_save(const void *ip, const struct xt_entry_target *target) 226{ 227 const struct ipt_natinfo *info = (const void *)target; 228 unsigned int i = 0; 229 230 for (i = 0; i < info->mr.rangesize; i++) { 231 printf(" --to-source "); 232 print_range(&info->mr.range[i]); 233 if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM) 234 printf(" --random"); 235 if (info->mr.range[i].flags & IP_NAT_RANGE_PERSISTENT) 236 printf(" --persistent"); 237 } 238} 239 240static struct xtables_target snat_tg_reg = { 241 .name = "SNAT", 242 .version = XTABLES_VERSION, 243 .family = NFPROTO_IPV4, 244 .size = XT_ALIGN(sizeof(struct nf_nat_multi_range)), 245 .userspacesize = XT_ALIGN(sizeof(struct nf_nat_multi_range)), 246 .help = SNAT_help, 247 .x6_parse = SNAT_parse, 248 .print = SNAT_print, 249 .save = SNAT_save, 250 .x6_options = SNAT_opts, 251}; 252 253void _init(void) 254{ 255 xtables_register_target(&snat_tg_reg); 256} 257