From b3e3a2fde656a3cba30fe3deaa10b44a6a39224e Mon Sep 17 00:00:00 2001 From: Chion Tang Date: Sat, 10 Mar 2018 17:24:43 +0000 Subject: feature: manage port allocation in this module --- xt_FULLCONENAT.c | 69 +++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 56 insertions(+), 13 deletions(-) diff --git a/xt_FULLCONENAT.c b/xt_FULLCONENAT.c index d2303cd..1a2d279 100644 --- a/xt_FULLCONENAT.c +++ b/xt_FULLCONENAT.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -141,6 +142,53 @@ static void fullconenat_tg_destroy(const struct xt_tgdtor_param *par) nf_ct_netns_put(par->net, par->family); } +static uint16_t find_appropriate_port(const uint16_t original_port, const struct nf_nat_ipv4_range *range, struct nf_conn *ct) { + uint16_t min, start, selected, range_size, i; + struct natmapping* mapping = NULL; + + if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) { + min = be16_to_cpu((range->min).udp.port); + range_size = be16_to_cpu((range->max).udp.port) - min + 1; + } else { + /* minimum port is 1024. same behavior as default linux NAT. */ + min = 1024; + range_size = 65535 - min + 1; + } + + if ((range->flags & NF_NAT_RANGE_PROTO_RANDOM) + || (range->flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY)) { + /* for now we do the same thing for both --random and --random-fully */ + + /* select a random starting point */ + start = (uint16_t)(prandom_u32() % (u32)range_size); + } else { + + if ((original_port >= min && original_port <= min + range_size - 1) + || !(range->flags & NF_NAT_RANGE_PROTO_SPECIFIED)) { + /* 1. try to preserve the port if it's available */ + mapping = get_mapping(original_port, 0); + if (mapping == NULL || !(is_mapping_active(mapping, ct))) { + return original_port; + } + } + + /* otherwise, we start from zero */ + start = 0; + } + + for (i = 0; i < range_size; i++) { + /* 2. try to find an available port */ + selected = min + ((start + i) % range_size); + mapping = get_mapping(selected, 0); + if (mapping == NULL || !(is_mapping_active(mapping, ct))) { + return selected; + } + } + + /* 3. at least we tried. rewrite a privous mapping. */ + return min + start; +} + static unsigned int fullconenat_tg(struct sk_buff *skb, const struct xt_action_param *par) { const struct nf_nat_ipv4_multi_range_compat *mr; @@ -155,7 +203,7 @@ static unsigned int fullconenat_tg(struct sk_buff *skb, const struct xt_action_p struct nf_nat_range newrange; __be32 new_ip, ip; - uint16_t port, original_port; + uint16_t port, original_port, want_port; uint8_t protonum; mr = par->targinfo; @@ -194,7 +242,7 @@ static unsigned int fullconenat_tg(struct sk_buff *skb, const struct xt_action_p return ret; } if (is_mapping_active(mapping, ct)) { - newrange.flags |= NF_NAT_RANGE_PROTO_SPECIFIED; + newrange.flags = NF_NAT_RANGE_MAP_IPS | 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); @@ -223,22 +271,17 @@ static unsigned int fullconenat_tg(struct sk_buff *skb, const struct xt_action_p /* outbound nat: if a previously established mapping is active, we will reuse that mapping. */ - newrange.flags |= NF_NAT_RANGE_PROTO_SPECIFIED; + newrange.flags = NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED; newrange.min_proto.udp.port = cpu_to_be16(src_mapping->port); newrange.max_proto = newrange.min_proto; - } else if (!(newrange.flags & NF_NAT_RANGE_PROTO_RANDOM) - && !(newrange.flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY)) { + } else { + want_port = find_appropriate_port(original_port, range, ct); - /* if multiple LAN hosts are using the same source port - and any PROTO_RANDOM is not specified, - we force a random port allocation to avoid collision. */ + newrange.flags = NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED; + newrange.min_proto.udp.port = cpu_to_be16(want_port); + newrange.max_proto = newrange.min_proto; - src_mapping = get_mapping(original_port, 0); - if (src_mapping != NULL - && is_mapping_active(src_mapping, ct)) { - newrange.flags |= NF_NAT_RANGE_PROTO_RANDOM; - } } } -- cgit v1.2.3 From 7cd889c96f14d93a1292a70901a5db14e6b38227 Mon Sep 17 00:00:00 2001 From: Chion Tang Date: Sun, 11 Mar 2018 03:33:23 +0000 Subject: linux 4.9.x compat --- xt_FULLCONENAT-old-kernel.c | 69 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 56 insertions(+), 13 deletions(-) diff --git a/xt_FULLCONENAT-old-kernel.c b/xt_FULLCONENAT-old-kernel.c index b5f4e45..3d46fee 100644 --- a/xt_FULLCONENAT-old-kernel.c +++ b/xt_FULLCONENAT-old-kernel.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -143,6 +144,53 @@ static void fullconenat_tg_destroy(const struct xt_tgdtor_param *par) } +static uint16_t find_appropriate_port(const uint16_t original_port, const struct nf_nat_ipv4_range *range, struct nf_conn *ct) { + uint16_t min, start, selected, range_size, i; + struct natmapping* mapping = NULL; + + if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) { + min = be16_to_cpu((range->min).udp.port); + range_size = be16_to_cpu((range->max).udp.port) - min + 1; + } else { + /* minimum port is 1024. same behavior as default linux NAT. */ + min = 1024; + range_size = 65535 - min + 1; + } + + if ((range->flags & NF_NAT_RANGE_PROTO_RANDOM) + || (range->flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY)) { + /* for now we do the same thing for both --random and --random-fully */ + + /* select a random starting point */ + start = (uint16_t)(prandom_u32() % (u32)range_size); + } else { + + if ((original_port >= min && original_port <= min + range_size - 1) + || !(range->flags & NF_NAT_RANGE_PROTO_SPECIFIED)) { + /* 1. try to preserve the port if it's available */ + mapping = get_mapping(original_port, 0); + if (mapping == NULL || !(is_mapping_active(mapping, ct))) { + return original_port; + } + } + + /* otherwise, we start from zero */ + start = 0; + } + + for (i = 0; i < range_size; i++) { + /* 2. try to find an available port */ + selected = min + ((start + i) % range_size); + mapping = get_mapping(selected, 0); + if (mapping == NULL || !(is_mapping_active(mapping, ct))) { + return selected; + } + } + + /* 3. at least we tried. rewrite a privous mapping. */ + return min + start; +} + static unsigned int fullconenat_tg(struct sk_buff *skb, const struct xt_action_param *par) { const struct nf_nat_ipv4_multi_range_compat *mr; @@ -157,7 +205,7 @@ static unsigned int fullconenat_tg(struct sk_buff *skb, const struct xt_action_p struct nf_nat_range newrange; __be32 new_ip, ip; - uint16_t port, original_port; + uint16_t port, original_port, want_port; uint8_t protonum; mr = par->targinfo; @@ -196,7 +244,7 @@ static unsigned int fullconenat_tg(struct sk_buff *skb, const struct xt_action_p return ret; } if (is_mapping_active(mapping, ct)) { - newrange.flags |= NF_NAT_RANGE_PROTO_SPECIFIED; + newrange.flags = NF_NAT_RANGE_MAP_IPS | 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); @@ -225,22 +273,17 @@ static unsigned int fullconenat_tg(struct sk_buff *skb, const struct xt_action_p /* outbound nat: if a previously established mapping is active, we will reuse that mapping. */ - newrange.flags |= NF_NAT_RANGE_PROTO_SPECIFIED; + newrange.flags = NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED; newrange.min_proto.udp.port = cpu_to_be16(src_mapping->port); newrange.max_proto = newrange.min_proto; - } else if (!(newrange.flags & NF_NAT_RANGE_PROTO_RANDOM) - && !(newrange.flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY)) { + } else { + want_port = find_appropriate_port(original_port, range, ct); - /* if multiple LAN hosts are using the same source port - and any PROTO_RANDOM is not specified, - we force a random port allocation to avoid collision. */ + newrange.flags = NF_NAT_RANGE_MAP_IPS | NF_NAT_RANGE_PROTO_SPECIFIED; + newrange.min_proto.udp.port = cpu_to_be16(want_port); + newrange.max_proto = newrange.min_proto; - src_mapping = get_mapping(original_port, 0); - if (src_mapping != NULL - && is_mapping_active(src_mapping, ct)) { - newrange.flags |= NF_NAT_RANGE_PROTO_RANDOM; - } } } -- cgit v1.2.3 From dccc893d1b691e1a99b7ec39a3f0bbd40fb67bc6 Mon Sep 17 00:00:00 2001 From: Chion Tang Date: Sun, 11 Mar 2018 09:25:51 +0000 Subject: refactor: handle failed nf_nat_setup_info() --- xt_FULLCONENAT-old-kernel.c | 11 +++++++---- xt_FULLCONENAT.c | 11 +++++++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/xt_FULLCONENAT-old-kernel.c b/xt_FULLCONENAT-old-kernel.c index 3d46fee..9a8b6ed 100644 --- a/xt_FULLCONENAT-old-kernel.c +++ b/xt_FULLCONENAT-old-kernel.c @@ -208,6 +208,9 @@ static unsigned int fullconenat_tg(struct sk_buff *skb, const struct xt_action_p uint16_t port, original_port, want_port; uint8_t protonum; + ip = 0; + original_port = 0; + mr = par->targinfo; range = &mr->range[0]; @@ -293,14 +296,14 @@ static unsigned int fullconenat_tg(struct sk_buff *skb, const struct xt_action_p ret = nf_nat_setup_info(ct, &newrange, HOOK2MANIP(xt_hooknum(par))); - /* the reply tuple contains the mapped port. */ - ct_tuple = &(ct->tuplehash[IP_CT_DIR_REPLY].tuple); - - if (protonum != IPPROTO_UDP) { + if (protonum != IPPROTO_UDP || ret != NF_ACCEPT) { spin_unlock(&fullconenat_lock); return ret; } + /* the reply tuple contains the mapped port. */ + ct_tuple = &(ct->tuplehash[IP_CT_DIR_REPLY].tuple); + port = be16_to_cpu((ct_tuple->dst).u.udp.port); /* store the mapping information to our mapping table */ diff --git a/xt_FULLCONENAT.c b/xt_FULLCONENAT.c index 1a2d279..ed65509 100644 --- a/xt_FULLCONENAT.c +++ b/xt_FULLCONENAT.c @@ -206,6 +206,9 @@ static unsigned int fullconenat_tg(struct sk_buff *skb, const struct xt_action_p uint16_t port, original_port, want_port; uint8_t protonum; + ip = 0; + original_port = 0; + mr = par->targinfo; range = &mr->range[0]; @@ -291,14 +294,14 @@ static unsigned int fullconenat_tg(struct sk_buff *skb, const struct xt_action_p ret = nf_nat_setup_info(ct, &newrange, HOOK2MANIP(xt_hooknum(par))); - /* the reply tuple contains the mapped port. */ - ct_tuple = &(ct->tuplehash[IP_CT_DIR_REPLY].tuple); - - if (protonum != IPPROTO_UDP) { + if (protonum != IPPROTO_UDP || ret != NF_ACCEPT) { spin_unlock(&fullconenat_lock); return ret; } + /* the reply tuple contains the mapped port. */ + ct_tuple = &(ct->tuplehash[IP_CT_DIR_REPLY].tuple); + port = be16_to_cpu((ct_tuple->dst).u.udp.port); /* store the mapping information to our mapping table */ -- cgit v1.2.3