[fast-classifier] hook up user space generic netlink to actual sfe offload
We create a rule now, when user space say it's done with a connection.
This change does a few things:
1) Tracks new connections in the post routing hook and saves
the sfe_ipv4_create result in a list
2) Cleans up the list when connections close
3) Finds connections in the list when an offload is requested and
sends off the SFE rule
Change-Id: I75c2afdc1cdd079be4f762c7a0ab1cf86eb3472d
Signed-off-by: Matthew McClintock <mmcclint@codeaurora.org>
diff --git a/fast-classifier/fast-classifier.c b/fast-classifier/fast-classifier.c
index 0243d02..26a406c 100644
--- a/fast-classifier/fast-classifier.c
+++ b/fast-classifier/fast-classifier.c
@@ -16,6 +16,8 @@
#include <net/netfilter/nf_conntrack_zones.h>
#include <net/netfilter/nf_conntrack_core.h>
#include <net/genetlink.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
#include "../shortcut-fe/sfe.h"
#include "../shortcut-fe/sfe_ipv4.h"
@@ -198,6 +200,16 @@
return true;
}
+static DEFINE_SPINLOCK(sfe_connections_lock);
+
+struct sfe_connection {
+ struct list_head list;
+ struct sfe_ipv4_create *sic;
+ struct nf_conn *ct;
+};
+
+static LIST_HEAD(sfe_connections);
+
/*
* fast_classifier_recv_genl_msg()
* Called from user space to offload a connection
@@ -206,13 +218,83 @@
{
struct nlattr *na;
struct fast_classifier_msg *fc_msg;
+ struct sfe_ipv4_create *p_sic;
+ struct sfe_connection *conn;
+ unsigned long flags;
na = info->attrs[FAST_CLASSIFIER_C_RECV];
fc_msg = nla_data(na);
- printk("DEBUG: would offload: %d, %d, %d, %d, %d\n", fc_msg->proto,
+
+ DEBUG_TRACE("INFO: want to offload: %d, %d, %d, %d, %d\n", fc_msg->proto,
fc_msg->src_saddr,
fc_msg->dst_saddr,
fc_msg->sport, fc_msg->dport);
+ spin_lock_irqsave(&sfe_connections_lock, flags);
+ list_for_each_entry(conn, &sfe_connections, list) {
+ struct nf_conn *ct = conn->ct;
+ p_sic = conn->sic;
+
+ DEBUG_TRACE(" -> COMPARING: proto: %d src_ip: %d dst_ip: %d, src_port: %d, dst_port: %d...",
+ p_sic->protocol, p_sic->src_ip, p_sic->dest_ip,
+ p_sic->src_port, p_sic->dest_port);
+
+ if (p_sic->protocol == fc_msg->proto &&
+ p_sic->src_port == fc_msg->sport &&
+ p_sic->dest_port == fc_msg->dport &&
+ p_sic->src_ip == fc_msg->src_saddr &&
+ p_sic->dest_ip == fc_msg->dst_saddr ) {
+ DEBUG_TRACE("FOUND, WILL OFFLOAD\n");
+ switch (p_sic->protocol) {
+ case IPPROTO_TCP:
+ p_sic->src_td_window_scale = ct->proto.tcp.seen[0].td_scale;
+ p_sic->src_td_max_window = ct->proto.tcp.seen[0].td_maxwin;
+ p_sic->src_td_end = ct->proto.tcp.seen[0].td_end;
+ p_sic->src_td_max_end = ct->proto.tcp.seen[0].td_maxend;
+ p_sic->dest_td_window_scale = ct->proto.tcp.seen[1].td_scale;
+ p_sic->dest_td_max_window = ct->proto.tcp.seen[1].td_maxwin;
+ p_sic->dest_td_end = ct->proto.tcp.seen[1].td_end;
+ p_sic->dest_td_max_end = ct->proto.tcp.seen[1].td_maxend;
+ if (nf_ct_tcp_no_window_check
+ || (ct->proto.tcp.seen[0].flags & IP_CT_TCP_FLAG_BE_LIBERAL)
+ || (ct->proto.tcp.seen[1].flags & IP_CT_TCP_FLAG_BE_LIBERAL)) {
+ p_sic->flags |= SFE_IPV4_CREATE_FLAG_NO_SEQ_CHECK;
+ }
+
+ /*
+ * If the connection is shutting down do not manage it.
+ * state can not be SYN_SENT, SYN_RECV because connection is assured
+ * Not managed states: FIN_WAIT, CLOSE_WAIT, LAST_ACK, TIME_WAIT, CLOSE.
+ */
+ spin_lock(&ct->lock);
+ if (ct->proto.tcp.state != TCP_CONNTRACK_ESTABLISHED) {
+ spin_unlock_bh(&ct->lock);
+ DEBUG_TRACE("connection in termination state: %#x, s: %pI4:%u, d: %pI4:%u\n",
+ ct->proto.tcp.state, &p_sic->src_ip, ntohs(p_sic->src_port),
+ &p_sic->dest_ip, ntohs(p_sic->dest_port));
+ spin_unlock_irqrestore(&sfe_connections_lock, flags);
+ return 0;
+ }
+ spin_unlock(&ct->lock);
+ break;
+
+ case IPPROTO_UDP:
+ break;
+
+ default:
+ DEBUG_TRACE("unhandled protocol %d\n", p_sic->protocol);
+ spin_unlock_irqrestore(&sfe_connections_lock, flags);
+ return 0;
+ }
+
+ DEBUG_TRACE("INFO: calling sfe rule creation!\n");
+ spin_unlock_irqrestore(&sfe_connections_lock, flags);
+ sfe_ipv4_create_rule(p_sic);
+ return 0;
+ }
+ DEBUG_TRACE("SEARCH CONTINUES\n");
+ }
+
+ spin_unlock_irqrestore(&sfe_connections_lock, flags);
return 0;
}
@@ -227,6 +309,7 @@
int (*okfn)(struct sk_buff *))
{
struct sfe_ipv4_create sic;
+ struct sfe_ipv4_create *p_sic;
struct net_device *in;
struct nf_conn *ct;
enum ip_conntrack_info ctinfo;
@@ -236,6 +319,9 @@
struct net_device *dest_br_dev = NULL;
struct nf_conntrack_tuple orig_tuple;
struct nf_conntrack_tuple reply_tuple;
+ struct sfe_connection *conn;
+ int sfe_connections_size = 0;
+ unsigned long flags;
/*
* Don't process broadcast or multicast packets.
@@ -328,19 +414,6 @@
sic.dest_port = orig_tuple.dst.u.tcp.port;
sic.src_port_xlate = reply_tuple.dst.u.tcp.port;
sic.dest_port_xlate = reply_tuple.src.u.tcp.port;
- sic.src_td_window_scale = ct->proto.tcp.seen[0].td_scale;
- sic.src_td_max_window = ct->proto.tcp.seen[0].td_maxwin;
- sic.src_td_end = ct->proto.tcp.seen[0].td_end;
- sic.src_td_max_end = ct->proto.tcp.seen[0].td_maxend;
- sic.dest_td_window_scale = ct->proto.tcp.seen[1].td_scale;
- sic.dest_td_max_window = ct->proto.tcp.seen[1].td_maxwin;
- sic.dest_td_end = ct->proto.tcp.seen[1].td_end;
- sic.dest_td_max_end = ct->proto.tcp.seen[1].td_maxend;
- if (nf_ct_tcp_no_window_check
- || (ct->proto.tcp.seen[0].flags & IP_CT_TCP_FLAG_BE_LIBERAL)
- || (ct->proto.tcp.seen[1].flags & IP_CT_TCP_FLAG_BE_LIBERAL)) {
- sic.flags |= SFE_IPV4_CREATE_FLAG_NO_SEQ_CHECK;
- }
/*
* Don't try to manage a non-established connection.
@@ -350,20 +423,6 @@
goto done1;
}
- /*
- * If the connection is shutting down do not manage it.
- * state can not be SYN_SENT, SYN_RECV because connection is assured
- * Not managed states: FIN_WAIT, CLOSE_WAIT, LAST_ACK, TIME_WAIT, CLOSE.
- */
- spin_lock_bh(&ct->lock);
- if (ct->proto.tcp.state != TCP_CONNTRACK_ESTABLISHED) {
- spin_unlock_bh(&ct->lock);
- 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));
- goto done1;
- }
- spin_unlock_bh(&ct->lock);
break;
case IPPROTO_UDP:
@@ -379,6 +438,36 @@
}
/*
+ * If we already have this connection in our list, skip it
+ * XXX: this may need to be optimized
+ */
+ DEBUG_TRACE("POST_ROUTE: checking new connection: %d src_ip: %d dst_ip: %d, src_port: %d, dst_port: %d\n",
+ sic.protocol, sic.src_ip, sic.dest_ip,
+ sic.src_port, sic.dest_port);
+ spin_lock_irqsave(&sfe_connections_lock, flags);
+ list_for_each_entry(conn, &sfe_connections, list) {
+ p_sic = conn->sic;
+ DEBUG_TRACE("\t\t-> COMPARING: proto: %d src_ip: %d dst_ip: %d, src_port: %d, dst_port: %d...",
+ p_sic->protocol, p_sic->src_ip, p_sic->dest_ip,
+ p_sic->src_port, p_sic->dest_port);
+
+ if (p_sic->protocol == sic.protocol &&
+ p_sic->src_port == sic.src_port &&
+ p_sic->dest_port == sic.dest_port &&
+ p_sic->src_ip == sic.src_ip &&
+ p_sic->dest_ip == sic.dest_ip ) {
+ DEBUG_TRACE("FOUND, SKIPPING\n");
+ spin_unlock_irqrestore(&sfe_connections_lock, flags);
+ goto done1;
+ } else {
+ DEBUG_TRACE("SEARCH CONTINUES");
+ }
+
+ sfe_connections_size++;
+ }
+ spin_unlock_irqrestore(&sfe_connections_lock, flags);
+
+ /*
* Get the MAC addresses that correspond to source and destination host addresses.
*/
if (!fast_classifier_find_mac_addr(sic.src_ip, sic.src_mac)) {
@@ -475,8 +564,30 @@
sic.src_mtu = 1500;
sic.dest_mtu = 1500;
- sfe_ipv4_create_rule(&sic);
+ conn = kmalloc(sizeof(struct sfe_connection), GFP_KERNEL);
+ if (conn == NULL) {
+ printk(KERN_CRIT "ERROR: no memory for sfe\n");
+ goto done3;
+ }
+ p_sic = kmalloc(sizeof(struct sfe_ipv4_create), GFP_KERNEL);
+ if (p_sic == NULL) {
+ printk(KERN_CRIT "ERROR: no memory for sfe\n");
+ kfree(conn);
+ goto done3;
+ }
+
+ memcpy(p_sic, &sic, sizeof(sic));
+ conn->sic = p_sic;
+ conn->ct = ct;
+ DEBUG_TRACE(" -> adding item to sfe_connections, new size: %d\n", ++sfe_connections_size);
+ DEBUG_TRACE("POST_ROUTE: new offloadable connection: proto: %d src_ip: %d dst_ip: %d, src_port: %d, dst_port: %d\n",
+ p_sic->protocol, p_sic->src_ip, p_sic->dest_ip,
+ p_sic->src_port, p_sic->dest_port);
+ spin_lock_irqsave(&sfe_connections_lock, flags);
+ list_add_tail(&(conn->list), &sfe_connections);
+ spin_unlock_irqrestore(&sfe_connections_lock, flags);
+done3:
/*
* If we had bridge ports then release them too.
*/
@@ -513,6 +624,11 @@
struct sfe_ipv4_destroy sid;
struct nf_conn *ct = item->ct;
struct nf_conntrack_tuple orig_tuple;
+ struct sfe_connection *conn;
+ struct sfe_ipv4_create *p_sic;
+ int sfe_found_match = 0;
+ int sfe_connections_size = 0;
+ unsigned long flags;
/*
* If we don't have a conntrack entry then we're done.
@@ -572,6 +688,45 @@
return NOTIFY_DONE;
}
+ /*
+ * If we already have this connection in our list, skip it
+ * XXX: this may need to be optimized
+ */
+ DEBUG_TRACE("INFO: want to clean up: proto: %d src_ip: %d dst_ip: %d, src_port: %d, dst_port: %d\n",
+ sid.protocol, sid.src_ip, sid.dest_ip,
+ sid.src_port, sid.dest_port);
+ spin_lock_irqsave(&sfe_connections_lock, flags);
+ list_for_each_entry(conn, &sfe_connections, list) {
+ p_sic = conn->sic;
+ DEBUG_TRACE(" -> COMPARING: proto: %d src_ip: %d dst_ip: %d, src_port: %d, dst_port: %d...",
+ p_sic->protocol, p_sic->src_ip, p_sic->dest_ip,
+ p_sic->src_port, p_sic->dest_port);
+
+ if (p_sic->protocol == sid.protocol &&
+ p_sic->src_port == sid.src_port &&
+ p_sic->dest_port == sid.dest_port &&
+ p_sic->src_ip == sid.src_ip &&
+ p_sic->dest_ip == sid.dest_ip ) {
+ sfe_found_match = 1;
+ DEBUG_TRACE("FOUND, DELETING\n");
+ break;
+ } else {
+ DEBUG_TRACE("SEARCH CONTINUES\n");
+ }
+ sfe_connections_size++;
+ }
+
+ if (sfe_found_match) {
+ DEBUG_TRACE("INFO: connection over proto: %d src_ip: %d dst_ip: %d, src_port: %d, dst_port: %d\n",
+ p_sic->protocol, p_sic->src_ip, p_sic->dest_ip,
+ p_sic->src_port, p_sic->dest_port);
+ kfree(conn->sic);
+ list_del(&(conn->list));
+ kfree(conn);
+ } else {
+ DEBUG_TRACE("NO MATCH FOUND IN %d ENTRIES!!\n", sfe_connections_size);
+ }
+ spin_unlock_irqrestore(&sfe_connections_lock, flags);
sfe_ipv4_destroy_rule(&sid);
return NOTIFY_DONE;