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

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

netfilter: Changes to handle segmentation in SIP ALG



Linux Kernel SIP ALG did not handle Segmented TCP Packets
because of which SIP communication could not be established
for some clients. This change fixes that issue.

Also,This change handle porting of below changes
1)Additional fixes for SIP Segmentation Support
I49fa88eebadb24c7ce03c4d189c71da0fe034e1d
2)Changes to handle MT call issue in SIP ALG
Ia4c83fb18c0e92e8402742f50c0ef05a344e51a4
3)Enable/Disable SIP Segmentation Support
I0e2fdc7dfa43f5d1cc00daa5c3103231c979ac43

Change-Id: I8c77322f69cf4d9ad4c7b4971da924ffd585dea0
Acked-by: default avatarSuraj Jaiswal <c_surajj@qti.qualcomm.com>
Signed-off-by: default avatarRavinder Konka <rkonka@codeaurora.org>
Signed-off-by: default avatarTyler Wear <twear@codeaurora.org>
Signed-off-by: default avatarMohammed Javid <mjavid@codeaurora.org>
parent a8118139
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -166,6 +166,11 @@ struct nf_nat_sip_hooks {
};
extern const struct nf_nat_sip_hooks *nf_nat_sip_hooks;

extern void (*nf_nat_sip_seq_adjust_hook)
			(struct sk_buff *skb,
			unsigned int protoff,
			s16 off);

int ct_sip_parse_request(const struct nf_conn *ct, const char *dptr,
			 unsigned int datalen, unsigned int *matchoff,
			 unsigned int *matchlen, union nf_inet_addr *addr,
+14 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@
#include <linux/compiler.h>
#include <linux/atomic.h>
#include <linux/rhashtable.h>
#include <linux/list.h>

#include <linux/netfilter/nf_conntrack_tcp.h>
#include <linux/netfilter/nf_conntrack_dccp.h>
@@ -27,6 +28,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 */
@@ -130,6 +139,11 @@ struct nf_conn {
#ifdef CONFIG_IP_NF_TARGET_NATTYPE_MODULE
	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;
+8 −0
Original line number Diff line number Diff line
@@ -20,6 +20,9 @@
/* This header is used to share core functionality between the
   standalone connection tracking module, and the compatibility layer's use
   of connection tracking. */

extern unsigned int nf_conntrack_hash_rnd;

unsigned int nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum,
			     struct sk_buff *skb);

@@ -88,4 +91,9 @@ void nf_conntrack_lock(spinlock_t *lock);

extern spinlock_t nf_conntrack_expect_lock;

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

#endif /* _NF_CONNTRACK_CORE_H */
+15 −1
Original line number Diff line number Diff line
@@ -188,6 +188,7 @@ unsigned int nf_conntrack_htable_size __read_mostly;
EXPORT_SYMBOL_GPL(nf_conntrack_htable_size);

unsigned int nf_conntrack_max __read_mostly;

seqcount_t nf_conntrack_generation __read_mostly;

unsigned int nf_conntrack_pkt_threshold __read_mostly;
@@ -196,7 +197,8 @@ EXPORT_SYMBOL(nf_conntrack_pkt_threshold);
DEFINE_PER_CPU(struct nf_conn, nf_conntrack_untracked);
EXPORT_PER_CPU_SYMBOL(nf_conntrack_untracked);

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

static u32 hash_conntrack_raw(const struct nf_conntrack_tuple *tuple,
			      const struct net *net)
@@ -399,6 +401,9 @@ destroy_conntrack(struct nf_conntrack *nfct)
	struct nf_conn *ct = (struct nf_conn *)nfct;
	struct nf_conntrack_l4proto *l4proto;
	void (*delete_entry)(struct nf_conn *ct);
	struct sip_list *sip_node = NULL;
	struct list_head *sip_node_list;
	struct list_head *sip_node_save_list;

	pr_debug("destroy_conntrack(%pK)\n", ct);
	NF_CT_ASSERT(atomic_read(&nfct->use) == 0);
@@ -426,6 +431,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,
@@ -1200,6 +1213,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);
+461 −16
Original line number Diff line number Diff line
/* SIP extension for IP connection tracking.
 *
 * Copyright (c) 2015,2017, 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
@@ -20,13 +21,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>");
@@ -54,6 +60,12 @@ EXPORT_SYMBOL_GPL(nf_nat_sip_hooks);
static struct ctl_table_header *sip_sysctl_header;
static unsigned int nf_ct_disable_sip_alg;
static int sip_direct_media = 1;
static unsigned int nf_ct_enable_sip_segmentation;
static int packet_count;
static
int proc_sip_segment(struct ctl_table *ctl, int write,
		     void __user *buffer, size_t *lenp, loff_t *ppos);

static struct ctl_table sip_sysctl_tbl[] = {
	{
		.procname     = "nf_conntrack_disable_sip_alg",
@@ -69,9 +81,289 @@ static struct ctl_table sip_sysctl_tbl[] = {
		.mode         = 0644,
		.proc_handler = proc_dointvec,
	},
	{
		.procname     = "nf_conntrack_enable_sip_segmentation",
		.data         = &nf_ct_enable_sip_segmentation,
		.maxlen       = sizeof(unsigned int),
		.mode         = 0644,
		.proc_handler = proc_sip_segment,
	},
	{}
};

static unsigned int (*nf_nat_sip_hook)
					(struct sk_buff *skb,
					unsigned int protoff,
					unsigned int dataoff,
					const char **dptr,
					unsigned int *datalen)
					__read_mostly;
EXPORT_SYMBOL(nf_nat_sip_hook);
static void sip_calculate_parameters(s16 *diff, s16 *tdiff,
				     unsigned int *dataoff, const char **dptr,
				     unsigned int *datalen,
				     unsigned int msglen, unsigned int origlen)
{
	*diff	 = msglen - origlen;
	*tdiff	+= *diff;
	*dataoff += msglen;
	*dptr	+= msglen;
	*datalen  = *datalen + *diff - msglen;
}

static void sip_update_params(enum ip_conntrack_dir dir,
			      unsigned int *msglen, unsigned int *origlen,
			      const char **dptr, unsigned int *datalen,
			      bool skb_is_combined, struct nf_conn *ct)
{
	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;
	}
}

/* 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
	 */
	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 {
			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)) {
					pr_debug(" Combining segments\n");
					*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);
}

