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

Commit 878fd3bf authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "netfilter: Changes to handle segmentation in SIP ALG"

parents 07e8ff83 7ec5a901
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#include <linux/bitops.h>
#include <linux/compiler.h>
#include <linux/atomic.h>
#include <linux/list.h>

#include <linux/netfilter/nf_conntrack_tcp.h>
#include <linux/netfilter/nf_conntrack_dccp.h>
@@ -26,6 +27,14 @@

#include <net/netfilter/nf_conntrack_tuple.h>

#define SIP_LIST_ELEMENTS	2

struct sip_length {
	int msg_length[SIP_LIST_ELEMENTS];
	int skb_len[SIP_LIST_ELEMENTS];
	int data_len[SIP_LIST_ELEMENTS];
};

/* per conntrack: protocol private data */
union nf_conntrack_proto {
	/* insert conntrack proto private data here */
@@ -121,6 +130,12 @@ struct nf_conn {
	unsigned long nattype_entry;
#endif

	struct list_head sip_segment_list;
	const char *dptr_prev;
	struct sip_length segment;
	bool sip_original_dir;
	bool sip_reply_dir;

	/* Storage reserved for other modules, must be the last member */
	union nf_conntrack_proto proto;
};
+5 −0
Original line number Diff line number Diff line
@@ -86,4 +86,9 @@ extern spinlock_t nf_conntrack_locks[CONNTRACK_LOCKS];

extern spinlock_t nf_conntrack_expect_lock;

struct sip_list {
	struct nf_queue_entry *entry;
	struct list_head list;
};

#endif /* _NF_CONNTRACK_CORE_H */
+44 −32
Original line number Diff line number Diff line
@@ -58,13 +58,13 @@
int (*nfnetlink_parse_nat_setup_hook)(struct nf_conn *ct,
				      enum nf_nat_manip_type manip,
				      const struct nlattr *attr) __read_mostly;
EXPORT_SYMBOL_GPL(nfnetlink_parse_nat_setup_hook);
EXPORT_SYMBOL(nfnetlink_parse_nat_setup_hook);

__cacheline_aligned_in_smp spinlock_t nf_conntrack_locks[CONNTRACK_LOCKS];
EXPORT_SYMBOL_GPL(nf_conntrack_locks);
EXPORT_SYMBOL(nf_conntrack_locks);

__cacheline_aligned_in_smp DEFINE_SPINLOCK(nf_conntrack_expect_lock);
EXPORT_SYMBOL_GPL(nf_conntrack_expect_lock);
EXPORT_SYMBOL(nf_conntrack_expect_lock);

static void nf_conntrack_double_unlock(unsigned int h1, unsigned int h2)
{
@@ -115,16 +115,16 @@ static void nf_conntrack_all_unlock(void)
}

unsigned int nf_conntrack_htable_size __read_mostly;
EXPORT_SYMBOL_GPL(nf_conntrack_htable_size);
EXPORT_SYMBOL(nf_conntrack_htable_size);

unsigned int nf_conntrack_max __read_mostly;
EXPORT_SYMBOL_GPL(nf_conntrack_max);
EXPORT_SYMBOL(nf_conntrack_max);

DEFINE_PER_CPU(struct nf_conn, nf_conntrack_untracked);
EXPORT_PER_CPU_SYMBOL(nf_conntrack_untracked);

unsigned int nf_conntrack_hash_rnd __read_mostly;
EXPORT_SYMBOL_GPL(nf_conntrack_hash_rnd);
EXPORT_SYMBOL(nf_conntrack_hash_rnd);

static u32 hash_conntrack_raw(const struct nf_conntrack_tuple *tuple, u16 zone)
{
@@ -183,7 +183,7 @@ nf_ct_get_tuple(const struct sk_buff *skb,

	return l4proto->pkt_to_tuple(skb, dataoff, tuple);
}
EXPORT_SYMBOL_GPL(nf_ct_get_tuple);
EXPORT_SYMBOL(nf_ct_get_tuple);

bool nf_ct_get_tuplepr(const struct sk_buff *skb, unsigned int nhoff,
		       u_int16_t l3num, struct nf_conntrack_tuple *tuple)
@@ -211,7 +211,7 @@ bool nf_ct_get_tuplepr(const struct sk_buff *skb, unsigned int nhoff,
	rcu_read_unlock();
	return ret;
}
EXPORT_SYMBOL_GPL(nf_ct_get_tuplepr);
EXPORT_SYMBOL(nf_ct_get_tuplepr);

bool
nf_ct_invert_tuple(struct nf_conntrack_tuple *inverse,
@@ -230,7 +230,7 @@ nf_ct_invert_tuple(struct nf_conntrack_tuple *inverse,
	inverse->dst.protonum = orig->dst.protonum;
	return l4proto->invert_tuple(inverse, orig);
}
EXPORT_SYMBOL_GPL(nf_ct_invert_tuple);
EXPORT_SYMBOL(nf_ct_invert_tuple);

static void
clean_from_lists(struct nf_conn *ct)
@@ -293,6 +293,9 @@ destroy_conntrack(struct nf_conntrack *nfct)
	struct nf_conn *ct = (struct nf_conn *)nfct;
	struct net *net = nf_ct_net(ct);
	struct nf_conntrack_l4proto *l4proto;
	struct sip_list *sip_node = NULL;
	struct list_head *sip_node_list;
	struct list_head *sip_node_save_list;

	pr_debug("destroy_conntrack(%p)\n", ct);
	NF_CT_ASSERT(atomic_read(&nfct->use) == 0);
@@ -306,6 +309,14 @@ destroy_conntrack(struct nf_conntrack *nfct)
	rcu_read_unlock();

	local_bh_disable();

	pr_debug("freeing item in the SIP list\n");
	list_for_each_safe(sip_node_list, sip_node_save_list,
			   &ct->sip_segment_list) {
		sip_node = list_entry(sip_node_list, struct sip_list, list);
		list_del(&sip_node->list);
		kfree(sip_node);
	}
	/* Expectations will have been removed in clean_from_lists,
	 * except TFTP can create an expectation on the first packet,
	 * before connection is in the list, so we need to clean here,
@@ -378,7 +389,7 @@ bool nf_ct_delete(struct nf_conn *ct, u32 portid, int report)
	nf_ct_put(ct);
	return true;
}
EXPORT_SYMBOL_GPL(nf_ct_delete);
EXPORT_SYMBOL(nf_ct_delete);

static void death_by_timeout(unsigned long ul_conntrack)
{
@@ -475,7 +486,7 @@ nf_conntrack_find_get(struct net *net, u16 zone,
	return __nf_conntrack_find_get(net, zone, tuple,
				       hash_conntrack_raw(tuple, zone));
}
EXPORT_SYMBOL_GPL(nf_conntrack_find_get);
EXPORT_SYMBOL(nf_conntrack_find_get);

static void __nf_conntrack_hash_insert(struct nf_conn *ct,
				       unsigned int hash,
@@ -538,7 +549,7 @@ out:
	local_bh_enable();
	return -EEXIST;
}
EXPORT_SYMBOL_GPL(nf_conntrack_hash_check_insert);
EXPORT_SYMBOL(nf_conntrack_hash_check_insert);

/* deletion from this larval template list happens via nf_ct_put() */
void nf_conntrack_tmpl_insert(struct net *net, struct nf_conn *tmpl)
@@ -560,7 +571,7 @@ void nf_conntrack_tmpl_insert(struct net *net, struct nf_conn *tmpl)
				 &pcpu->tmpl);
	spin_unlock_bh(&pcpu->lock);
}
EXPORT_SYMBOL_GPL(nf_conntrack_tmpl_insert);
EXPORT_SYMBOL(nf_conntrack_tmpl_insert);

/* Confirm a connection given skb; places it in hash table */
int
@@ -676,7 +687,7 @@ out:
	local_bh_enable();
	return NF_DROP;
}
EXPORT_SYMBOL_GPL(__nf_conntrack_confirm);
EXPORT_SYMBOL(__nf_conntrack_confirm);

