diff options
author | Chion Tang <sdspeedonion@gmail.com> | 2018-01-19 17:53:25 +0000 |
---|---|---|
committer | Chion Tang <sdspeedonion@gmail.com> | 2018-01-19 17:53:25 +0000 |
commit | ec47c790e054d3ca7794dc6d3601bde77388141f (patch) | |
tree | 826b3833cb37e01c099e237603c650e561859be4 | |
download | netfilter-full-cone-nat-ec47c790e054d3ca7794dc6d3601bde77388141f.tar.gz netfilter-full-cone-nat-ec47c790e054d3ca7794dc6d3601bde77388141f.tar.bz2 netfilter-full-cone-nat-ec47c790e054d3ca7794dc6d3601bde77388141f.zip |
init
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | libipt_FULLCONENAT.c | 190 | ||||
-rw-r--r-- | xt_FULLCONENAT.c | 182 |
3 files changed, 373 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e43b0f9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Store diff --git a/libipt_FULLCONENAT.c b/libipt_FULLCONENAT.c new file mode 100644 index 0000000..6f8a56f --- /dev/null +++ b/libipt_FULLCONENAT.c @@ -0,0 +1,190 @@ +#include <stdio.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <xtables.h> +#include <limits.h> /* INT_MAX in ip_tables.h */ +#include <linux/netfilter_ipv4/ip_tables.h> +#include <linux/netfilter/nf_nat.h> + +enum { + O_TO_PORTS = 0, + O_RANDOM, + O_RANDOM_FULLY, +}; + +static void FULLCONENAT_help(void) +{ + printf( +"FULLCONENAT target options:\n" +" --to-ports <port>[-<port>]\n" +" Port (range) to map to.\n" +" --random\n" +" Randomize source port.\n" +" --random-fully\n" +" Fully randomize source port.\n"); +} + +static const struct xt_option_entry FULLCONENAT_opts[] = { + {.name = "to-ports", .id = O_TO_PORTS, .type = XTTYPE_STRING}, + {.name = "random", .id = O_RANDOM, .type = XTTYPE_NONE}, + {.name = "random-fully", .id = O_RANDOM_FULLY, .type = XTTYPE_NONE}, + XTOPT_TABLEEND, +}; + +static void FULLCONENAT_init(struct xt_entry_target *t) +{ + struct nf_nat_ipv4_multi_range_compat *mr = (struct nf_nat_ipv4_multi_range_compat *)t->data; + + /* Actually, it's 0, but it's ignored at the moment. */ + mr->rangesize = 1; +} + +/* Parses ports */ +static void +parse_ports(const char *arg, struct nf_nat_ipv4_multi_range_compat *mr) +{ + char *end; + unsigned int port, maxport; + + mr->range[0].flags |= NF_NAT_RANGE_PROTO_SPECIFIED; + + if (!xtables_strtoui(arg, &end, &port, 0, UINT16_MAX)) + xtables_param_act(XTF_BAD_VALUE, "FULLCONENAT", "--to-ports", arg); + + switch (*end) { + case '\0': + mr->range[0].min.tcp.port + = mr->range[0].max.tcp.port + = htons(port); + return; + case '-': + if (!xtables_strtoui(end + 1, NULL, &maxport, 0, UINT16_MAX)) + break; + + if (maxport < port) + break; + + mr->range[0].min.tcp.port = htons(port); + mr->range[0].max.tcp.port = htons(maxport); + return; + default: + break; + } + xtables_param_act(XTF_BAD_VALUE, "FULLCONENAT", "--to-ports", arg); +} + +static void FULLCONENAT_parse(struct xt_option_call *cb) +{ + const struct ipt_entry *entry = cb->xt_entry; + int portok; + struct nf_nat_ipv4_multi_range_compat *mr = cb->data; + + if (entry->ip.proto == IPPROTO_TCP + || entry->ip.proto == IPPROTO_UDP + || entry->ip.proto == IPPROTO_SCTP + || entry->ip.proto == IPPROTO_DCCP + || entry->ip.proto == IPPROTO_ICMP) + portok = 1; + else + portok = 0; + + xtables_option_parse(cb); + switch (cb->entry->id) { + case O_TO_PORTS: + if (!portok) + xtables_error(PARAMETER_PROBLEM, + "Need TCP, UDP, SCTP or DCCP with port specification"); + parse_ports(cb->arg, mr); + break; + case O_RANDOM: + mr->range[0].flags |= NF_NAT_RANGE_PROTO_RANDOM; + break; + case O_RANDOM_FULLY: + mr->range[0].flags |= NF_NAT_RANGE_PROTO_RANDOM_FULLY; + break; + } +} + +static void +FULLCONENAT_print(const void *ip, const struct xt_entry_target *target, + int numeric) +{ + const struct nf_nat_ipv4_multi_range_compat *mr = (const void *)target->data; + const struct nf_nat_ipv4_range *r = &mr->range[0]; + + if (r->flags & NF_NAT_RANGE_PROTO_SPECIFIED) { + printf(" masq ports: "); + printf("%hu", ntohs(r->min.tcp.port)); + if (r->max.tcp.port != r->min.tcp.port) + printf("-%hu", ntohs(r->max.tcp.port)); + } + + if (r->flags & NF_NAT_RANGE_PROTO_RANDOM) + printf(" random"); + + if (r->flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY) + printf(" random-fully"); +} + +static void +FULLCONENAT_save(const void *ip, const struct xt_entry_target *target) +{ + const struct nf_nat_ipv4_multi_range_compat *mr = (const void *)target->data; + const struct nf_nat_ipv4_range *r = &mr->range[0]; + + if (r->flags & NF_NAT_RANGE_PROTO_SPECIFIED) { + printf(" --to-ports %hu", ntohs(r->min.tcp.port)); + if (r->max.tcp.port != r->min.tcp.port) + printf("-%hu", ntohs(r->max.tcp.port)); + } + + if (r->flags & NF_NAT_RANGE_PROTO_RANDOM) + printf(" --random"); + + if (r->flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY) + printf(" --random-fully"); +} + +static int FULLCONENAT_xlate(struct xt_xlate *xl, + const struct xt_xlate_tg_params *params) +{ + const struct nf_nat_ipv4_multi_range_compat *mr = + (const void *)params->target->data; + const struct nf_nat_ipv4_range *r = &mr->range[0]; + + xt_xlate_add(xl, "fullconenat"); + + if (r->flags & NF_NAT_RANGE_PROTO_SPECIFIED) { + xt_xlate_add(xl, " to :%hu", ntohs(r->min.tcp.port)); + if (r->max.tcp.port != r->min.tcp.port) + xt_xlate_add(xl, "-%hu", ntohs(r->max.tcp.port)); + } + + xt_xlate_add(xl, " "); + if (r->flags & NF_NAT_RANGE_PROTO_RANDOM) + xt_xlate_add(xl, "random "); + + return 1; +} + +static struct xtables_target fullconenat_tg_reg = { + .name = "FULLCONENAT", + .version = XTABLES_VERSION, + .family = NFPROTO_IPV4, + .size = XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)), + .userspacesize = XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)), + .help = FULLCONENAT_help, + .init = FULLCONENAT_init, + .x6_parse = FULLCONENAT_parse, + .print = FULLCONENAT_print, + .save = FULLCONENAT_save, + .x6_options = FULLCONENAT_opts, + .xlate = FULLCONENAT_xlate, +}; + +void _init(void) +{ + xtables_register_target(&fullconenat_tg_reg); +} diff --git a/xt_FULLCONENAT.c b/xt_FULLCONENAT.c new file mode 100644 index 0000000..3d63264 --- /dev/null +++ b/xt_FULLCONENAT.c @@ -0,0 +1,182 @@ +#include <linux/ip.h> +#include <linux/udp.h> +#include <linux/time.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/inetdevice.h> +#include <linux/ipv6.h> +#include <linux/netfilter.h> +#include <linux/netfilter_ipv4.h> +#include <linux/netfilter_ipv6.h> +#include <linux/netfilter/x_tables.h> +#include <net/netfilter/nf_nat.h> + +#define MAX_PORTS 65536 + +struct natmapping { + __be32 int_addr; + uint16_t int_port; +}; + +static struct natmapping mappings[MAX_PORTS]; + +static int is_mapping_active(const struct natmapping* mapping) +{ + return mapping->int_addr != 0; +} + +static __be32 get_device_ip(const struct net_device* dev) { + struct in_device* in_dev; + struct in_ifaddr* if_info; + + in_dev = dev->ip_ptr; + if (in_dev == NULL) { + return 0; + } + if_info = in_dev->ifa_list; + if (if_info) { + return if_info->ifa_local; + } else { + return 0; + } +} + +static void tg_destroy(const struct xt_tgdtor_param *par) +{ + nf_ct_netns_put(par->net, par->family); +} + +static unsigned int fullconenat_tg4(struct sk_buff *skb, const struct xt_action_param *par) +{ + const struct nf_nat_ipv4_multi_range_compat *mr; + const struct nf_nat_ipv4_range *range; + + struct nf_conn *ct; + enum ip_conntrack_info ctinfo; + struct nf_conntrack_tuple *ct_tuple, *ct_tuple_origin; + + struct natmapping* mapping; + unsigned int ret; + struct nf_nat_range newrange; + + __be32 new_ip, ip; + uint16_t port, original_port; + uint8_t protonum; + + mr = par->targinfo; + range = &mr->range[0]; + + mapping = NULL; + ret = XT_CONTINUE; + + ct = nf_ct_get(skb, &ctinfo); + + memset(&newrange.min_addr, 0, sizeof(newrange.min_addr)); + memset(&newrange.max_addr, 0, sizeof(newrange.max_addr)); + newrange.flags = mr->range[0].flags | NF_NAT_RANGE_MAP_IPS; + newrange.min_proto = mr->range[0].min; + newrange.max_proto = mr->range[0].max; + + if (xt_hooknum(par) == NF_INET_PRE_ROUTING) { + //inbound packets + ct_tuple = &(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); + + protonum = (ct_tuple->dst).protonum; + if (protonum != IPPROTO_UDP) { + return ret; + } + ip = (ct_tuple->src).u3.ip; + port = be16_to_cpu((ct_tuple->dst).u.udp.port); + + mapping = &mappings[port]; + if (is_mapping_active(mapping)) { + printk("DNAT: src_ip=%pI4; dst_port=%d; map_to=%pI4:%d \n", &ip, port, &(mapping->int_addr), mapping->int_port); + + newrange.flags |= NF_NAT_RANGE_PROTO_SPECIFIED; + newrange.min_addr.ip = mapping->int_addr; + newrange.max_addr.ip = mapping->int_addr; + newrange.min_proto.udp.port = cpu_to_be16(mapping->int_port); + newrange.max_proto = newrange.min_proto; + return nf_nat_setup_info(ct, &newrange, HOOK2MANIP(xt_hooknum(par))); + } + + } else if (xt_hooknum(par) == NF_INET_POST_ROUTING) { + //outbound packets + new_ip = get_device_ip(skb->dev); + newrange.min_addr.ip = new_ip; + newrange.max_addr.ip = new_ip; + ret = nf_nat_setup_info(ct, &newrange, HOOK2MANIP(xt_hooknum(par))); + + ct_tuple_origin = &(ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple); + ct_tuple = &(ct->tuplehash[IP_CT_DIR_REPLY].tuple); + + protonum = (ct_tuple->dst).protonum; + if (protonum != IPPROTO_UDP) { + return ret; + } + + ip = (ct_tuple_origin->src).u3.ip; + original_port = be16_to_cpu((ct_tuple_origin->src).u.udp.port); + port = be16_to_cpu((ct_tuple->dst).u.udp.port); + + printk("SNAT: src_ip=%pI4; src_port=%d; mapped_src_port=%d; \n", &ip, original_port, port); + + mapping = &mappings[port]; + mapping->int_addr = ip; + mapping->int_port = original_port; + } + + return ret; +} + +static int tg4_check(const struct xt_tgchk_param *par) +{ + // const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo; + + return nf_ct_netns_get(par->net, par->family); +} + +static struct xt_target tg_reg[] __read_mostly = { + { + .name = "FULLCONENAT", + .family = NFPROTO_IPV4, + .revision = 0, + .target = fullconenat_tg4, + .targetsize = sizeof(struct nf_nat_ipv4_multi_range_compat), + .table = "nat", + .hooks = (1 << NF_INET_PRE_ROUTING) | + (1 << NF_INET_POST_ROUTING) | + (1 << NF_INET_LOCAL_OUT) | + (1 << NF_INET_LOCAL_IN), + .checkentry = tg4_check, + .destroy = tg_destroy, + .me = THIS_MODULE, + }, +}; + +static int __init tg_init(void) +{ + int i; + + printk("xt_FULLCONENAT init"); + for (i=0; i<MAX_PORTS; i++) { + mappings[i].int_addr = 0; + mappings[i].int_port = 0; + } + return xt_register_targets(tg_reg, ARRAY_SIZE(tg_reg)); +} + +static void tg_exit(void) +{ + printk("xt_FULLCONENAT exit"); + xt_unregister_targets(tg_reg, ARRAY_SIZE(tg_reg)); +} + +module_init(tg_init); +module_exit(tg_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Xtables: implementation of UDP-only full cone NAT"); +MODULE_AUTHOR("Chion Tang <tech@chionlab.moe>"); +MODULE_ALIAS("ipt_fullconenat"); |