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 <linux/netfilter/nf_nat.h> 10 11enum { 12 O_TO_SRC = 0, 13 O_RANDOM, 14 O_RANDOM_FULLY, 15 O_PERSISTENT, 16 O_X_TO_SRC, 17 F_TO_SRC = 1 << O_TO_SRC, 18 F_RANDOM = 1 << O_RANDOM, 19 F_RANDOM_FULLY = 1 << O_RANDOM_FULLY, 20 F_X_TO_SRC = 1 << O_X_TO_SRC, 21}; 22 23/* Source NAT data consists of a multi-range, indicating where to map 24 to. */ 25struct ipt_natinfo 26{ 27 struct xt_entry_target t; 28 struct nf_nat_ipv4_multi_range_compat mr; 29}; 30 31static void SNAT_help(void) 32{ 33 printf( 34"SNAT target options:\n" 35" --to-source [<ipaddr>[-<ipaddr>]][:port[-port]]\n" 36" Address to map source to.\n" 37"[--random] [--random-fully] [--persistent]\n"); 38} 39 40static const struct xt_option_entry SNAT_opts[] = { 41 {.name = "to-source", .id = O_TO_SRC, .type = XTTYPE_STRING, 42 .flags = XTOPT_MAND | XTOPT_MULTI}, 43 {.name = "random", .id = O_RANDOM, .type = XTTYPE_NONE}, 44 {.name = "random-fully", .id = O_RANDOM_FULLY, .type = XTTYPE_NONE}, 45 {.name = "persistent", .id = O_PERSISTENT, .type = XTTYPE_NONE}, 46 XTOPT_TABLEEND, 47}; 48 49static struct ipt_natinfo * 50append_range(struct ipt_natinfo *info, const struct nf_nat_ipv4_range *range) 51{ 52 unsigned int size; 53 54 /* One rangesize already in struct ipt_natinfo */ 55 size = XT_ALIGN(sizeof(*info) + info->mr.rangesize * sizeof(*range)); 56 57 info = realloc(info, size); 58 if (!info) 59 xtables_error(OTHER_PROBLEM, "Out of memory\n"); 60 61 info->t.u.target_size = size; 62 info->mr.range[info->mr.rangesize] = *range; 63 info->mr.rangesize++; 64 65 return info; 66} 67 68/* Ranges expected in network order. */ 69static struct xt_entry_target * 70parse_to(const char *orig_arg, int portok, struct ipt_natinfo *info) 71{ 72 struct nf_nat_ipv4_range range; 73 char *arg, *colon, *dash, *error; 74 const struct in_addr *ip; 75 76 arg = strdup(orig_arg); 77 if (arg == NULL) 78 xtables_error(RESOURCE_PROBLEM, "strdup"); 79 memset(&range, 0, sizeof(range)); 80 colon = strchr(arg, ':'); 81 82 if (colon) { 83 int port; 84 85 if (!portok) 86 xtables_error(PARAMETER_PROBLEM, 87 "Need TCP, UDP, SCTP or DCCP with port specification"); 88 89 range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED; 90 91 port = atoi(colon+1); 92 if (port <= 0 || port > 65535) 93 xtables_error(PARAMETER_PROBLEM, 94 "Port `%s' not valid\n", colon+1); 95 96 error = strchr(colon+1, ':'); 97 if (error) 98 xtables_error(PARAMETER_PROBLEM, 99 "Invalid port:port syntax - use dash\n"); 100 101 dash = strchr(colon, '-'); 102 if (!dash) { 103 range.min.tcp.port 104 = range.max.tcp.port 105 = htons(port); 106 } else { 107 int maxport; 108 109 maxport = atoi(dash + 1); 110 if (maxport <= 0 || maxport > 65535) 111 xtables_error(PARAMETER_PROBLEM, 112 "Port `%s' not valid\n", dash+1); 113 if (maxport < port) 114 /* People are stupid. */ 115 xtables_error(PARAMETER_PROBLEM, 116 "Port range `%s' funky\n", colon+1); 117 range.min.tcp.port = htons(port); 118 range.max.tcp.port = htons(maxport); 119 } 120 /* Starts with a colon? No IP info...*/ 121 if (colon == arg) { 122 free(arg); 123 return &(append_range(info, &range)->t); 124 } 125 *colon = '\0'; 126 } 127 128 range.flags |= NF_NAT_RANGE_MAP_IPS; 129 dash = strchr(arg, '-'); 130 if (colon && dash && dash > colon) 131 dash = NULL; 132 133 if (dash) 134 *dash = '\0'; 135 136 ip = xtables_numeric_to_ipaddr(arg); 137 if (!ip) 138 xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n", 139 arg); 140 range.min_ip = ip->s_addr; 141 if (dash) { 142 ip = xtables_numeric_to_ipaddr(dash+1); 143 if (!ip) 144 xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n", 145 dash+1); 146 range.max_ip = ip->s_addr; 147 } else 148 range.max_ip = range.min_ip; 149 150 free(arg); 151 return &(append_range(info, &range)->t); 152} 153 154static void SNAT_parse(struct xt_option_call *cb) 155{ 156 const struct ipt_entry *entry = cb->xt_entry; 157 struct ipt_natinfo *info = (void *)(*cb->target); 158 int portok; 159 160 if (entry->ip.proto == IPPROTO_TCP 161 || entry->ip.proto == IPPROTO_UDP 162 || entry->ip.proto == IPPROTO_SCTP 163 || entry->ip.proto == IPPROTO_DCCP 164 || entry->ip.proto == IPPROTO_ICMP) 165 portok = 1; 166 else 167 portok = 0; 168 169 xtables_option_parse(cb); 170 switch (cb->entry->id) { 171 case O_TO_SRC: 172 if (cb->xflags & F_X_TO_SRC) { 173 if (!kernel_version) 174 get_kernel_version(); 175 if (kernel_version > LINUX_VERSION(2, 6, 10)) 176 xtables_error(PARAMETER_PROBLEM, 177 "SNAT: Multiple --to-source not supported"); 178 } 179 *cb->target = parse_to(cb->arg, portok, info); 180 cb->xflags |= F_X_TO_SRC; 181 break; 182 case O_PERSISTENT: 183 info->mr.range[0].flags |= NF_NAT_RANGE_PERSISTENT; 184 break; 185 } 186} 187 188static void SNAT_fcheck(struct xt_fcheck_call *cb) 189{ 190 static const unsigned int f = F_TO_SRC | F_RANDOM; 191 static const unsigned int r = F_TO_SRC | F_RANDOM_FULLY; 192 struct nf_nat_ipv4_multi_range_compat *mr = cb->data; 193 194 if ((cb->xflags & f) == f) 195 mr->range[0].flags |= NF_NAT_RANGE_PROTO_RANDOM; 196 if ((cb->xflags & r) == r) 197 mr->range[0].flags |= NF_NAT_RANGE_PROTO_RANDOM_FULLY; 198} 199 200static void print_range(const struct nf_nat_ipv4_range *r) 201{ 202 if (r->flags & NF_NAT_RANGE_MAP_IPS) { 203 struct in_addr a; 204 205 a.s_addr = r->min_ip; 206 printf("%s", xtables_ipaddr_to_numeric(&a)); 207 if (r->max_ip != r->min_ip) { 208 a.s_addr = r->max_ip; 209 printf("-%s", xtables_ipaddr_to_numeric(&a)); 210 } 211 } 212 if (r->flags & NF_NAT_RANGE_PROTO_SPECIFIED) { 213 printf(":"); 214 printf("%hu", ntohs(r->min.tcp.port)); 215 if (r->max.tcp.port != r->min.tcp.port) 216 printf("-%hu", ntohs(r->max.tcp.port)); 217 } 218} 219 220static void SNAT_print(const void *ip, const struct xt_entry_target *target, 221 int numeric) 222{ 223 const struct ipt_natinfo *info = (const void *)target; 224 unsigned int i = 0; 225 226 printf(" to:"); 227 for (i = 0; i < info->mr.rangesize; i++) { 228 print_range(&info->mr.range[i]); 229 if (info->mr.range[i].flags & NF_NAT_RANGE_PROTO_RANDOM) 230 printf(" random"); 231 if (info->mr.range[i].flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY) 232 printf(" random-fully"); 233 if (info->mr.range[i].flags & NF_NAT_RANGE_PERSISTENT) 234 printf(" persistent"); 235 } 236} 237 238static void SNAT_save(const void *ip, const struct xt_entry_target *target) 239{ 240 const struct ipt_natinfo *info = (const void *)target; 241 unsigned int i = 0; 242 243 for (i = 0; i < info->mr.rangesize; i++) { 244 printf(" --to-source "); 245 print_range(&info->mr.range[i]); 246 if (info->mr.range[i].flags & NF_NAT_RANGE_PROTO_RANDOM) 247 printf(" --random"); 248 if (info->mr.range[i].flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY) 249 printf(" --random-fully"); 250 if (info->mr.range[i].flags & NF_NAT_RANGE_PERSISTENT) 251 printf(" --persistent"); 252 } 253} 254 255static void print_range_xlate(const struct nf_nat_ipv4_range *r, 256 struct xt_xlate *xl) 257{ 258 if (r->flags & NF_NAT_RANGE_MAP_IPS) { 259 struct in_addr a; 260 261 a.s_addr = r->min_ip; 262 xt_xlate_add(xl, "%s", xtables_ipaddr_to_numeric(&a)); 263 if (r->max_ip != r->min_ip) { 264 a.s_addr = r->max_ip; 265 xt_xlate_add(xl, "-%s", xtables_ipaddr_to_numeric(&a)); 266 } 267 } 268 if (r->flags & NF_NAT_RANGE_PROTO_SPECIFIED) { 269 xt_xlate_add(xl, ":"); 270 xt_xlate_add(xl, "%hu", ntohs(r->min.tcp.port)); 271 if (r->max.tcp.port != r->min.tcp.port) 272 xt_xlate_add(xl, "-%hu", ntohs(r->max.tcp.port)); 273 } 274} 275 276static int SNAT_xlate(struct xt_xlate *xl, 277 const struct xt_xlate_tg_params *params) 278{ 279 const struct ipt_natinfo *info = (const void *)params->target; 280 unsigned int i = 0; 281 bool sep_need = false; 282 const char *sep = " "; 283 284 for (i = 0; i < info->mr.rangesize; i++) { 285 xt_xlate_add(xl, "snat to "); 286 print_range_xlate(&info->mr.range[i], xl); 287 if (info->mr.range[i].flags & NF_NAT_RANGE_PROTO_RANDOM) { 288 xt_xlate_add(xl, " random"); 289 sep_need = true; 290 } 291 if (info->mr.range[i].flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY) { 292 if (sep_need) 293 sep = ","; 294 xt_xlate_add(xl, "%sfully-random", sep); 295 sep_need = true; 296 } 297 if (info->mr.range[i].flags & NF_NAT_RANGE_PERSISTENT) { 298 if (sep_need) 299 sep = ","; 300 xt_xlate_add(xl, "%spersistent", sep); 301 } 302 } 303 304 return 1; 305} 306 307static struct xtables_target snat_tg_reg = { 308 .name = "SNAT", 309 .version = XTABLES_VERSION, 310 .family = NFPROTO_IPV4, 311 .size = XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)), 312 .userspacesize = XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)), 313 .help = SNAT_help, 314 .x6_parse = SNAT_parse, 315 .x6_fcheck = SNAT_fcheck, 316 .print = SNAT_print, 317 .save = SNAT_save, 318 .x6_options = SNAT_opts, 319 .xlate = SNAT_xlate, 320}; 321 322void _init(void) 323{ 324 xtables_register_target(&snat_tg_reg); 325} 326