/* Returns true if a connection correspondings to the tuple (required
   for NAT). */
@@ -710,7 +721,7 @@ nf_conntrack_tuple_taken(const struct nf_conntrack_tuple *tuple,

	return 0;
}
EXPORT_SYMBOL_GPL(nf_conntrack_tuple_taken);
EXPORT_SYMBOL(nf_conntrack_tuple_taken);

#define NF_CT_EVICTION_RANGE	8

@@ -872,7 +883,7 @@ struct nf_conn *nf_conntrack_alloc(struct net *net, u16 zone,
{
	return __nf_conntrack_alloc(net, zone, orig, repl, gfp, 0);
}
EXPORT_SYMBOL_GPL(nf_conntrack_alloc);
EXPORT_SYMBOL(nf_conntrack_alloc);

void nf_conntrack_free(struct nf_conn *ct)
{
@@ -889,7 +900,7 @@ void nf_conntrack_free(struct nf_conn *ct)
	smp_mb__before_atomic();
	atomic_dec(&net->ct.count);
}
EXPORT_SYMBOL_GPL(nf_conntrack_free);
EXPORT_SYMBOL(nf_conntrack_free);


/* Allocate a new conntrack: we return -ENOMEM if classification
@@ -951,6 +962,7 @@ init_conntrack(struct net *net, struct nf_conn *tmpl,
			     GFP_ATOMIC);

	local_bh_disable();
	INIT_LIST_HEAD(&ct->sip_segment_list);
	if (net->ct.expect_count) {
		spin_lock(&nf_conntrack_expect_lock);
		exp = nf_ct_find_expectation(net, zone, tuple);
@@ -1170,7 +1182,7 @@ out:

	return ret;
}
EXPORT_SYMBOL_GPL(nf_conntrack_in);
EXPORT_SYMBOL(nf_conntrack_in);

bool nf_ct_invert_tuplepr(struct nf_conntrack_tuple *inverse,
			  const struct nf_conntrack_tuple *orig)
@@ -1185,7 +1197,7 @@ bool nf_ct_invert_tuplepr(struct nf_conntrack_tuple *inverse,
	rcu_read_unlock();
	return ret;
}
EXPORT_SYMBOL_GPL(nf_ct_invert_tuplepr);
EXPORT_SYMBOL(nf_ct_invert_tuplepr);

/* Alter reply tuple (maybe alter helper).  This is for NAT, and is
   implicitly racy: see __nf_conntrack_confirm */
@@ -1208,7 +1220,7 @@ void nf_conntrack_alter_reply(struct nf_conn *ct,
	__nf_ct_try_assign_helper(ct, NULL, GFP_ATOMIC);
	rcu_read_unlock();
}
EXPORT_SYMBOL_GPL(nf_conntrack_alter_reply);
EXPORT_SYMBOL(nf_conntrack_alter_reply);

/* Refresh conntrack for this many jiffies and do accounting if do_acct is 1 */
void __nf_ct_refresh_acct(struct nf_conn *ct,
@@ -1255,7 +1267,7 @@ acct:
		}
	}
}
EXPORT_SYMBOL_GPL(__nf_ct_refresh_acct);
EXPORT_SYMBOL(__nf_ct_refresh_acct);

