Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e1e6576c authored by Mohammed Javid's avatar Mohammed Javid Committed by Gerrit - the friendly Code Review server
Browse files

Support full CONE NAT and ADDRESS RESTRICTED CONE NAT



Added support for Full cone NAT and Address Restricted Cone
NAT using the NATTYPE module.

Change-Id: Ib17f87757b86fdb88bbe25bab7d7193b0530f812
Git-commit: 4d71eea17c1eb4362f9d1ae13fce53815b6c9574
Git-repo: source.codeaurora.org/quic/le/kernel/msm/commit/?h=msm-3.4
Signed-off-by: default avatarSmita Ghosh <smitag@codeaurora.org>
Signed-off-by: default avatarMohammed Javid <mjavid@codeaurora.org>
parent f92f804a
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -8,3 +8,4 @@ header-y += ipt_TTL.h
header-y += ipt_ah.h
header-y += ipt_ecn.h
header-y += ipt_ttl.h
header-y += ipt_NATTYPE.h
+25 −0
Original line number Diff line number Diff line
#ifndef _IPT_NATTYPE_H_target
#define _IPT_NATTYPE_H_target

#define NATTYPE_TIMEOUT 300

enum nattype_mode {
	MODE_DNAT,
	MODE_FORWARD_IN,
	MODE_FORWARD_OUT
};

enum nattype_type {
	TYPE_PORT_ADDRESS_RESTRICTED,
	TYPE_ENDPOINT_INDEPENDENT,
	TYPE_ADDRESS_RESTRICTED
};


struct ipt_nattype_info {
	u_int16_t mode;
	u_int16_t type;
};

#endif /*_IPT_NATTYPE_H_target*/
+11 −0
Original line number Diff line number Diff line
@@ -282,6 +282,17 @@ config IP_NF_TARGET_MASQUERADE

	  To compile it as a module, choose M here.  If unsure, say N.

config IP_NF_TARGET_NATTYPE_MODULE
	tristate "NATTYPE target support"
	depends on NF_NAT
	default m if NETFILTER_ADVANCED=n
	help
	  NATTYPE is a special case of NAT: used to support FULL Cone NAT
	  and ADDRESS Restricted Cone NAT. All incoming connections are
	  allowed if there is an outgoing connection using that port.

	  To compile it as a module, choose M here.  If unsure, say N.

config IP_NF_TARGET_NETMAP
	tristate "NETMAP target support"
	depends on NETFILTER_ADVANCED
+1 −0
Original line number Diff line number Diff line
@@ -57,6 +57,7 @@ obj-$(CONFIG_IP_NF_MATCH_RPFILTER) += ipt_rpfilter.o
obj-$(CONFIG_IP_NF_TARGET_CLUSTERIP) += ipt_CLUSTERIP.o
obj-$(CONFIG_IP_NF_TARGET_ECN) += ipt_ECN.o
obj-$(CONFIG_IP_NF_TARGET_MASQUERADE) += ipt_MASQUERADE.o
obj-$(CONFIG_IP_NF_TARGET_NATTYPE_MODULE) += ipt_NATTYPE.o
obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o
obj-$(CONFIG_IP_NF_TARGET_SYNPROXY) += ipt_SYNPROXY.o

+595 −0
Original line number Diff line number Diff line
/* netfilter NATTYPE
 * net/ipv4/netfilter/ipt_NATTYPE.c
 * Endpoint Independent, Address Restricted and Port-Address Restricted
 * NAT types' kernel side implementation.
 *
 * (C) Copyright 2011, Ubicom, Inc.
 *
 * This file is part of the Ubicom32 Linux Kernel Port.
 *
 * The Ubicom32 Linux Kernel Port is free software: you can redistribute
 * it and/or modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation, either version 2 of the
 * License, or (at your option) any later version.
 *
 * The Ubicom32 Linux Kernel Port is distributed in the hope that it
 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
 * the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with the Ubicom32 Linux Kernel Port.  If not,
 * see <http://www.gnu.org/licenses/>.
 *
 * Ubicom32 implementation derived from
 * Cameo's implementation(with many thanks):
 */
