shortcut-fe: add exception counters in sfe connection manager

Change-Id: I1ef34f25645faf1dcd6bc1e725d70b294aa605ac
Signed-off-by: Xiaoping Fan <xfan@codeaurora.org>
diff --git a/shortcut-fe/sfe_cm.c b/shortcut-fe/sfe_cm.c
index 61f1014..dd668fd 100644
--- a/shortcut-fe/sfe_cm.c
+++ b/shortcut-fe/sfe_cm.c
@@ -27,6 +27,47 @@
 #include "sfe_cm.h"
 #include "sfe_backport.h"
 
+typedef enum sfe_cm_exception {
+	SFE_CM_EXCEPTION_PACKET_BROADCAST,
+	SFE_CM_EXCEPTION_PACKET_MULTICAST,
+	SFE_CM_EXCEPTION_NO_IIF,
+	SFE_CM_EXCEPTION_NO_CT,
+	SFE_CM_EXCEPTION_CT_NO_TRACK,
+	SFE_CM_EXCEPTION_CT_NO_CONFIRM,
+	SFE_CM_EXCEPTION_CT_IS_ALG,
+	SFE_CM_EXCEPTION_IS_IPV4_MCAST,
+	SFE_CM_EXCEPTION_IS_IPV6_MCAST,
+	SFE_CM_EXCEPTION_TCP_NOT_ASSURED,
+	SFE_CM_EXCEPTION_TCP_NOT_ESTABLISHED,
+	SFE_CM_EXCEPTION_UNKNOW_PROTOCOL,
+	SFE_CM_EXCEPTION_NO_SRC_DEV,
+	SFE_CM_EXCEPTION_NO_SRC_XLATE_DEV,
+	SFE_CM_EXCEPTION_NO_DEST_DEV,
+	SFE_CM_EXCEPTION_NO_DEST_XLATE_DEV,
+	SFE_CM_EXCEPTION_NO_BRIDGE,
+	SFE_CM_EXCEPTION_MAX
+} sfe_cm_exception_t;
+
+static char *sfe_cm_exception_events_string[SFE_CM_EXCEPTION_MAX] = {
+	"PACKET_BROADCAST",
+	"PACKET_MULTICAST",
+	"NO_IIF",
+	"NO_CT",
+	"CT_NO_TRACK",
+	"CT_NO_CONFIRM",
+	"CT_IS_ALG",
+	"IS_IPV4_MCAST",
+	"IS_IPV6_MCAST",
+	"TCP_NOT_ASSURED",
+	"TCP_NOT_ESTABLISHED",
+	"UNKNOW_PROTOCOL",
+	"NO_SRC_DEV",
+	"NO_SRC_XLATE_DEV",
+	"NO_DEST_DEV",
+	"NO_DEST_XLATE_DEV",
+	"NO_BRIDGE"
+};
+
 /*
  * Per-module structure.
  */
@@ -47,6 +88,7 @@
 					/* IPv4 notifier */
 	struct notifier_block inet6_notifier;
 					/* IPv6 notifier */
+	uint32_t exceptions[SFE_CM_EXCEPTION_MAX];
 };
 
 struct sfe_cm __sc;
@@ -62,6 +104,19 @@
 extern int nf_ct_tcp_no_window_check;
 
 /*
+ * sfe_cm_incr_exceptions()
+ *	increase an exception counter.
+ */
+static inline void sfe_cm_incr_exceptions(sfe_cm_exception_t except)
+{
+	struct sfe_cm *sc = &__sc;
+
+	spin_lock_bh(&sc->lock);
+	sc->exceptions[except]++;
+	spin_unlock_bh(&sc->lock);
+}
+
+/*
  * sfe_cm_recv()
  *	Handle packet receives.
  *
@@ -244,10 +299,12 @@
 	 * Don't process broadcast or multicast packets.
 	 */
 	if (unlikely(skb->pkt_type == PACKET_BROADCAST)) {
+		sfe_cm_incr_exceptions(SFE_CM_EXCEPTION_PACKET_BROADCAST);
 		DEBUG_TRACE("broadcast, ignoring\n");
 		return NF_ACCEPT;
 	}
 	if (unlikely(skb->pkt_type == PACKET_MULTICAST)) {
+		sfe_cm_incr_exceptions(SFE_CM_EXCEPTION_PACKET_MULTICAST);
 		DEBUG_TRACE("multicast, ignoring\n");
 		return NF_ACCEPT;
 	}
@@ -267,6 +324,7 @@
 	 */
 	in = dev_get_by_index(&init_net, skb->skb_iif);
 	if (!in) {
+		sfe_cm_incr_exceptions(SFE_CM_EXCEPTION_NO_IIF);
 		DEBUG_TRACE("packet not forwarding\n");
 		return NF_ACCEPT;
 	}