bool __nf_ct_kill_acct(struct nf_conn *ct,
		       enum ip_conntrack_info ctinfo,
@@ -1281,7 +1293,7 @@ bool __nf_ct_kill_acct(struct nf_conn *ct,
	}
	return false;
}
EXPORT_SYMBOL_GPL(__nf_ct_kill_acct);
EXPORT_SYMBOL(__nf_ct_kill_acct);

#ifdef CONFIG_NF_CONNTRACK_ZONES
static struct nf_ct_ext_type nf_ct_zone_extend __read_mostly = {
@@ -1311,13 +1323,13 @@ int nf_ct_port_tuple_to_nlattr(struct sk_buff *skb,
nla_put_failure:
	return -1;
}
EXPORT_SYMBOL_GPL(nf_ct_port_tuple_to_nlattr);
EXPORT_SYMBOL(nf_ct_port_tuple_to_nlattr);

const struct nla_policy nf_ct_port_nla_policy[CTA_PROTO_MAX+1] = {
	[CTA_PROTO_SRC_PORT]  = { .type = NLA_U16 },
	[CTA_PROTO_DST_PORT]  = { .type = NLA_U16 },
};
EXPORT_SYMBOL_GPL(nf_ct_port_nla_policy);
EXPORT_SYMBOL(nf_ct_port_nla_policy);

int nf_ct_port_nlattr_to_tuple(struct nlattr *tb[],
			       struct nf_conntrack_tuple *t)
@@ -1330,13 +1342,13 @@ int nf_ct_port_nlattr_to_tuple(struct nlattr *tb[],

	return 0;
}
EXPORT_SYMBOL_GPL(nf_ct_port_nlattr_to_tuple);
EXPORT_SYMBOL(nf_ct_port_nlattr_to_tuple);

int nf_ct_port_nlattr_tuple_size(void)
{
	return nla_policy_len(nf_ct_port_nla_policy, CTA_PROTO_MAX + 1);
}
EXPORT_SYMBOL_GPL(nf_ct_port_nlattr_tuple_size);
EXPORT_SYMBOL(nf_ct_port_nlattr_tuple_size);
#endif

/* Used by ipt_REJECT and ip6t_REJECT. */
@@ -1422,7 +1434,7 @@ void nf_ct_iterate_cleanup(struct net *net,
		nf_ct_put(ct);
	}
}
EXPORT_SYMBOL_GPL(nf_ct_iterate_cleanup);
EXPORT_SYMBOL(nf_ct_iterate_cleanup);