#include <linux/types.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/module.h>
#include <net/protocol.h>
#include <net/checksum.h>
#include <net/ip.h>
#include <linux/tcp.h>
#include <net/netfilter/nf_conntrack.h>
#include <net/netfilter/nf_conntrack_core.h>
#include <net/netfilter/nf_nat_rule.h>
#include <linux/netfilter/x_tables.h>
#include <linux/netfilter_ipv4/ipt_NATTYPE.h>
#include <linux/atomic.h>

#if !defined(NATTYPE_DEBUG)
#define DEBUGP(type, args...)
#else
static const char * const types[] = {"TYPE_PORT_ADDRESS_RESTRICTED",
			"TYPE_ENDPOINT_INDEPENDENT",
			"TYPE_ADDRESS_RESTRICTED"};
static const char * const modes[] = {"MODE_DNAT", "MODE_FORWARD_IN",
			"MODE_FORWARD_OUT"};
#define DEBUGP(args...) pr_debug(args)
#endif

/* netfilter NATTYPE TODO:
 * Add magic value checks to data structure.
 */
struct ipt_nattype {
	struct list_head list;
	struct timer_list timeout;
	unsigned short proto;		/* Protocol: TCP or UDP */
	struct nf_nat_ipv4_range range;	/* LAN side src info*/
	unsigned short nat_port;	/* Routed NAT port */
	unsigned int dest_addr;	/* Original egress packets dst addr */
	unsigned short dest_port;/* Original egress packets destination port */
};

/* TODO: It might be better to use a hash table for performance in
 * heavy traffic.
 */
static LIST_HEAD(nattype_list);
static DEFINE_SPINLOCK(nattype_lock);

/* netfilter NATTYPE
 * nattype_nte_debug_print()
 */
static void nattype_nte_debug_print(const struct ipt_nattype *nte,
				    const char *s)
{
	DEBUGP("%p: %s - proto[%d], src[%pI4:%d], nat[<x>:%d], dest[%pI4:%d]\n",
	       nte, s, nte->proto,
		&nte->range.min_ip, ntohs(nte->range.min.all),
		ntohs(nte->nat_port),
		&nte->dest_addr, ntohs(nte->dest_port));
}

/* netfilter NATTYPE nattype_free()
 * Free the object.
 */
static void nattype_free(struct ipt_nattype *nte)
{
	nattype_nte_debug_print(nte, "free");
	kfree(nte);
}

/* netfilter NATTYPE nattype_refresh_timer()
 * Refresh the timer for this object.
 */
static bool nattype_refresh_timer(struct ipt_nattype *nte)
{
	if (del_timer(&nte->timeout)) {
		nte->timeout.expires = jiffies + NATTYPE_TIMEOUT * HZ;
		add_timer(&nte->timeout);
		return true;
	}
	return false;
}

/* netfilter NATTYPE nattype_timer_timeout()
 * The timer has gone off, self-destruct
 */
static void nattype_timer_timeout(unsigned long in_nattype)
{
	struct ipt_nattype *nte = (void *)in_nattype;

	/* netfilter NATTYPE
	 * The race with list deletion is solved by ensuring
	 * that either this code or the list deletion code
	 * but not both will remove the oject.
	 */
	nattype_nte_debug_print(nte, "timeout");
	spin_lock_bh(&nattype_lock);
	list_del(&nte->list);
	spin_unlock_bh(&nattype_lock);
	nattype_free(nte);
}

/* netfilter NATTYPE nattype_packet_in_match()
 * Ingress packet, try to match with this nattype entry.
 */
