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