static int kill_all(struct nf_conn *i, void *data)
{
@@ -1437,13 +1449,13 @@ void nf_ct_free_hashtable(void *hash, unsigned int size)
		free_pages((unsigned long)hash,
			   get_order(sizeof(struct hlist_head) * size));
}
EXPORT_SYMBOL_GPL(nf_ct_free_hashtable);
EXPORT_SYMBOL(nf_ct_free_hashtable);

void nf_conntrack_flush_report(struct net *net, u32 portid, int report)
{
	nf_ct_iterate_cleanup(net, kill_all, NULL, portid, report);
}
EXPORT_SYMBOL_GPL(nf_conntrack_flush_report);
EXPORT_SYMBOL(nf_conntrack_flush_report);

static int untrack_refs(void)
{
@@ -1554,7 +1566,7 @@ void *nf_ct_alloc_hashtable(unsigned int *sizep, int nulls)

	return hash;
}
EXPORT_SYMBOL_GPL(nf_ct_alloc_hashtable);
EXPORT_SYMBOL(nf_ct_alloc_hashtable);

int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp)
{
@@ -1615,7 +1627,7 @@ int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp)
	nf_ct_free_hashtable(old_hash, old_size);
	return 0;
}
EXPORT_SYMBOL_GPL(nf_conntrack_set_hashsize);
EXPORT_SYMBOL(nf_conntrack_set_hashsize);

module_param_call(hashsize, nf_conntrack_set_hashsize, param_get_uint,
		  &nf_conntrack_htable_size, 0600);
@@ -1627,7 +1639,7 @@ void nf_ct_untracked_status_or(unsigned long bits)
	for_each_possible_cpu(cpu)
		per_cpu(nf_conntrack_untracked, cpu).status |= bits;
}
EXPORT_SYMBOL_GPL(nf_ct_untracked_status_or);
EXPORT_SYMBOL(nf_ct_untracked_status_or);