static bool nattype_packet_in_match(const struct ipt_nattype *nte,
				    struct sk_buff *skb,
				    const struct ipt_nattype_info *info)
{
	const struct iphdr *iph = ip_hdr(skb);
	u16 dst_port = 0;

	/* If the protocols are not the same, no sense in looking
	 * further.
	 */
	if (nte->proto != iph->protocol) {
		DEBUGP("nattype_packet_in_match: protocol failed: nte proto:"
		DEBUGP(" %d, packet proto: %d\n",
		       nte->proto, iph->protocol);
		return false;
	}

	 /* In ADDRESS_RESTRICT, the egress destination must match the source
	  * of this ingress packet.
	  */
	if (info->type == TYPE_ADDRESS_RESTRICTED) {
		if (nte->dest_addr != iph->saddr) {
			DEBUGP("nattype_packet_in_match: dest/src check");
			DEBUGP(" failed: dest_addr: %pI4, src dest: %pI4\n",
			       &nte->dest_addr, &iph->saddr);
			return false;
		}
	}

	/* Obtain the destination port value for TCP or UDP.  The nattype
	 * entries are stored in native (not host).
	 */
	if (iph->protocol == IPPROTO_TCP) {
		struct tcphdr _tcph;
		struct tcphdr *tcph;

		tcph = skb_header_pointer(skb, ip_hdrlen(skb),
					  sizeof(_tcph), &_tcph);
		if (!tcph)
			return false;
		dst_port = tcph->dest;
	} else if (iph->protocol == IPPROTO_UDP) {
		struct udphdr _udph;
		struct udphdr *udph;

		udph = skb_header_pointer(skb, ip_hdrlen(skb),
					  sizeof(_udph), &_udph);
		if (!udph)
			return false;
		dst_port = udph->dest;
	}

	/* Our NAT port must match the ingress pacekt's
	 * destination packet.
	 */
	if (nte->nat_port != dst_port) {
		DEBUGP("nattype_packet_in_match fail: ");
		DEBUGP(" nat port: %d,dest_port: %d\n",
		       ntohs(nte->nat_port), ntohs(dst_port));
		return false;
	}

	/* In either EI or AR mode, the ingress packet's src port
	 * can be anything.
	 */
	nattype_nte_debug_print(nte, "INGRESS MATCH");
	return true;
}

/* netfilter NATTYPE nattype_compare
 * Compare two entries, return true if relevant fields are the same.
 */
static bool nattype_compare(struct ipt_nattype *n1, struct ipt_nattype *n2)
{
	/* netfilter NATTYPE Protocol
	 * compare.
	 */
	if (n1->proto != n2->proto) {
		DEBUGP("nattype_compare: protocol mismatch: %d:%d\n",
		       n1->proto, n2->proto);
		return false;
	}

	 /* netfilter NATTYPE LAN Source compare.
	  * Since we always keep min/max values the same,
	  * just compare the min values.
	  */
	if (n1->range.min_ip != n2->range.min_ip) {
		DEBUGP("nattype_compare: r.min_ip mismatch: %pI4:%pI4\n",
		       &n1->range.min_ip, &n2->range.min_ip);
		return false;
	}

	if (n1->range.min.all != n2->range.min.all) {
		DEBUGP("nattype_compare: r.min mismatch: %d:%d\n",
		       ntohs(n1->range.min.all),
		       ntohs(n2->range.min.all));
		return false;
	}

	/* netfilter NATTYPE
	 * NAT port
	 */
	if (n1->nat_port != n2->nat_port) {
		DEBUGP("nattype_compare: nat_port mistmatch: %d:%d\n",
		       ntohs(n1->nat_port), ntohs(n2->nat_port));
		return false;
	}

	/* netfilter NATTYPE
	 * Destination compare
	 */
	if (n1->dest_addr != n2->dest_addr) {
		DEBUGP("nattype_compare: dest_addr mismatch: %pI4:%pI4\n",
		       &n1->dest_addr, &n2->dest_addr);
		return false;
	}

	if (n1->dest_port != n2->dest_port) {
		DEBUGP("nattype_compare: dest_port mismatch: %d:%d\n",
		       ntohs(n1->dest_port), ntohs(n2->dest_port));
		return false;
	}
	return true;
}

 /**
  *  netfilter NATTYPE nattype_nat()
  * Ingress packet on PRE_ROUTING hook, find match, update conntrack
  * to allow
  **/
static unsigned int nattype_nat(struct sk_buff *skb,
				const struct xt_action_param *par)
{
	struct ipt_nattype *nte;

	if (par->hooknum != NF_INET_PRE_ROUTING)
		return XT_CONTINUE;
	spin_lock_bh(&nattype_lock);
	list_for_each_entry(nte, &nattype_list, list) {
		struct nf_conn *ct;
		enum ip_conntrack_info ctinfo;
		struct nf_nat_ipv4_range newrange;
		unsigned int ret;

		if (!nattype_packet_in_match(nte, skb, par->targinfo))
			continue;

		/* Copy the LAN source data into the ingress' pacekts
		 * conntrack in the reply direction.
		 */
		newrange = nte->range;
		spin_unlock_bh(&nattype_lock);

		/* netfilter NATTYPE Find the
		 * ingress packet's conntrack.
		 */
		ct = nf_ct_get(skb, &ctinfo);
		if (!ct) {
			DEBUGP("ingress packet conntrack not found\n");
			return XT_CONTINUE;
		}

		/* Expand the ingress conntrack
		 * to include the reply as source
		 */
		DEBUGP("Expand ingress conntrack=%p, type=%d, src[%pI4:%d]\n",
		       ct, ctinfo, &newrange.min_ip, ntohs(newrange.min.all));
		ret = nf_nat_setup_info(ct, &newrange, NF_NAT_MANIP_DST);
		DEBUGP("Expand returned: %d\n", ret);
		return ret;
	}
	spin_unlock_bh(&nattype_lock);
	return XT_CONTINUE;
}

/* netfilter NATTYPE nattype_forward()
 * Ingress and Egress packet forwarding hook
 */
static unsigned int nattype_forward(struct sk_buff *skb,
				    const struct xt_action_param *par)
{
	const struct iphdr *iph = ip_hdr(skb);
	void *protoh = (void *)iph + iph->ihl * 4;
	struct ipt_nattype *nte;
	struct ipt_nattype *nte2;
	struct nf_conn *ct;
	enum ip_conntrack_info ctinfo;
	const struct ipt_nattype_info *info = par->targinfo;
	u16 nat_port;

	if (par->hooknum != NF_INET_FORWARD)
		return XT_CONTINUE;

	/* Ingress packet,
	 * refresh the timer if we find an entry.
	 */
	if (info->mode == MODE_FORWARD_IN) {
		spin_lock_bh(&nattype_lock);
		list_for_each_entry(nte, &nattype_list, list) {
			/* netfilter NATTYPE
			 * Compare the ingress packet with the existing
			 * entries looking for a match.
			 */
			if (!nattype_packet_in_match(nte, skb, info))
				continue;

			/* netfilter NATTYPE
			 * Refresh the timer, if we fail, break
			 * out and forward fail as though we never
			 * found the entry.
			 */
			if (!nattype_refresh_timer(nte))
				break;

			/* netfilter NATTYPE
			 * The entry is found and refreshed, the
			 * entry values should not change so print
			 * them outside the lock.
			 */
			spin_unlock_bh(&nattype_lock);
			nattype_nte_debug_print(nte, "refresh");
			DEBUGP("FORWARD_IN_ACCEPT\n");
			return NF_ACCEPT;
		}
		spin_unlock_bh(&nattype_lock);
		DEBUGP("FORWARD_IN_FAIL\n");
		return XT_CONTINUE;
	}

	/* netfilter NATTYPE
	 * Egress packet, create a new rule in our list.  If conntrack does
	 * not have an entry, skip this packet.
	 */
	ct = nf_ct_get(skb, &ctinfo);
	if (!ct || (ctinfo == IP_CT_NEW && ctinfo == IP_CT_RELATED))
		return XT_CONTINUE;

	nat_port = ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.all;

	/* netfilter NATTYPE
	 * Allocate a new entry
	 */
	nte = kzalloc(sizeof(*nte), GFP_ATOMIC | __GFP_NOWARN);
	if (!nte) {
		DEBUGP("kernel malloc fail\n");
		return XT_CONTINUE;
	}

	INIT_LIST_HEAD(&nte->list);

	nte->proto = iph->protocol;
	nte->nat_port = nat_port;
	nte->dest_addr = iph->daddr;
	nte->range.min_ip = iph->saddr;
	nte->range.max_ip = nte->range.min_ip;

	/* netfilter NATTYPE
	 * TOOD: Would it be better to get this information from the
	 * conntrack instead of the headers.
	 */
	if (iph->protocol == IPPROTO_TCP) {
		nte->range.min.tcp.port = ((struct tcphdr *)protoh)->source;
		nte->range.max.tcp.port = nte->range.min.tcp.port;
		nte->dest_port = ((struct tcphdr *)protoh)->dest;
	} else if (iph->protocol == IPPROTO_UDP) {
		nte->range.min.udp.port = ((struct udphdr *)protoh)->source;
		nte->range.max.udp.port = nte->range.min.udp.port;
		nte->dest_port = ((struct udphdr *)protoh)->dest;
	}
	nte->range.flags = (NF_NAT_RANGE_MAP_IPS |
			NF_NAT_RANGE_PROTO_SPECIFIED);

	/* netfilter NATTYPE
	 * Initialize the self-destruct timer.
	 */
	init_timer(&nte->timeout);
	nte->timeout.data = (unsigned long)nte;
	nte->timeout.function = nattype_timer_timeout;

	/* netfilter NATTYPE
	 * We have created the new nte; however, it might not be unique.
	 * Search the list for a matching entry.  If found, throw away
	 * the new entry and refresh the old.  If not found, atomically
	 * insert the new entry on the list.
	 */
	spin_lock_bh(&nattype_lock);
	list_for_each_entry(nte2, &nattype_list, list) {
		if (!nattype_compare(nte, nte2))
			continue;

		/* netfilter NATTYPE
		 * If we can not refresh this entry, insert our new
		 * entry as this one is timed out and will be removed
		 * from the list shortly.
		 */
		if (!nattype_refresh_timer(nte2))
			break;

		/* netfilter NATTYPE
		 * Found and refreshed an existing entry.  Its values
		 * do not change so print the values outside of the lock.
		 *
		 * Free up the new entry.
		 */
		spin_unlock_bh(&nattype_lock);
		nattype_nte_debug_print(nte2, "refresh");
		nattype_free(nte);
		return XT_CONTINUE;
	}

	/* netfilter NATTYPE
	 * Add the new entry to the list.
	 */
	nte->timeout.expires = jiffies + (NATTYPE_TIMEOUT  * HZ);
	add_timer(&nte->timeout);
	list_add(&nte->list, &nattype_list);
	spin_unlock_bh(&nattype_lock);
	nattype_nte_debug_print(nte, "ADD");
	return XT_CONTINUE;
}

/* netfilter NATTYPE
 * nattype_target()
 *	One of the iptables hooks has a packet for us to analyze, do so.
 */
static unsigned int nattype_target(struct sk_buff *skb,
				   const struct xt_action_param *par)
{
	const struct ipt_nattype_info *info = par->targinfo;
	const struct iphdr *iph = ip_hdr(skb);

	/* netfilter NATTYPE
	 * The default behavior for Linux is PORT and ADDRESS restricted. So
	 * we do not need to create rules/entries if we are in that mode.
	 */
	if (info->type == TYPE_PORT_ADDRESS_RESTRICTED)
		return XT_CONTINUE;

	/* netfilter NATTYPE
	 * Check if we have enough data in the skb.
	 */
	if (skb->len < ip_hdrlen(skb))
		return XT_CONTINUE;

	/* netfilter NATTYPE
	 * We can not perform endpoint filtering on anything but UDP and TCP.
	 */
	if ((iph->protocol != IPPROTO_TCP) && (iph->protocol != IPPROTO_UDP))
		return XT_CONTINUE;

	/* netfilter NATTYPE
	 * Check for LAND attack and ignore.
	 */
	if (iph->daddr == iph->saddr)
		return XT_CONTINUE;

	/* netfilter NATTYPE
	 * Check that we have valid source and destination addresses.
	 */
	if ((iph->daddr == (__be32)0) || (iph->saddr == (__be32)0))
		return XT_CONTINUE;

	DEBUGP("nattype_target: type = %s, mode = %s\n",
	       types[info->type], modes[info->mode]);

	/* netfilter NATTYPE
	 * TODO: why have mode at all since par->hooknum provides
	 * this information?
	 */
	switch (info->mode) {
	case MODE_DNAT:
		return nattype_nat(skb, par);
	case MODE_FORWARD_OUT:
	case MODE_FORWARD_IN:
		return nattype_forward(skb, par);
	}
	return XT_CONTINUE;
}

/* netfilter NATTYPE
 * nattype_check()
 *	check info (mode/type) set by iptables.
 */
static int nattype_check(const struct xt_tgchk_param *par)
{
	const struct ipt_nattype_info *info = par->targinfo;
	struct list_head *cur, *tmp;

	if ((info->type != TYPE_PORT_ADDRESS_RESTRICTED) &&
	    (info->type != TYPE_ENDPOINT_INDEPENDENT) &&
		(info->type != TYPE_ADDRESS_RESTRICTED)) {
		DEBUGP("nattype_check: unknown type: %d\n", info->type);
		return -EINVAL;
	}

	if (info->mode != MODE_DNAT && info->mode != MODE_FORWARD_IN &&
	    info->mode != MODE_FORWARD_OUT) {
		DEBUGP("nattype_check: unknown mode - %d.\n", info->mode);
		return -EINVAL;
	}

	DEBUGP("nattype_check: type = %s, mode = %s\n",
	       types[info->type], modes[info->mode]);

	if (par->hook_mask & ~((1 << NF_INET_PRE_ROUTING) |
		(1 << NF_INET_FORWARD))) {
		DEBUGP("nattype_check: bad hooks %x.\n", par->hook_mask);
		return -EINVAL;
	}

	/* netfilter NATTYPE
	 * Remove all entries from the nattype list.
	 */
drain:
	spin_lock_bh(&nattype_lock);
	list_for_each_safe(cur, tmp, &nattype_list) {
		struct ipt_nattype *nte = (void *)cur;

		/* netfilter NATTYPE
		 * If the timeout is in process, it will tear
		 * us down.  Since it is waiting on the spinlock
		 * we have to give up the spinlock to give the
		 * timeout on another CPU a chance to run.
		 */
		if (!del_timer(&nte->timeout)) {
			spin_unlock_bh(&nattype_lock);
			goto drain;
		}

		DEBUGP("%p: removing from list\n", nte);
		list_del(&nte->list);
		spin_unlock_bh(&nattype_lock);
		nattype_free(nte);
		goto drain;
	}
	spin_unlock_bh(&nattype_lock);
	return 0;
}

static struct xt_target nattype = {
	.name		= "NATTYPE",
	.family		= NFPROTO_IPV4,
	.target		= nattype_target,
	.checkentry	= nattype_check,
	.targetsize	= sizeof(struct ipt_nattype_info),
	.hooks		= ((1 << NF_INET_PRE_ROUTING) |
				(1 << NF_INET_FORWARD)),
	.me		= THIS_MODULE,
};

static int __init init(void)
{
	return xt_register_target(&nattype);
}

static void __exit fini(void)
{
	xt_unregister_target(&nattype);
}

module_init(init);
module_exit(fini);

MODULE_LICENSE("GPL");