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