@@ -278,6 +336,7 @@
 	 */
 	ct = nf_ct_get(skb, &ctinfo);
 	if (unlikely(!ct)) {
+		sfe_cm_incr_exceptions(SFE_CM_EXCEPTION_NO_CT);
 		DEBUG_TRACE("no conntrack connection, ignoring\n");
 		return NF_ACCEPT;
 	}
@@ -286,6 +345,7 @@
 	 * Don't process untracked connections.
 	 */
 	if (unlikely(ct == &nf_conntrack_untracked)) {
+		sfe_cm_incr_exceptions(SFE_CM_EXCEPTION_CT_NO_TRACK);
 		DEBUG_TRACE("untracked connection\n");
 		return NF_ACCEPT;
 	}
@@ -295,6 +355,7 @@
 	 * So we don't process unconfirmed connections.
 	 */
 	if (!nf_ct_is_confirmed(ct)) {
+		sfe_cm_incr_exceptions(SFE_CM_EXCEPTION_CT_NO_CONFIRM);
 		DEBUG_TRACE("unconfirmed connection\n");
 		return NF_ACCEPT;
 	}
@@ -303,6 +364,7 @@
 	 * Don't process connections that require support from a 'helper' (typically a NAT ALG).
 	 */
 	if (unlikely(nfct_help(ct))) {
+		sfe_cm_incr_exceptions(SFE_CM_EXCEPTION_CT_IS_ALG);
 		DEBUG_TRACE("connection has helper\n");
 		return NF_ACCEPT;
 	}
@@ -325,6 +387,7 @@
 		sic.dest_ip.ip = (__be32)orig_tuple.dst.u3.ip;
 
 		if (ipv4_is_multicast(sic.src_ip.ip) || ipv4_is_multicast(sic.dest_ip.ip)) {
+			sfe_cm_incr_exceptions(SFE_CM_EXCEPTION_IS_IPV4_MCAST);
 			DEBUG_TRACE("multicast address\n");
 			return NF_ACCEPT;
 		}
@@ -341,6 +404,7 @@
 
 		if (ipv6_addr_is_multicast((struct in6_addr *)sic.src_ip.ip6) ||
 		    ipv6_addr_is_multicast((struct in6_addr *)sic.dest_ip.ip6)) {
+			sfe_cm_incr_exceptions(SFE_CM_EXCEPTION_IS_IPV6_MCAST);
 			DEBUG_TRACE("multicast address\n");
 			return NF_ACCEPT;
 		}
@@ -379,6 +443,7 @@
 		 * Don't try to manage a non-established connection.
 		 */
 		if (!test_bit(IPS_ASSURED_BIT, &ct->status)) {
+			sfe_cm_incr_exceptions(SFE_CM_EXCEPTION_TCP_NOT_ASSURED);
 			DEBUG_TRACE("non-established connection\n");
 			return NF_ACCEPT;
 		}
@@ -391,6 +456,7 @@
 		spin_lock_bh(&ct->lock);
 		if (ct->proto.tcp.state != TCP_CONNTRACK_ESTABLISHED) {
 			spin_unlock_bh(&ct->lock);
+			sfe_cm_incr_exceptions(SFE_CM_EXCEPTION_TCP_NOT_ESTABLISHED);
 			DEBUG_TRACE("connection in termination state: %#x, s: %pI4:%u, d: %pI4:%u\n",
 				    ct->proto.tcp.state, &sic.src_ip, ntohs(sic.src_port),
 				    &sic.dest_ip, ntohs(sic.dest_port));
@@ -407,6 +473,7 @@
 		break;
 
 	default:
+		sfe_cm_incr_exceptions(SFE_CM_EXCEPTION_UNKNOW_PROTOCOL);
 		DEBUG_TRACE("unhandled protocol %d\n", sic.protocol);
 		return NF_ACCEPT;
 	}
@@ -438,22 +505,26 @@
 	 * destination host addresses.
 	 */
 	if (!sfe_cm_find_dev_and_mac_addr(&sic.src_ip, &src_dev, sic.src_mac, is_v4)) {
+		sfe_cm_incr_exceptions(SFE_CM_EXCEPTION_NO_SRC_DEV);
 		return NF_ACCEPT;
 	}
 
 	if (!sfe_cm_find_dev_and_mac_addr(&sic.src_ip_xlate, &dev, sic.src_mac_xlate, is_v4)) {
+		sfe_cm_incr_exceptions(SFE_CM_EXCEPTION_NO_SRC_XLATE_DEV);
 		goto done1;
 	}
 
 	dev_put(dev);
 
 	if (!sfe_cm_find_dev_and_mac_addr(&sic.dest_ip, &dev, sic.dest_mac, is_v4)) {
+		sfe_cm_incr_exceptions(SFE_CM_EXCEPTION_NO_DEST_DEV);
 		goto done1;
 	}
 
 	dev_put(dev);
 
 	if (!sfe_cm_find_dev_and_mac_addr(&sic.dest_ip_xlate, &dest_dev, sic.dest_mac_xlate, is_v4)) {
+		sfe_cm_incr_exceptions(SFE_CM_EXCEPTION_NO_DEST_XLATE_DEV);
 		goto done1;
 	}
 
