Patch v4.4.100 to Cradlepoint Current

Change-Id: Ia3c8d927e2d4445cc67793de5468074e521507a3
diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c
index 1c4ad47..c79dec5 100644
--- a/net/xfrm/xfrm_input.c
+++ b/net/xfrm/xfrm_input.c
@@ -327,6 +327,12 @@
 
 		spin_unlock(&x->lock);
 
+		/* cradlepoint */
+		write_seqlock(&net->xfrm.xfrm_total_stats_lock);
+		net->xfrm.total_bytes += skb->len;
+		net->xfrm.total_packets++;
+		write_sequnlock(&net->xfrm.xfrm_total_stats_lock);
+
 		XFRM_MODE_SKB_CB(skb)->protocol = nexthdr;
 
 		inner_mode = x->inner_mode;
diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c
index ff4a91f..80718d9 100644
--- a/net/xfrm/xfrm_output.c
+++ b/net/xfrm/xfrm_output.c
@@ -97,6 +97,12 @@
 
 		spin_unlock_bh(&x->lock);
 
+		/* cradlepoint */
+		write_seqlock_bh(&net->xfrm.xfrm_total_stats_lock);
+		net->xfrm.total_bytes += skb->len;
+		net->xfrm.total_packets++;
+		write_sequnlock_bh(&net->xfrm.xfrm_total_stats_lock);
+
 		skb_dst_force(skb);
 
 		err = x->type->output(x, skb);
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index 0e01250..adbb453 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -117,6 +117,7 @@
 
 static inline struct dst_entry *__xfrm_dst_lookup(struct net *net,
 						  int tos, int oif,
+						  int mark,
 						  const xfrm_address_t *saddr,
 						  const xfrm_address_t *daddr,
 						  int family)
@@ -128,7 +129,7 @@
 	if (unlikely(afinfo == NULL))
 		return ERR_PTR(-EAFNOSUPPORT);
 
-	dst = afinfo->dst_lookup(net, tos, oif, saddr, daddr);
+	dst = afinfo->dst_lookup(net, tos, oif, mark, saddr, daddr);
 
 	xfrm_policy_put_afinfo(afinfo);
 
@@ -137,6 +138,7 @@
 
 static inline struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x,
 						int tos, int oif,
+						int mark,
 						xfrm_address_t *prev_saddr,
 						xfrm_address_t *prev_daddr,
 						int family)
@@ -155,7 +157,7 @@
 		daddr = x->coaddr;
 	}
 
-	dst = __xfrm_dst_lookup(net, tos, oif, saddr, daddr, family);
+	dst = __xfrm_dst_lookup(net, tos, oif, mark, saddr, daddr, family);
 
 	if (!IS_ERR(dst)) {
 		if (prev_saddr != saddr)
@@ -1524,6 +1526,21 @@
 	return tos;
 }
 
+static inline int xfrm_get_mark(const struct flowi *fl, int family)
+{
+	struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family);
+	int mark;
+
+	if (!afinfo)
+		return -EINVAL;
+
+	mark = afinfo->get_mark(fl);
+
+	xfrm_policy_put_afinfo(afinfo);
+
+	return mark;
+}
+
 static struct flow_cache_object *xfrm_bundle_flo_get(struct flow_cache_object *flo)
 {
 	struct xfrm_dst *xdst = container_of(flo, struct xfrm_dst, flo);
@@ -1666,6 +1683,7 @@
 	int nfheader_len = 0;
 	int trailer_len = 0;
 	int tos;
+	int mark;
 	int family = policy->selector.family;
 	xfrm_address_t saddr, daddr;
 
@@ -1676,6 +1694,8 @@
 	if (tos < 0)
 		goto put_states;
 
+	mark = xfrm_get_mark(fl, family);
+
 	dst_hold(dst);
 
 	for (; i < nx; i++) {
@@ -1711,7 +1731,7 @@
 
 		if (xfrm[i]->props.mode != XFRM_MODE_TRANSPORT) {
 			family = xfrm[i]->props.family;
-			dst = xfrm_dst_lookup(xfrm[i], tos, fl->flowi_oif,
+			dst = xfrm_dst_lookup(xfrm[i], tos, fl->flowi_oif, mark,
 					      &saddr, &daddr, family);
 			err = PTR_ERR(dst);
 			if (IS_ERR(dst))
diff --git a/net/xfrm/xfrm_sysctl.c b/net/xfrm/xfrm_sysctl.c
index 05a6e3d..9bc3217 100644
--- a/net/xfrm/xfrm_sysctl.c
+++ b/net/xfrm/xfrm_sysctl.c
@@ -9,9 +9,44 @@
 	net->xfrm.sysctl_aevent_rseqth = XFRM_AE_SEQT_SIZE;
 	net->xfrm.sysctl_larval_drop = 1;
 	net->xfrm.sysctl_acq_expires = 30;
+
+	// cradlepoint
+	net->xfrm.total_packets = 0;
+	net->xfrm.total_bytes = 0;
 }
 
 #ifdef CONFIG_SYSCTL
+
+// cradlepoint
+int proc_total_stats(struct ctl_table *table, int write,
+		     void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+	u64 val;
+	int ret;
+	unsigned int seq;
+	seqlock_t *lock = &((struct net*)table->extra1)->xfrm.xfrm_total_stats_lock;
+
+	if (write)
+		return -EPERM;
+
+	do {
+		seq = read_seqbegin(lock);
+		val = *(u64 *)(table->extra2);
+	} while (read_seqretry(lock, seq));
+
+	table->data = kmalloc(table->maxlen, GFP_USER);
+	if (!table->data)
+		return -ENOMEM;
+
+	snprintf((char*)(table->data), table->maxlen, "%llu", val);
+
+	ret = proc_dostring(table, write, buffer, lenp, ppos);
+
+	kfree(table->data);
+
+	return ret;
+}
+
 static struct ctl_table xfrm_table[] = {
 	{
 		.procname	= "xfrm_aevent_etime",
@@ -37,6 +72,20 @@
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec
 	},
+	{
+		/* cradlepoint */
+		.procname	= "xfrm_total_packets",
+		.maxlen		= 256,
+		.mode		= 0644,
+		.proc_handler	= proc_total_stats
+	},
+	{
+		/* cradlepoint */
+		.procname	= "xfrm_total_bytes",
+		.maxlen		= 256,
+		.mode		= 0644,
+		.proc_handler	= proc_total_stats
+	},
 	{}
 };
 
@@ -54,6 +103,12 @@
 	table[2].data = &net->xfrm.sysctl_larval_drop;
 	table[3].data = &net->xfrm.sysctl_acq_expires;
 
+	// cradlepoint xfrm total counters
+	table[4].extra1 = (void*) net;
+	table[4].extra2 = &net->xfrm.total_packets;
+	table[5].extra1 = (void*) net;
+	table[5].extra2 = &net->xfrm.total_bytes;
+
 	/* Don't export sysctls to unprivileged users */
 	if (net->user_ns != &init_user_ns)
 		table[0].procname = NULL;
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index 7a5a64e..bfcc7d8 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -3132,6 +3132,10 @@
 		return -ENOMEM;
 	net->xfrm.nlsk_stash = nlsk; /* Don't set to NULL */
 	rcu_assign_pointer(net->xfrm.nlsk, nlsk);
+
+	/* cradlepoint */
+	seqlock_init(&net->xfrm.xfrm_total_stats_lock);
+
 	return 0;
 }