diff options
Diffstat (limited to 'libip6t_MAP66.c')
-rw-r--r-- | libip6t_MAP66.c | 170 |
1 files changed, 170 insertions, 0 deletions
diff --git a/libip6t_MAP66.c b/libip6t_MAP66.c new file mode 100644 index 0000000..a8a0f72 --- /dev/null +++ b/libip6t_MAP66.c @@ -0,0 +1,170 @@ +/* + * MAP66: Network Address Translation IPv6-to-IPv6 as + * proposed in the IETF's second NAT66 draft document. + * (c) 2010 sven-ola()gmx.de + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <arpa/inet.h> + +#define IPTABLES_VERSION_CMP(a,b,c) (((a) << 16) + ((b) << 8) + (c)) + +#if IPTABLES_VERSION_CODE < IPTABLES_VERSION_CMP(1,4,0) +# include <ip6tables.h> +# define xt_entry_target ip6t_entry_target +# define void_entry struct ip6t_entry +# define void_ip6 struct ip6t_ip6 +# define XT_ALIGN IP6T_ALIGN +#else +# include <xtables.h> +# define void_entry void +# define void_ip6 void +#endif + +#if IPTABLES_VERSION_CODE < IPTABLES_VERSION_CMP(1,4,1) +# define xtables_target ip6tables_target +# define XTABLES_VERSION IPTABLES_VERSION +# define xtables_register_target register_target6 +#endif + +#if IPTABLES_VERSION_CODE < IPTABLES_VERSION_CMP(1,4,3) +# define xtables_error exit_error +# define xtables_check_inverse check_inverse +# define NFPROTO_IPV6 PF_INET6 +#endif + +#include "ip6t_MAP66.h" + +/* One's complement add */ +static inline u_int16_t add16( + u_int16_t a, + u_int16_t b) +{ + a += b; + return a + (a < b); +} + +/* Calc one's complement csum */ +static inline u_int16_t csum16(const u_int16_t *buf, int len) +{ + u_int16_t csum = 0; + while(len--) csum = add16(csum, *buf++); + return csum; +} + +static void MAP66_help(void) +{ + printf( +"MAP66 target options\n" +" --to ipv6addr/prefixlength (External IPv6 address)\n" +" --nospoof (Disables spoof check)\n" +"\n" +"Note: you need two ip6tables rules to map an internal network\n" +"using ULAs to an external network with official IPv6 address.\n" +"\n" +"Example:\n" +"\n" +"ip6tables -t mangle -I PREROUTING -i eth0 -d 2001:0DB8:0001::/48 -j MAP66 --to FD01:0203:0405::/48\n" +"ip6tables -t mangle -I POSTROUTING -o eth0 -s FD01:0203:0405::/48 -j MAP66 --to 2001:0DB8:0001::/48\n"); +} + +static int MAP66_parse( + int c, + char **argv, + int invert, + unsigned int *flags, + const void_entry *entry, + struct xt_entry_target **target) +{ + int i; + char *p; + struct ip6t_MAP66_info* info = (struct ip6t_MAP66_info*)(*target)->data; + + switch(c) { + case '1': + if (!optarg) { + xtables_error(PARAMETER_PROBLEM, "--to: You must specify a value"); + } + if (xtables_check_inverse(optarg, &invert, NULL, 0 +#if IPTABLES_VERSION_CODE >= IPTABLES_VERSION_CMP(1,4,6) + ,argv +#endif + )) { + xtables_error(PARAMETER_PROBLEM, "Unexpected `!' after --to"); + } + if (0 != (IP6T_MAP66_OPT_TO & *flags)) { + xtables_error(PARAMETER_PROBLEM, "Multiple --to not supported"); + } + *flags |= IP6T_MAP66_OPT_TO; + if (NULL == (p = strchr(optarg, '/'))) { + xtables_error(PARAMETER_PROBLEM, "Missing '/' character in --to: \"%s\"", optarg); + } + *p = '\0'; + if (1 != inet_pton(AF_INET6, optarg, &info->prefix)) { + xtables_error(PARAMETER_PROBLEM, "Invalid IPv6 address in --to: \"%s\"", optarg); + } + i = atoi(p + 1); + if (0 >= i || 128 <= i || 0 != i % 16) { + xtables_error(PARAMETER_PROBLEM, "Invalid prefix length in --to: \"%s\" (use /112, /96 .. /16)", p + 1); + } + info->prefixlength = i / 16; + info->prefixcsum = ~csum16((const u_int16_t *)&info->prefix, info->prefixlength); + return 1; + break; + case '2': + if (0 != (IP6T_MAP66_OPT_NOSPOOF & *flags)) { + xtables_error(PARAMETER_PROBLEM, "Multiple --nospoof not supported"); + } + info->mapflags |= IP6T_MAP66_OPT_NOSPOOF; + *flags |= IP6T_MAP66_OPT_NOSPOOF; + return 1; + break; + } + return 0; +} + +static void MAP66_check(unsigned int flags) +{ + if (!(flags & IP6T_MAP66_OPT_TO)) { + xtables_error(PARAMETER_PROBLEM, "You must specify --to"); + } +} + +static void MAP66_save( + const void_ip6 *ip, + const struct xt_entry_target *target) +{ + char s[50+1]; + const struct ip6t_MAP66_info* info = (struct ip6t_MAP66_info*)target->data; + printf("--to %s/%d ", inet_ntop(AF_INET6, &info->prefix, s, sizeof(s)), 16 * info->prefixlength); + if (0 != (IP6T_MAP66_OPT_NOSPOOF & info->mapflags)) printf("--nospoof "); +} + +static struct option MAP66_opts[] = { + { .name = "to", .has_arg = 1, .flag = NULL, .val = '1' }, + { .name = "nospoof", .has_arg = 0, .flag = NULL, .val = '2' }, + { .name = NULL } +}; + +static struct xtables_target MAP66_tg6_reg = { + .name = "MAP66", + .version = XTABLES_VERSION, +#if IPTABLES_VERSION_CODE >= IPTABLES_VERSION_CMP(1,4,1) + .family = NFPROTO_IPV6, +#endif + .size = XT_ALIGN(sizeof(struct ip6t_MAP66_info)), + .userspacesize = XT_ALIGN(sizeof(struct ip6t_MAP66_info)), + .help = MAP66_help, + .parse = MAP66_parse, + .final_check = MAP66_check, + .save = MAP66_save, + .extra_opts = MAP66_opts, +}; + +void _init(void) +{ + xtables_register_target(&MAP66_tg6_reg); +} |