@@ -465,6 +536,7 @@
 	if (src_dev->priv_flags & IFF_EBRIDGE) {
 		src_br_dev = br_port_dev_get(src_dev, sic.src_mac);
 		if (!src_br_dev) {
+			sfe_cm_incr_exceptions(SFE_CM_EXCEPTION_NO_BRIDGE);
 			DEBUG_TRACE("no port found on bridge\n");
 			goto done2;
 		}
@@ -475,6 +547,7 @@
 	if (dest_dev->priv_flags & IFF_EBRIDGE) {
 		dest_br_dev = br_port_dev_get(dest_dev, sic.dest_mac_xlate);
 		if (!dest_br_dev) {
+			sfe_cm_incr_exceptions(SFE_CM_EXCEPTION_NO_BRIDGE);
 			DEBUG_TRACE("no port found on bridge\n");
 			goto done3;
 		}
@@ -489,6 +562,7 @@
 	if (src_dev->priv_flags & IFF_BRIDGE_PORT) {
 		src_br_dev = SFE_DEV_MASTER(src_dev);
 		if (!src_br_dev) {
+			sfe_cm_incr_exceptions(SFE_CM_EXCEPTION_NO_BRIDGE);
 			DEBUG_TRACE("no bridge found for: %s\n", src_dev->name);
 			goto done2;
 		}
@@ -500,6 +574,7 @@
 	if (dest_dev->priv_flags & IFF_BRIDGE_PORT) {
 		dest_br_dev = SFE_DEV_MASTER(dest_dev);
 		if (!dest_br_dev) {
+			sfe_cm_incr_exceptions(SFE_CM_EXCEPTION_NO_BRIDGE);
 			DEBUG_TRACE("no bridge found for: %s\n", dest_dev->name);
 			goto done3;
 		}
@@ -824,6 +899,34 @@
 }
 
 /*
+ * sfe_cm_get_exceptions
+ * 	dump exception counters
+ */
+static ssize_t sfe_cm_get_exceptions(struct device *dev,
+				     struct device_attribute *attr,
+				     char *buf)
+{
+	int idx, len;
+	struct sfe_cm *sc = &__sc;
+
+	spin_lock_bh(&sc->lock);
+	for (len = 0, idx = 0; idx < SFE_CM_EXCEPTION_MAX; idx++) {
+		if (sc->exceptions[idx]) {
+			len += sprintf(buf + len, "%s = %d\n", sfe_cm_exception_events_string[idx], sc->exceptions[idx]);
+		}
+	}
+	spin_unlock_bh(&sc->lock);
+
+	return len;
+}
+
+/*
+ * sysfs attributes.
+ */
+static const struct device_attribute sfe_cm_exceptions_attr =
+	__ATTR(exceptions, S_IRUGO, sfe_cm_get_exceptions, NULL);
+
+/*
  * sfe_cm_init()
  */
 static int __init sfe_cm_init(void)
@@ -842,6 +945,15 @@
 		goto exit1;
 	}
 
+	/*
+	 * Create sys/sfe_cm/exceptions
+	 */
+	result = sysfs_create_file(sc->sys_sfe_cm, &sfe_cm_exceptions_attr.attr);
+	if (result) {
+		DEBUG_ERROR("failed to register exceptions file: %d\n", result);
+		goto exit2;
+	}
+
 	sc->dev_notifier.notifier_call = sfe_cm_device_event;
 	sc->dev_notifier.priority = 1;
 	register_netdevice_notifier(&sc->dev_notifier);
@@ -859,7 +971,7 @@
 	result = nf_register_hooks(sfe_cm_ops_post_routing, ARRAY_SIZE(sfe_cm_ops_post_routing));
 	if (result < 0) {
 		DEBUG_ERROR("can't register nf post routing hook: %d\n", result);
-		goto exit2;
+		goto exit3;
 	}
 
 #ifdef CONFIG_NF_CONNTRACK_EVENTS
@@ -869,7 +981,7 @@
 	result = nf_conntrack_register_notifier(&init_net, &sfe_cm_conntrack_notifier);
 	if (result < 0) {
 		DEBUG_ERROR("can't register nf notifier hook: %d\n", result);
-		goto exit3;
+		goto exit4;
 	}
 #endif
 
@@ -889,14 +1001,15 @@
 	return 0;
 
 #ifdef CONFIG_NF_CONNTRACK_EVENTS
-exit3:
+exit4:
 #endif
 	nf_unregister_hooks(sfe_cm_ops_post_routing, ARRAY_SIZE(sfe_cm_ops_post_routing));
 
-exit2:
+exit3:
 	unregister_inet6addr_notifier(&sc->inet6_notifier);
 	unregister_inetaddr_notifier(&sc->inet_notifier);
 	unregister_netdevice_notifier(&sc->dev_notifier);
+exit2:
 	kobject_put(sc->sys_sfe_cm);
 
 exit1: