/* * MAP66: Network Address Translation IPv6-to-IPv6 as * proposed in the IETF's second NAT66 draft document. * (c) 2010 sven-ola()gmx.de */ #include #include #include #include #include #define IPTABLES_VERSION_CMP(a,b,c) (((a) << 16) + ((b) << 8) + (c)) #if IPTABLES_VERSION_CODE < IPTABLES_VERSION_CMP(1,4,0) # include # 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 # 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 #ifndef XT_ALIGN # define XT_ALIGN IP6T_ALIGN #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" " --" IP6T_MAP66_DST_TO " ipv6addr/prefixlength (Prefix to map IPv6 destination address to)\n" " --" IP6T_MAP66_SRC_TO " ipv6addr/prefixlength (Prefix to map IPv6 source address to)\n" " --nocheck (Disables the do-not-map-to-my-addr check)\n" " --unbalanced (Don't balance address for csum neutrality)\n" "\n" "Note: you need two ip6tables rules to map an internal network\n" "using ULAs to/from 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 --" IP6T_MAP66_DST_TO " FD01:0203:0405::/48\n" "ip6tables -t mangle -I POSTROUTING -o eth0 -s FD01:0203:0405::/48 -j MAP66 --" IP6T_MAP66_SRC_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, "--" IP6T_MAP66_DST_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 --" IP6T_MAP66_DST_TO); } if (0 != (IP6T_MAP66_OPT_DST_TO & *flags)) { xtables_error(PARAMETER_PROBLEM, "Multiple --" IP6T_MAP66_DST_TO " not supported"); } *flags |= IP6T_MAP66_OPT_DST_TO; info->mapflags |= IP6T_MAP66_OPT_DST_TO; if (NULL == (p = strchr(optarg, '/'))) { xtables_error(PARAMETER_PROBLEM, "Missing '/' character in --" IP6T_MAP66_DST_TO ": \"%s\"", optarg); } *p = '\0'; if (1 != inet_pton(AF_INET6, optarg, &info->pfix_dst_to)) { xtables_error(PARAMETER_PROBLEM, "Invalid IPv6 address in --" IP6T_MAP66_DST_TO ": \"%s\"", optarg); } i = atoi(p + 1); if (0 >= i || 128 <= i || 0 != i % 16) { xtables_error(PARAMETER_PROBLEM, "Invalid prefix length in --" IP6T_MAP66_DST_TO ": \"%s\" (use /112, /96 .. /16)", p + 1); } info->pfix_dst_len = i / 16; info->pfix_dst_csum = ~csum16((const u_int16_t *)&info->pfix_dst_to, info->pfix_dst_len); return 1; break; case '2': if (!optarg) { xtables_error(PARAMETER_PROBLEM, "--" IP6T_MAP66_SRC_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 --" IP6T_MAP66_SRC_TO); } if (0 != (IP6T_MAP66_OPT_SRC_TO & *flags)) { xtables_error(PARAMETER_PROBLEM, "Multiple --" IP6T_MAP66_SRC_TO " not supported"); } *flags |= IP6T_MAP66_OPT_SRC_TO; info->mapflags |= IP6T_MAP66_OPT_SRC_TO; if (NULL == (p = strchr(optarg, '/'))) { xtables_error(PARAMETER_PROBLEM, "Missing '/' character in --" IP6T_MAP66_SRC_TO ": \"%s\"", optarg); } *p = '\0'; if (1 != inet_pton(AF_INET6, optarg, &info->pfix_src_to)) { xtables_error(PARAMETER_PROBLEM, "Invalid IPv6 address in --" IP6T_MAP66_SRC_TO ": \"%s\"", optarg); } i = atoi(p + 1); if (0 >= i || 128 < i || 0 != i % 16) { xtables_error(PARAMETER_PROBLEM, "Invalid prefix length in --" IP6T_MAP66_SRC_TO ": \"%s\" (use /128, /112, /96 .. /16)", p + 1); } info->pfix_src_len = i / 16; info->pfix_src_csum = ~csum16((const u_int16_t *)&info->pfix_src_to, info->pfix_src_len); return 1; break; case '3': if (0 != (IP6T_MAP66_OPT_NOCHECK & *flags)) { xtables_error(PARAMETER_PROBLEM, "Multiple --nocheck not supported"); } info->mapflags |= IP6T_MAP66_OPT_NOCHECK; *flags |= IP6T_MAP66_OPT_NOCHECK; return 1; break; case '4': if (0 != (IP6T_MAP66_OPT_UNBALANCED & *flags)) { xtables_error(PARAMETER_PROBLEM, "Multiple --unbalanced not supported"); } info->mapflags |= IP6T_MAP66_OPT_UNBALANCED; *flags |= IP6T_MAP66_OPT_UNBALANCED; return 1; break; } return 0; } static void MAP66_check(unsigned int flags) { if (0 == ((IP6T_MAP66_OPT_DST_TO | IP6T_MAP66_OPT_SRC_TO) & flags)) { xtables_error(PARAMETER_PROBLEM, "You must specify --" IP6T_MAP66_DST_TO " or --" IP6T_MAP66_SRC_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; if (0 != (IP6T_MAP66_OPT_DST_TO & info->mapflags)) { printf("--" IP6T_MAP66_DST_TO " %s/%d ", inet_ntop(AF_INET6, &info->pfix_dst_to, s, sizeof(s)), 16 * info->pfix_dst_len); } if (0 != (IP6T_MAP66_OPT_SRC_TO & info->mapflags)) { printf("--" IP6T_MAP66_SRC_TO " %s/%d ", inet_ntop(AF_INET6, &info->pfix_src_to, s, sizeof(s)), 16 * info->pfix_src_len); } if (0 != (IP6T_MAP66_OPT_NOCHECK & info->mapflags)) { printf("--nocheck "); } if (0 != (IP6T_MAP66_OPT_UNBALANCED & info->mapflags)) { printf("--unbalanced "); } } static struct option MAP66_opts[] = { { .name = IP6T_MAP66_DST_TO, .has_arg = 1, .flag = NULL, .val = '1' }, { .name = IP6T_MAP66_SRC_TO, .has_arg = 1, .flag = NULL, .val = '2' }, { .name = "nocheck", .has_arg = 0, .flag = NULL, .val = '3' }, { .name = "unbalanced", .has_arg = 0, .flag = NULL, .val = '4' }, { .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); }