void (*nf_nat_sip_seq_adjust_hook)
			(struct sk_buff *skb,
			unsigned int protoff,
			s16 off);

static 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);

static 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);

static 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);

static 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);

static 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)
{
@@ -84,6 +376,43 @@ 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 proc_sip_segment(struct ctl_table *ctl, int write,
		     void __user *buffer, size_t *lenp, loff_t *ppos)
{
	int ret;

	ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
	if (nf_ct_enable_sip_segmentation) {
		pr_debug("registering queue handler\n");
		nf_register_queue_handler(&init_net, &nf_sip_qh);
	} else {
		pr_debug("de-registering queue handler\n");
		nf_unregister_queue_handler(&init_net);
	}
	return ret;
}

static int digits_len(const struct nf_conn *ct, const char *dptr,
		      const char *limit, int *shift)
{
@@ -1505,13 +1834,29 @@ 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;
	bool content_len_exists = 1;

	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)
@@ -1535,11 +1880,26 @@ 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;

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

		clen = simple_strtoul(dptr + matchoff, (char **)&end, 10);
		if (dptr + matchoff == end)
@@ -1555,26 +1915,111 @@ static int sip_help_tcp(struct sk_buff *skb, unsigned int protoff,
		}
		if (!term)
			break;

		end += strlen("\r\n\r\n") + clen;
destination:

		msglen = origlen = end - dptr;
		if (msglen > datalen)
		if (content_len_exists == 0) {
			origlen = datalen;
			msglen = origlen;
		} else {
			origlen = end - dptr;
			msglen = origlen;
		}
		pr_debug("mslgen %d datalen %d\n", msglen, datalen);
		dir = CTINFO2DIR(ctinfo);
		combined_skb = skb;
		if (nf_ct_enable_sip_segmentation) {
			/* 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);
				sip_update_params(dir, &msglen, &origlen, &dptr,
						  &datalen,
						  skb_is_combined, ct);

				if (skip_sip_process)
					goto here;
				} else if (do_not_process) {
					goto here;
				}
		} else if (msglen > datalen) {
			return NF_ACCEPT;

		ret = process_sip_msg(skb, ct, protoff, dataoff,
		}
		/* 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;
		sip_calculate_parameters(&diff, &tdiff, &dataoff, &dptr,
					 &datalen, msglen, origlen);
		if (nf_ct_enable_sip_segmentation && skb_is_combined)
			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 */
		if (sip_entry) {
			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;
	}

	if (ret == NF_ACCEPT && ct->status & IPS_NAT_MASK) {
here:

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

		hooks = rcu_dereference(nf_nat_sip_hooks);