int nf_conntrack_init_start(void)
{
+383 −23
Original line number Diff line number Diff line
/* SIP extension for IP connection tracking.
 *
 * Copyright (c) 2015, The Linux Foundation. All rights reserved.
 * (C) 2005 by Christian Hentschel <chentschel@arnet.com.ar>
 * based on RR's ip_conntrack_ftp.c and other modules.
 * (C) 2007 United Security Providers
@@ -18,13 +19,18 @@
#include <linux/udp.h>
#include <linux/tcp.h>
#include <linux/netfilter.h>

#include <net/tcp.h>
#include <net/netfilter/nf_conntrack.h>
#include <net/netfilter/nf_conntrack_core.h>
#include <net/netfilter/nf_conntrack_expect.h>
#include <net/netfilter/nf_conntrack_helper.h>
#include <net/netfilter/nf_conntrack_zones.h>
#include <linux/netfilter/nf_conntrack_sip.h>
#include <net/netfilter/nf_nat.h>
#include <net/netfilter/nf_nat_l3proto.h>
#include <net/netfilter/nf_nat_l4proto.h>
#include <net/netfilter/nf_queue.h>


MODULE_LICENSE("GPL");
MODULE_AUTHOR("Christian Hentschel <chentschel@arnet.com.ar>");
@@ -48,10 +54,11 @@ MODULE_PARM_DESC(sip_direct_signalling, "expect incoming calls from registrar "
					"only (default 1)");

const struct nf_nat_sip_hooks *nf_nat_sip_hooks;
EXPORT_SYMBOL_GPL(nf_nat_sip_hooks);
EXPORT_SYMBOL(nf_nat_sip_hooks);
static struct ctl_table_header *sip_sysctl_header;
static unsigned nf_ct_disable_sip_alg;
static int sip_direct_media = 1;
static int packet_count;
static struct ctl_table sip_sysctl_tbl[] = {
	{
		.procname     = "nf_conntrack_disable_sip_alg",
@@ -70,6 +77,224 @@ static struct ctl_table sip_sysctl_tbl[] = {
	{}
};

unsigned int (*nf_nat_sip_hook)(struct sk_buff *skb, unsigned int protoff,
				unsigned int dataoff, const char **dptr,
				unsigned int *datalen) __read_mostly;

/* This function is to save all the information of the first segment
 * that will be needed for combining the two segments
 */
static bool sip_save_segment_info(struct nf_conn *ct, struct sk_buff *skb,
				  unsigned int msglen, unsigned int datalen,
				  const char *dptr,
				  enum ip_conntrack_info ctinfo)
{
	enum ip_conntrack_dir dir = IP_CT_DIR_MAX;
	bool skip = false;

	/* one set of information is saved per direction ,also only one segment
	 * per direction is queued based on the assumption that after the first
	 * complete message leaves the kernel, only then the next fragmented
	 * segment will reach the kernel
	 */
	if (ct)
		dir = CTINFO2DIR(ctinfo);

	if (dir == IP_CT_DIR_ORIGINAL) {
		/* here we check if there is already an element queued for this
		 * direction, in that case we do not queue the next element,we
		 * make skip 1.ideally this scenario should never be hit
		 */
		if (ct->sip_original_dir == 1) {
			skip = true;
		} else {
			ct->segment.msg_length[0] = msglen;
			ct->segment.data_len[0] = datalen;
			ct->segment.skb_len[0] = skb->len;
			ct->dptr_prev = dptr;
			ct->sip_original_dir = 1;
			skip = false;
		}
	} else {
		if (ct->sip_reply_dir == 1) {
			skip = true;
		} else {
			ct->segment.msg_length[1] = msglen;
			ct->segment.data_len[1] = datalen;
			ct->segment.skb_len[1] = skb->len;
			ct->dptr_prev = dptr;
			ct->sip_reply_dir = 1;
			skip = false;
		}
	}
	return skip;
}

static struct sip_list *sip_coalesce_segments(struct nf_conn *ct,
					      struct sk_buff **skb_ref,
					      unsigned int dataoff,
					      struct sk_buff **combined_skb_ref,
					      bool *skip_sip_process,
					      bool do_not_process,
					      enum ip_conntrack_info ctinfo,
					      bool *success)

{
	struct list_head *list_trav_node;
	struct list_head *list_backup_node;
	struct nf_conn *ct_list;
	enum ip_conntrack_info ctinfo_list;
	enum ip_conntrack_dir dir_list;
	enum ip_conntrack_dir dir = IP_CT_DIR_MAX;
	const struct tcphdr *th_old;
	unsigned int prev_data_len;
	unsigned int seq_no, seq_old, exp_seq_no;
	const struct tcphdr *th_new;
	bool fragstolen = false;
	int delta_truesize = 0;
	struct sip_list *sip_entry = NULL;

	th_new = (struct tcphdr *)(skb_network_header(*skb_ref) +
		ip_hdrlen(*skb_ref));
	seq_no = ntohl(th_new->seq);

	if (ct)
		dir = CTINFO2DIR(ctinfo);
	/* traverse the list it would have 1 or 2 elements. 1 element per
	 * direction at max
	 */
	list_for_each_safe(list_trav_node, list_backup_node,
			   &ct->sip_segment_list){
		sip_entry = list_entry(list_trav_node, struct sip_list, list);
		ct_list = nf_ct_get(sip_entry->entry->skb, &ctinfo_list);
		dir_list = CTINFO2DIR(ctinfo_list);
		/* take an element and check if its direction matches with the
		 * current one
		 */
		if (dir_list == dir) {
			/* once we have the two elements to be combined we do
			 * another check. match the next expected seq no of the
			 * packet in the list with the seq no of the current
			 * packet.this is to be protected  against out of order
			 * fragments
			 */
			th_old = ((struct tcphdr *)(skb_network_header
				(sip_entry->entry->skb) +
				ip_hdrlen(sip_entry->entry->skb)));

			prev_data_len = (dir == IP_CT_DIR_ORIGINAL) ?
			 ct->segment.data_len[0] : ct->segment.data_len[1];
			seq_old = (ntohl(th_old->seq));
			exp_seq_no = seq_old+prev_data_len;

			if (exp_seq_no == seq_no) {
				/* Found packets to be combined.Pull header from
				 * second skb when preparing combined skb.This
				 * shifts the second skb start pointer to its
				 * data that was initially at the start of its
				 * headers.This so that the  combined skb has
				 * the tcp ip headerof the first skb followed
				 * by the data of first skb followed by the data
				 * of second skb.
				 */
				skb_pull(*skb_ref, dataoff);
				if (skb_try_coalesce(sip_entry->entry->skb,
						     *skb_ref, &fragstolen,
							&delta_truesize)) {
					*combined_skb_ref =
							  sip_entry->entry->skb;
					*success = true;
					list_del(list_trav_node);
					} else{
					skb_push(*skb_ref, dataoff);
					}
			}
		} else if (do_not_process) {
			*skip_sip_process = true;
		}
	}
	return sip_entry;
}

static void recalc_header(struct sk_buff *skb, unsigned int skblen,
			  unsigned int oldlen, unsigned int protoff)
{
	unsigned int datalen;
	struct tcphdr *tcph;
	const struct nf_nat_l3proto *l3proto;

	/* here we recalculate ip and tcp headers */
	if (nf_ct_l3num((struct nf_conn *)skb->nfct) == NFPROTO_IPV4) {
			/* fix IP hdr checksum information */
			ip_hdr(skb)->tot_len = htons(skblen);
			ip_send_check(ip_hdr(skb));
	} else {
		ipv6_hdr(skb)->payload_len =
				htons(skblen - sizeof(struct ipv6hdr));
	}
	datalen = skb->len - protoff;
	tcph = (struct tcphdr *)((void *)skb->data + protoff);
	l3proto = __nf_nat_l3proto_find(nf_ct_l3num
					((struct nf_conn *)skb->nfct));
	l3proto->csum_recalc(skb, IPPROTO_TCP, tcph, &tcph->check,
			     datalen, oldlen);
}
EXPORT_SYMBOL(nf_nat_sip_hook);
void (*nf_nat_sip_seq_adjust_hook)(struct sk_buff *skb, unsigned int protoff,
				   s16 off) __read_mostly;
EXPORT_SYMBOL(nf_nat_sip_seq_adjust_hook);
unsigned int (*nf_nat_sip_expect_hook)(struct sk_buff *skb,
				       unsigned int protoff,
				       unsigned int dataoff,
				       const char **dptr,
				       unsigned int *datalen,
				       struct nf_conntrack_expect *exp,
				       unsigned int matchoff,
				       unsigned int matchlen) __read_mostly;
EXPORT_SYMBOL(nf_nat_sip_expect_hook);

unsigned int (*nf_nat_sdp_addr_hook)(struct sk_buff *skb, unsigned int protoff,
				     unsigned int dataoff,
				     const char **dptr,
				     unsigned int *datalen,
				     unsigned int sdpoff,
				     enum sdp_header_types type,
				     enum sdp_header_types term,
				     const union nf_inet_addr *addr)
				     __read_mostly;
EXPORT_SYMBOL(nf_nat_sdp_addr_hook);

unsigned int (*nf_nat_sdp_port_hook)(struct sk_buff *skb, unsigned int protoff,
				     unsigned int dataoff,
				     const char **dptr,
				     unsigned int *datalen,
				     unsigned int matchoff,
				     unsigned int matchlen,
				     u_int16_t port) __read_mostly;
EXPORT_SYMBOL(nf_nat_sdp_port_hook);

unsigned int (*nf_nat_sdp_session_hook)(struct sk_buff *skb,
					unsigned int protoff,
					unsigned int dataoff,
					const char **dptr,
					unsigned int *datalen,
					unsigned int sdpoff,
					const union nf_inet_addr *addr)
					__read_mostly;
EXPORT_SYMBOL(nf_nat_sdp_session_hook);

unsigned int (*nf_nat_sdp_media_hook)(struct sk_buff *skb, unsigned int protoff,
				      unsigned int dataoff,
				      const char **dptr,
				      unsigned int *datalen,
				      struct nf_conntrack_expect *rtp_exp,
				      struct nf_conntrack_expect *rtcp_exp,
				      unsigned int mediaoff,
				      unsigned int medialen,
				      union nf_inet_addr *rtp_addr)
				      __read_mostly;
EXPORT_SYMBOL(nf_nat_sdp_media_hook);

static int string_len(const struct nf_conn *ct, const char *dptr,
		      const char *limit, int *shift)
{
@@ -82,6 +307,26 @@ static int string_len(const struct nf_conn *ct, const char *dptr,
	return len;
}

static int nf_sip_enqueue_packet(struct nf_queue_entry *entry,
				 unsigned int queuenum)
{
	enum ip_conntrack_info ctinfo_list;
	struct nf_conn *ct_temp;
	struct sip_list *node = kzalloc(sizeof(*node),
			GFP_ATOMIC | __GFP_NOWARN);
	if (!node)
		return XT_CONTINUE;

	ct_temp = nf_ct_get(entry->skb, &ctinfo_list);
	node->entry = entry;
	list_add(&node->list, &ct_temp->sip_segment_list);
	return 0;
}

static const struct nf_queue_handler nf_sip_qh = {
	.outfn	= &nf_sip_enqueue_packet,
};

static int digits_len(const struct nf_conn *ct, const char *dptr,
		      const char *limit, int *shift)
{
@@ -288,7 +533,7 @@ int ct_sip_parse_request(const struct nf_conn *ct,
	*matchlen = end - dptr;
	return 1;
}
EXPORT_SYMBOL_GPL(ct_sip_parse_request);
EXPORT_SYMBOL(ct_sip_parse_request);

/* SIP header parsing: SIP headers are located at the beginning of a line, but
 * may span several lines, in which case the continuation lines begin with a
@@ -436,7 +681,7 @@ int ct_sip_get_header(const struct nf_conn *ct, const char *dptr,
	}
	return 0;
}
EXPORT_SYMBOL_GPL(ct_sip_get_header);
EXPORT_SYMBOL(ct_sip_get_header);

/* Get next header field in a list of comma separated values */
static int ct_sip_next_header(const struct nf_conn *ct, const char *dptr,
@@ -540,7 +785,7 @@ int ct_sip_parse_header_uri(const struct nf_conn *ct, const char *dptr,
		*dataoff = c - dptr;
	return 1;
}
EXPORT_SYMBOL_GPL(ct_sip_parse_header_uri);
EXPORT_SYMBOL(ct_sip_parse_header_uri);

static int ct_sip_parse_param(const struct nf_conn *ct, const char *dptr,
			      unsigned int dataoff, unsigned int datalen,
@@ -594,7 +839,7 @@ int ct_sip_parse_address_param(const struct nf_conn *ct, const char *dptr,
	*matchlen = end - start;
	return 1;
}
EXPORT_SYMBOL_GPL(ct_sip_parse_address_param);
EXPORT_SYMBOL(ct_sip_parse_address_param);

/* Parse numerical header parameter and return value, offset and length */
int ct_sip_parse_numerical_param(const struct nf_conn *ct, const char *dptr,
@@ -625,7 +870,7 @@ int ct_sip_parse_numerical_param(const struct nf_conn *ct, const char *dptr,
	}
	return 1;
}
EXPORT_SYMBOL_GPL(ct_sip_parse_numerical_param);
EXPORT_SYMBOL(ct_sip_parse_numerical_param);

static int ct_sip_parse_transport(struct nf_conn *ct, const char *dptr,
				  unsigned int dataoff, unsigned int datalen,
@@ -783,7 +1028,7 @@ int ct_sip_get_sdp_header(const struct nf_conn *ct, const char *dptr,
	}
	return 0;
}
EXPORT_SYMBOL_GPL(ct_sip_get_sdp_header);
EXPORT_SYMBOL(ct_sip_get_sdp_header);

static int ct_sip_parse_sdp_addr(const struct nf_conn *ct, const char *dptr,
				 unsigned int dataoff, unsigned int datalen,
@@ -1500,13 +1745,28 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff,
			struct nf_conn *ct, enum ip_conntrack_info ctinfo)
{
	struct tcphdr *th, _tcph;
	unsigned int dataoff, datalen;
	unsigned int dataoff;
	unsigned int matchoff, matchlen, clen;
	unsigned int msglen, origlen;
	const char *dptr, *end;
	s16 diff, tdiff = 0;
	int ret = NF_ACCEPT;
	bool term;
	unsigned int datalen = 0, msglen = 0, origlen = 0;
	unsigned int dataoff_orig = 0;
	unsigned int splitlen, oldlen, oldlen1;
	struct sip_list *sip_entry = NULL;
	bool skip_sip_process = false;
	bool do_not_process = false;
	bool skip = false;
	bool skb_is_combined = false;
	enum ip_conntrack_dir dir = IP_CT_DIR_MAX;
	struct sk_buff *combined_skb = NULL;

	packet_count++;
	pr_debug("packet count %d\n", packet_count);

	if (nf_ct_disable_sip_alg)
		return NF_ACCEPT;

	if (ctinfo != IP_CT_ESTABLISHED &&
	    ctinfo != IP_CT_ESTABLISHED_REPLY)
@@ -1530,15 +1790,25 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff,
	if (datalen < strlen("SIP/2.0 200"))
		return NF_ACCEPT;

	/* here we save the original datalength and data offset of the skb, this
	 * is needed later to split combined skbs
	 */
	oldlen1 = skb->len - protoff;
	dataoff_orig = dataoff;

	while (1) {
		if (ct_sip_get_header(ct, dptr, 0, datalen,
				      SIP_HDR_CONTENT_LENGTH,
				      &matchoff, &matchlen) <= 0)
			break;
				      &matchoff, &matchlen) <= 0){
			do_not_process = true;
			goto destination;
		}

		clen = simple_strtoul(dptr + matchoff, (char **)&end, 10);
		if (dptr + matchoff == end)
			break;
		if (dptr + matchoff == end) {
			do_not_process = true;
			goto destination;
		}

		term = false;
		for (; end + strlen("\r\n\r\n") <= dptr + datalen; end++) {
@@ -1548,27 +1818,114 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff,
				break;
			}
		}
		if (!term)
			break;
		end += strlen("\r\n\r\n") + clen;
		if (!term) {
			do_not_process = true;
			goto destination;
		}

		end += strlen("\r\n\r\n") + clen;
		msglen = origlen = end - dptr;
		if (msglen > datalen)
			return NF_ACCEPT;

		ret = process_sip_msg(skb, ct, protoff, dataoff,
destination:

		if (ct)
			dir = CTINFO2DIR(ctinfo);
		combined_skb = skb;

		/* Segmented Packet */
		if (msglen > datalen) {
			skip = sip_save_segment_info(ct, skb, msglen, datalen,
						     dptr, ctinfo);
			if (!skip)
				return NF_QUEUE;
		}
		/* Traverse list to find prev segment */
		/*Traverse the list if list non empty */
		if (((&ct->sip_segment_list)->next) !=
			(&ct->sip_segment_list)) {
			/* Combine segments if they are fragments of the same
			 * message.
			 */
			sip_entry = sip_coalesce_segments(ct,
							  &skb, dataoff,
							  &combined_skb,
					&skip_sip_process, do_not_process,
					ctinfo, &skb_is_combined);

		if (skb_is_combined) {
			/* The msglen of first skb has the total msg length of
			 * the two fragments. hence after combining,we update
			 * the msglen to that of the msglen of first skb
			 */
			msglen = (dir == IP_CT_DIR_ORIGINAL) ?
			ct->segment.msg_length[0] : ct->segment.msg_length[1];
			origlen = msglen;
			dptr = ct->dptr_prev;
			datalen = msglen;
		}
		if (skip_sip_process)
			goto here;
		} else if (do_not_process) {
			goto here;
		}
		/* process the combined skb having the complete SIP message */
		ret = process_sip_msg(combined_skb, ct, protoff, dataoff,
				      &dptr, &msglen);

		/* process_sip_* functions report why this packet is dropped */
		if (ret != NF_ACCEPT)
			break;

		diff	 = msglen - origlen;
		tdiff	+= diff;

		dataoff += msglen;
		dptr	+= msglen;
		datalen  = datalen + diff - msglen;

		break;
	}

	if (skb_is_combined) {
		/* once combined skb is processed, split the skbs again The
		 * length to split at is the same as length of first skb. Any
		 * changes in the combined skb length because of SIP processing
		 * will reflect in the second fragment
		 */
		splitlen = (dir == IP_CT_DIR_ORIGINAL) ?
				ct->segment.skb_len[0] : ct->segment.skb_len[1];
		oldlen = combined_skb->len - protoff;
		skb_split(combined_skb, skb, splitlen);
		/* Headers need to be recalculated since during SIP processing
		 * headers are calculated based on the change in length of the
		 * combined message
		 */
		recalc_header(combined_skb, splitlen, oldlen, protoff);
		/* Reinject the first skb now that the processing is complete */
		nf_reinject(sip_entry->entry, NF_ACCEPT);
		kfree(sip_entry);
		skb->len = (oldlen1 + protoff) + tdiff - dataoff_orig;
		/* After splitting, push the headers back to the first skb which
		 * were removed before combining the skbs.This moves the skb
		 * begin pointer back to the beginning of its headers
		 */
		skb_push(skb, dataoff_orig);
		/* Since the length of this second segment willbe affected
		 * because of SIP processing,we need to recalculate its header
		 * as well.
		 */
		recalc_header(skb, skb->len, oldlen1, protoff);
		/* Now that the processing is done and the first skb reinjected.
		 * We allow addition of fragmented skbs to the list for this
		 * direction
		 */
		if (dir == IP_CT_DIR_ORIGINAL)
			ct->sip_original_dir = 0;
		else
			ct->sip_reply_dir = 0;
	}

here:

	if (ret == NF_ACCEPT && ct->status & IPS_NAT_MASK) {
		const struct nf_nat_sip_hooks *hooks;

@@ -1700,6 +2057,9 @@ static int __init nf_conntrack_sip_init(void)
			}
		}
	}

	nf_register_queue_handler(&nf_sip_qh);

	return 0;
}