[qca-nss-ecm] Add OVS VLAN support
A new external classifier module is addded.
This module will process the flow and decide
if there is VLAN tag[s] for this flow in the OVS
flow table.
Change-Id: I6a9705a82b6b2a8cb5fbb1083a541e50b111a757
Signed-off-by: Murat Sezgin <msezgin@codeaurora.org>
diff --git a/Makefile b/Makefile
index 45c86bd..5f1d1ab 100644
--- a/Makefile
+++ b/Makefile
@@ -28,6 +28,9 @@
ifeq ($(EXAMPLES_BUILD_MARK),y)
obj-m += examples/ecm_mark_test.o
endif
+ifeq ($(EXAMPLES_BUILD_OVS),y)
+obj-m += examples/ecm_ovs.o
+endif
ecm-y := \
ecm_tracker_udp.o \
diff --git a/ecm_classifier.h b/ecm_classifier.h
index f24fd70..6679e73 100644
--- a/ecm_classifier.h
+++ b/ecm_classifier.h
@@ -86,6 +86,12 @@
#define ECM_CLASSIFIER_PROCESS_ACTION_IGS_QOS_TAG 0x00000040 /* Contains flow & return ingress qos tags */
#endif
+#ifdef ECM_CLASSIFIER_OVS_ENABLE
+#define ECM_CLASSIFIER_PROCESS_ACTION_OVS_VLAN_TAG 0x00000080 /* Contains OVS VLAN tags */
+#define ECM_CLASSIFIER_PROCESS_ACTION_OVS_VLAN_QINQ_TAG 0x00000100
+ /* Contains OVS QinQ VLAN tags */
+#endif
+
/*
* struct ecm_classifier_process_response
* Response structure returned by a process call
@@ -108,6 +114,10 @@
uint8_t flow_dscp; /* DSCP mark for flow */
uint8_t return_dscp; /* DSCP mark for return */
#endif
+#ifdef ECM_CLASSIFIER_OVS_ENABLE
+ uint32_t ingress_vlan_tag[2]; /* Ingress VLAN tags */
+ uint32_t egress_vlan_tag[2]; /* Egress VLAN tags */
+#endif
ecm_classifier_acceleration_mode_t accel_mode; /* Acceleration needed for this connection */
ecm_db_timer_group_t timer_group; /* Timer group the connection should be in */
};
@@ -264,6 +274,26 @@
}
}
+#ifdef ECM_CLASSIFIER_OVS_ENABLE
+ if (pr->process_actions & ECM_CLASSIFIER_PROCESS_ACTION_OVS_VLAN_TAG) {
+ if ((result = ecm_state_write(sfi, "ingress_vlan_tag[0]", "0x%x", pr->ingress_vlan_tag[0]))) {
+ return result;
+ }
+ if ((result = ecm_state_write(sfi, "egress_vlan_tag[0]", "0x%x", pr->egress_vlan_tag[0]))) {
+ return result;
+ }
+ }
+
+ if (pr->process_actions & ECM_CLASSIFIER_PROCESS_ACTION_OVS_VLAN_QINQ_TAG) {
+ if ((result = ecm_state_write(sfi, "ingress_vlan_tag[1]", "0x%x", pr->ingress_vlan_tag[1]))) {
+ return result;
+ }
+ if ((result = ecm_state_write(sfi, "egress_vlan_tag[1]", "0x%x", pr->egress_vlan_tag[1]))) {
+ return result;
+ }
+ }
+#endif
+
#ifdef ECM_CLASSIFIER_DSCP_ENABLE
if (pr->process_actions & ECM_CLASSIFIER_PROCESS_ACTION_QOS_TAG) {
if ((result = ecm_state_write(sfi, "flow_qos_tag", "%u", pr->flow_qos_tag))) {
diff --git a/ecm_classifier_ovs.c b/ecm_classifier_ovs.c
index f0e7208..721b243 100644
--- a/ecm_classifier_ovs.c
+++ b/ecm_classifier_ovs.c
@@ -58,6 +58,7 @@
#include "ecm_tracker_tcp.h"
#include "ecm_db.h"
#include "ecm_classifier_ovs.h"
+#include "ecm_classifier_ovs_public.h"
#include "ecm_front_end_common.h"
#include "ecm_front_end_ipv4.h"
#ifdef ECM_IPV6_ENABLE
@@ -118,6 +119,11 @@
static int ecm_classifier_ovs_count = 0; /* Tracks number of instances allocated */
/*
+ * Callback object.
+ */
+static struct ecm_classifier_ovs_callbacks ovs;
+
+/*
* ecm_classifier_ovs_ref()
* Ref
*/
@@ -239,6 +245,281 @@
}
/*
+ * ecm_classifier_ovs_process_route_flow()
+ * Process routed flows.
+ */
+static void ecm_classifier_ovs_process_route_flow(struct ecm_classifier_ovs_instance *ecvi, struct ecm_db_connection_instance *ci,
+ struct sk_buff *skb, struct net_device *from_dev, struct net_device *to_dev,
+ struct ecm_classifier_process_response *process_response,
+ ecm_classifier_ovs_process_callback_t cb)
+{
+ ecm_classifier_ovs_result_t result;
+ struct ecm_classifier_ovs_process_response resp;
+ struct ovsmgr_dp_flow flow;
+ struct net_device *br_dev;
+ ip_addr_t src_ip, dst_ip;
+
+ memset(&flow, 0, sizeof(flow));
+
+ flow.is_routed = true;
+ flow.tuple.ip_version = ecm_db_connection_ip_version_get(ci);
+ flow.tuple.protocol = ecm_db_connection_protocol_get(ci);
+
+ spin_lock_bh(&ecm_classifier_ovs_lock);
+ ecvi->process_response.ingress_vlan_tag[0] = ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED;
+ ecvi->process_response.egress_vlan_tag[0] = ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED;
+ ecvi->process_response.ingress_vlan_tag[1] = ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED;
+ ecvi->process_response.egress_vlan_tag[1] = ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED;
+ spin_unlock_bh(&ecm_classifier_ovs_lock);
+
+ /*
+ * For routed flows both from and to side can be OVS bridge port, if there
+ * is a routed flow between two OVS bridges. (e.g: ovs-br1 and ovs-br2)
+ *
+ * Connection Direction:
+ * PC1 -----> eth1-ovs-br1 (DUT) ovs-br2-eth2 -----> PC2
+ from_dev to_dev
+ *
+ * 1. SNAT/DNAT Enabled: FROM FROM_NAT TO/TO_NAT
+ * 2. SNAT/DNAT Disabled: FROM/FROM_NAT TO/TO_NAT
+ *
+ * Connection Direction:
+ * PC1 <----- eth1-ovs-br1 (DUT) ovs-br2-eth2 <----- PC2
+ * to_dev from_dev
+ *
+ * 3. SNAT/DNAT Enabled: TO TO_NAT FROM/FROM_NAT
+ * 4. SNAT/DNAT Disabled: TO/TO_NAT FROM/FROM_NAT
+ */
+ if (from_dev) {
+ /*
+ * Case 1/2
+ * from_dev = eth1
+ * br_dev = ovs-br1
+ *
+ * Case 3/4
+ * from_dev = eth2
+ * br_dev = ovs-br2
+ */
+ br_dev = ecm_classifier_ovs_interface_get_and_ref(ci, ECM_DB_OBJ_DIR_FROM, false);
+ if (!br_dev) {
+ DEBUG_WARN("%p: from_dev = %s is a OVS bridge port, bridge interface is not found\n",
+ ecvi, from_dev->name);
+ goto route_deny_accel;
+ }
+
+ DEBUG_TRACE("%p: processing route flow from_dev = %s, br_dev = %s", ecvi, from_dev->name, br_dev->name);
+
+ /*
+ * We always take the flow from bridge to port, so indev is brdige and outdev is port device.
+ */
+ flow.indev = br_dev;
+ flow.outdev = from_dev;
+
+ flow.tuple.src_port = htons(ecm_db_connection_port_get(ci, ECM_DB_OBJ_DIR_TO_NAT));
+ flow.tuple.dst_port = htons(ecm_db_connection_port_get(ci, ECM_DB_OBJ_DIR_FROM));
+
+ ecm_db_connection_address_get(ci, ECM_DB_OBJ_DIR_TO_NAT, src_ip);
+ ecm_db_connection_address_get(ci, ECM_DB_OBJ_DIR_FROM, dst_ip);
+
+ if (flow.tuple.ip_version == 4) {
+ ECM_IP_ADDR_TO_NIN4_ADDR(flow.tuple.ipv4.src, src_ip);
+ ECM_IP_ADDR_TO_NIN4_ADDR(flow.tuple.ipv4.dst, dst_ip);
+ } else if (flow.tuple.ip_version == 6) {
+ ECM_IP_ADDR_TO_NIN6_ADDR(flow.tuple.ipv6.src, src_ip);
+ ECM_IP_ADDR_TO_NIN6_ADDR(flow.tuple.ipv6.dst, dst_ip);
+ } else {
+ DEBUG_ASSERT(NULL, "%p: unexpected ip_version: %d", ecvi, flow.tuple.ip_version );
+ }
+
+ ether_addr_copy(flow.smac, br_dev->dev_addr);
+ ecm_db_connection_node_address_get(ci, ECM_DB_OBJ_DIR_FROM, flow.dmac);
+
+ memset(&resp, 0, sizeof(struct ecm_classifier_ovs_process_response));
+
+ /*
+ * Call the external callback and get the result.
+ */
+ result = cb(&flow, skb, &resp);
+
+ dev_put(br_dev);
+
+ /*
+ * Handle the result
+ */
+ switch (result) {
+ case ECM_CLASSIFIER_OVS_RESULT_ALLOW_VLAN_ACCEL:
+ case ECM_CLASSIFIER_OVS_RESULT_ALLOW_VLAN_QINQ_ACCEL:
+ /*
+ * Allow accel after setting the external module response.
+ */
+ DEBUG_WARN("%p: External callback process succeeded\n", ecvi);
+
+ spin_lock_bh(&ecm_classifier_ovs_lock);
+ if (resp.egress_vlan[0].h_vlan_TCI) {
+ ecvi->process_response.ingress_vlan_tag[0] = resp.egress_vlan[0].h_vlan_encapsulated_proto << 16 | resp.egress_vlan[0].h_vlan_TCI;
+ DEBUG_TRACE("Ingress vlan tag[0] set : %x\n", ecvi->process_response.ingress_vlan_tag[0]);
+ }
+
+ ecvi->process_response.process_actions = ECM_CLASSIFIER_PROCESS_ACTION_OVS_VLAN_TAG;
+
+ if (result == ECM_CLASSIFIER_OVS_RESULT_ALLOW_VLAN_QINQ_ACCEL) {
+ if (resp.egress_vlan[1].h_vlan_TCI) {
+ ecvi->process_response.ingress_vlan_tag[1] = resp.egress_vlan[1].h_vlan_encapsulated_proto << 16 | resp.egress_vlan[1].h_vlan_TCI;
+ DEBUG_TRACE("Ingress vlan tag[0] set : %x\n", ecvi->process_response.ingress_vlan_tag[1]);
+ }
+
+ ecvi->process_response.process_actions |= ECM_CLASSIFIER_PROCESS_ACTION_OVS_VLAN_QINQ_TAG;
+ }
+ spin_unlock_bh(&ecm_classifier_ovs_lock);
+ break;
+
+ case ECM_CLASSIFIER_OVS_RESULT_DENY_ACCEL:
+ /*
+ * External callback failed to process VLAN process. So, let's deny the acceleration
+ * and try more with the subsequent packets.
+ */
+ DEBUG_WARN("%p: External callback failed to process VLAN tags\n", ecvi);
+ goto route_deny_accel;
+
+ case ECM_CLASSIFIER_OVS_RESULT_ALLOW_ACCEL:
+
+ /*
+ * There is no VLAN tag in the flow. Just allow the acceleration.
+ */
+ DEBUG_WARN("%p: External callback didn't find any VLAN relation\n", ecvi);
+ break;
+
+ default:
+ DEBUG_ASSERT(false, "Unhandled result: %d\n", result);
+ }
+ }
+
+ if (to_dev) {
+ /*
+ * Case 1/2
+ * from_dev = eth2
+ * br_dev = ovs-br2
+ *
+ * Case 3/4
+ * from_dev = eth1
+ * br_dev = ovs-br1
+ */
+ br_dev = ecm_classifier_ovs_interface_get_and_ref(ci, ECM_DB_OBJ_DIR_TO, false);
+ if (!br_dev) {
+ DEBUG_WARN("%p: to_dev = %s is a OVS bridge port, bridge interface is not found\n",
+ ecvi, to_dev->name);
+ goto route_deny_accel;
+ }
+
+ DEBUG_TRACE("%p: processing route flow to_dev = %s, br_dev = %s", ecvi, to_dev->name, br_dev->name);
+
+ flow.indev = br_dev;
+ flow.outdev = to_dev;
+
+ flow.tuple.src_port = htons(ecm_db_connection_port_get(ci, ECM_DB_OBJ_DIR_FROM_NAT));
+ flow.tuple.dst_port = htons(ecm_db_connection_port_get(ci, ECM_DB_OBJ_DIR_TO));
+
+ ecm_db_connection_address_get(ci, ECM_DB_OBJ_DIR_FROM_NAT, src_ip);
+ ecm_db_connection_address_get(ci, ECM_DB_OBJ_DIR_TO, dst_ip);
+
+ if (flow.tuple.ip_version == 4) {
+ ECM_IP_ADDR_TO_NIN4_ADDR(flow.tuple.ipv4.src, src_ip);
+ ECM_IP_ADDR_TO_NIN4_ADDR(flow.tuple.ipv4.dst, dst_ip);
+ } else if (flow.tuple.ip_version == 6) {
+ ECM_IP_ADDR_TO_NIN6_ADDR(flow.tuple.ipv6.src, src_ip);
+ ECM_IP_ADDR_TO_NIN6_ADDR(flow.tuple.ipv6.dst, dst_ip);
+ } else {
+ DEBUG_ASSERT(NULL, "%p: unexpected ip_version: %d", ecvi, flow.tuple.ip_version );
+ }
+
+ ether_addr_copy(flow.smac, br_dev->dev_addr);
+ ecm_db_connection_node_address_get(ci, ECM_DB_OBJ_DIR_TO, flow.dmac);
+
+ memset(&resp, 0, sizeof(struct ecm_classifier_ovs_process_response));
+
+ /*
+ * Call the external callback and get the result.
+ */
+ result = cb(&flow, skb, &resp);
+
+ dev_put(br_dev);
+
+ /*
+ * Handle the result
+ */
+ switch (result) {
+ case ECM_CLASSIFIER_OVS_RESULT_ALLOW_VLAN_ACCEL:
+ case ECM_CLASSIFIER_OVS_RESULT_ALLOW_VLAN_QINQ_ACCEL:
+ /*
+ * Allow accel after setting the external module response.
+ */
+ DEBUG_WARN("%p: External callback process succeeded\n", ecvi);
+
+ spin_lock_bh(&ecm_classifier_ovs_lock);
+ if (resp.egress_vlan[0].h_vlan_TCI) {
+ ecvi->process_response.egress_vlan_tag[0] = resp.egress_vlan[0].h_vlan_encapsulated_proto << 16 | resp.egress_vlan[0].h_vlan_TCI;
+ DEBUG_TRACE("Egress vlan tag[0] set : %x\n", ecvi->process_response.egress_vlan_tag[0]);
+ }
+
+ ecvi->process_response.process_actions = ECM_CLASSIFIER_PROCESS_ACTION_OVS_VLAN_TAG;
+
+ if (result == ECM_CLASSIFIER_OVS_RESULT_ALLOW_VLAN_QINQ_ACCEL) {
+ if (resp.egress_vlan[1].h_vlan_TCI) {
+ ecvi->process_response.egress_vlan_tag[1] = resp.egress_vlan[1].h_vlan_encapsulated_proto << 16 | resp.egress_vlan[1].h_vlan_TCI;
+ DEBUG_TRACE("Ingress vlan tag[0] set : %x\n", ecvi->process_response.ingress_vlan_tag[1]);
+ }
+
+ ecvi->process_response.process_actions |= ECM_CLASSIFIER_PROCESS_ACTION_OVS_VLAN_QINQ_TAG;
+ }
+ spin_unlock_bh(&ecm_classifier_ovs_lock);
+ break;
+
+ case ECM_CLASSIFIER_OVS_RESULT_DENY_ACCEL:
+ /*
+ * External callback failed to process VLAN process. So, let's deny the acceleration
+ * and try more with the subsequent packets.
+ */
+ DEBUG_WARN("%p: External callback failed to process VLAN tags\n", ecvi);
+ goto route_deny_accel;
+
+ case ECM_CLASSIFIER_OVS_RESULT_ALLOW_ACCEL:
+
+ /*
+ * There is no VLAN tag in the flow. Just allow the acceleration.
+ */
+ DEBUG_WARN("%p: External callback didn't find any VLAN relation\n", ecvi);
+ break;
+
+ default:
+ DEBUG_ASSERT(false, "Unhandled result: %d\n", result);
+ }
+ }
+
+ /*
+ * Acceleration is permitted
+ */
+ spin_lock_bh(&ecm_classifier_ovs_lock);
+ ecvi->process_response.relevance = ECM_CLASSIFIER_RELEVANCE_YES;
+ ecvi->process_response.process_actions |= ECM_CLASSIFIER_PROCESS_ACTION_ACCEL_MODE;
+ ecvi->process_response.accel_mode = ECM_CLASSIFIER_ACCELERATION_MODE_ACCEL;
+ *process_response = ecvi->process_response;
+ spin_unlock_bh(&ecm_classifier_ovs_lock);
+
+ return;
+
+route_deny_accel:
+ /*
+ * ecm_classifier_ovs_lock MUST be held
+ */
+ spin_lock_bh(&ecm_classifier_ovs_lock);
+ ecvi->process_response.relevance = ECM_CLASSIFIER_RELEVANCE_YES;
+ ecvi->process_response.process_actions = ECM_CLASSIFIER_PROCESS_ACTION_ACCEL_MODE;
+ ecvi->process_response.accel_mode = ECM_CLASSIFIER_ACCELERATION_MODE_NO;
+ *process_response = ecvi->process_response;
+ spin_unlock_bh(&ecm_classifier_ovs_lock);
+}
+
+/*
* ecm_classifier_ovs_process()
* Process new packet
*
@@ -249,7 +530,13 @@
struct ecm_classifier_process_response *process_response)
{
struct ecm_classifier_ovs_instance *ecvi = (struct ecm_classifier_ovs_instance *)aci;
+ ecm_classifier_ovs_result_t result = 0;
struct ecm_db_connection_instance *ci;
+ ecm_classifier_ovs_process_callback_t cb = NULL;
+ ip_addr_t src_ip;
+ ip_addr_t dst_ip;
+ struct ecm_classifier_ovs_process_response resp;
+ struct ovsmgr_dp_flow flow;
struct net_device *from_dev = NULL;
struct net_device *to_dev = NULL;
@@ -294,21 +581,118 @@
}
/*
- * If the connection is a bridge flow, both devices should be present.
+ * Is there an external callback to get the ovs value from the packet?
*/
- if (!ecm_db_connection_is_routed_get(ci)) {
- if (!from_dev || !to_dev) {
- DEBUG_ERROR("%p: One of the ports is NULL from_dev: %p to_dev: %p\n", aci, from_dev, to_dev);
- if (from_dev)
- dev_put(from_dev);
+ spin_lock_bh(&ecm_classifier_ovs_lock);
+ cb = ovs.ovs_process;
+ if (!cb) {
+ /*
+ * Allow acceleration.
+ * Keep the classifier relevant to connection for stats update..
+ */
+ spin_unlock_bh(&ecm_classifier_ovs_lock);
+ DEBUG_WARN("%p: No external process callback set\n", aci);
+ if (from_dev)
+ dev_put(from_dev);
- if (to_dev)
- dev_put(to_dev);
+ if (to_dev)
+ dev_put(to_dev);
- ecm_db_connection_deref(ci);
- goto not_relevant;
- }
+ spin_lock_bh(&ecm_classifier_ovs_lock);
+ goto allow_accel;
}
+ spin_unlock_bh(&ecm_classifier_ovs_lock);
+
+ /*
+ * If the flow is a routed flow, set the is_routed flag of the flow.
+ */
+ if (ecm_db_connection_is_routed_get(ci)) {
+ ecm_classifier_ovs_process_route_flow(ecvi, ci, skb, from_dev, to_dev, process_response, cb);
+
+ if (from_dev)
+ dev_put(from_dev);
+
+ if (to_dev)
+ dev_put(to_dev);
+
+ ecm_db_connection_deref(ci);
+ return;
+ }
+
+ memset(&flow, 0, sizeof(struct ovsmgr_dp_flow));
+
+ /*
+ * If the connection is a bridge flow, both devices should be present.
+ * TODO: This is an unexpected situation and should be assertion.
+ * We should also make sure that both devices are on the same OVS bridge.
+ */
+ if (!from_dev || !to_dev) {
+ DEBUG_ERROR("%p: One of the ports is NULL from_dev: %p to_dev: %p\n", aci, from_dev, to_dev);
+
+ if (from_dev)
+ dev_put(from_dev);
+
+ if (to_dev)
+ dev_put(to_dev);
+
+ ecm_db_connection_deref(ci);
+ goto not_relevant;
+ }
+
+ /*
+ * Flow is an OVS bridge flow.
+ */
+ flow.tuple.ip_version = ecm_db_connection_ip_version_get(ci);
+ flow.tuple.protocol = ecm_db_connection_protocol_get(ci);
+
+ /*
+ * For the flow lookup, we need to use the proper 5-tuple, in/out dev and
+ * src/dest MAC address. If the packet is coming from the destination side of the connection
+ * (e.g: ACK packets of the TCP connection) these values should be reversed.
+ */
+ if (sender == ECM_TRACKER_SENDER_TYPE_SRC) {
+ DEBUG_TRACE("%p: sender is SRC\n", aci);
+ flow.indev = from_dev;
+ flow.outdev = to_dev;
+
+ flow.tuple.src_port = htons(ecm_db_connection_port_get(ci, ECM_DB_OBJ_DIR_FROM));
+ flow.tuple.dst_port = htons(ecm_db_connection_port_get(ci, ECM_DB_OBJ_DIR_TO));
+
+ ecm_db_connection_address_get(ci, ECM_DB_OBJ_DIR_FROM, src_ip);
+ ecm_db_connection_address_get(ci, ECM_DB_OBJ_DIR_TO, dst_ip);
+ ecm_db_connection_node_address_get(ci, ECM_DB_OBJ_DIR_FROM, flow.smac);
+ ecm_db_connection_node_address_get(ci, ECM_DB_OBJ_DIR_TO, flow.dmac);
+ } else {
+ DEBUG_TRACE("%p: sender is DEST\n", aci);
+ flow.indev = to_dev;
+ flow.outdev = from_dev;
+
+ flow.tuple.src_port = htons(ecm_db_connection_port_get(ci, ECM_DB_OBJ_DIR_TO));
+ flow.tuple.dst_port = htons(ecm_db_connection_port_get(ci, ECM_DB_OBJ_DIR_FROM));
+
+ ecm_db_connection_address_get(ci, ECM_DB_OBJ_DIR_TO, src_ip);
+ ecm_db_connection_address_get(ci, ECM_DB_OBJ_DIR_FROM, dst_ip);
+ ecm_db_connection_node_address_get(ci, ECM_DB_OBJ_DIR_TO, flow.smac);
+ ecm_db_connection_node_address_get(ci, ECM_DB_OBJ_DIR_FROM, flow.dmac);
+
+ }
+
+ if (flow.tuple.ip_version == 4) {
+ ECM_IP_ADDR_TO_NIN4_ADDR(flow.tuple.ipv4.src, src_ip);
+ ECM_IP_ADDR_TO_NIN4_ADDR(flow.tuple.ipv4.dst, dst_ip);
+ } else if (flow.tuple.ip_version == 6) {
+ ECM_IP_ADDR_TO_NIN6_ADDR(flow.tuple.ipv6.src, src_ip);
+ ECM_IP_ADDR_TO_NIN6_ADDR(flow.tuple.ipv6.dst, dst_ip);
+ } else {
+ DEBUG_ASSERT(NULL, "%p: unexpected ip_version: %d", aci, flow.tuple.ip_version );
+ }
+
+ memset(&resp, 0, sizeof(struct ecm_classifier_ovs_process_response));
+
+ /*
+ * Call the external callback and get the result.
+ */
+ result = cb(&flow, skb, &resp);
if (from_dev)
dev_put(from_dev);
@@ -317,9 +701,117 @@
dev_put(to_dev);
/*
+ * Handle the result
+ */
+ switch (result) {
+ case ECM_CLASSIFIER_OVS_RESULT_ALLOW_VLAN_ACCEL:
+ case ECM_CLASSIFIER_OVS_RESULT_ALLOW_VLAN_QINQ_ACCEL:
+ /*
+ * Allow accel after setting the external module response.
+ */
+ DEBUG_WARN("%p: External callback process succeeded\n", aci);
+
+ spin_lock_bh(&ecm_classifier_ovs_lock);
+ ecvi->process_response.ingress_vlan_tag[0] = ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED;
+ ecvi->process_response.egress_vlan_tag[0] = ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED;
+ ecvi->process_response.ingress_vlan_tag[1] = ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED;
+ ecvi->process_response.egress_vlan_tag[1] = ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED;
+
+ /*
+ * Primary VLAN tag is always present even it is QinQ.
+ */
+ ecvi->process_response.process_actions = ECM_CLASSIFIER_PROCESS_ACTION_OVS_VLAN_TAG;
+
+ /*
+ * If the sender type is source, which means the packet is coming from the originator,
+ * we assign the ECM-ingress<>OVS-ingress, ECM-egress<>OVS-egress values.
+ */
+ if (sender == ECM_TRACKER_SENDER_TYPE_SRC) {
+ if (resp.ingress_vlan[0].h_vlan_TCI) {
+ ecvi->process_response.ingress_vlan_tag[0] = resp.ingress_vlan[0].h_vlan_encapsulated_proto << 16 | resp.ingress_vlan[0].h_vlan_TCI;
+ DEBUG_TRACE("Ingress vlan tag[0] set : 0x%x\n", ecvi->process_response.ingress_vlan_tag[0]);
+ }
+
+ if (resp.egress_vlan[0].h_vlan_TCI) {
+ ecvi->process_response.egress_vlan_tag[0] = resp.egress_vlan[0].h_vlan_encapsulated_proto << 16 | resp.egress_vlan[0].h_vlan_TCI;
+ DEBUG_TRACE("Egress vlan tag[0] set : 0x%x\n", ecvi->process_response.egress_vlan_tag[0]);
+ }
+
+ if (result == ECM_CLASSIFIER_OVS_RESULT_ALLOW_VLAN_QINQ_ACCEL) {
+ if (resp.ingress_vlan[1].h_vlan_TCI) {
+ ecvi->process_response.ingress_vlan_tag[1] = resp.ingress_vlan[1].h_vlan_encapsulated_proto << 16 | resp.ingress_vlan[1].h_vlan_TCI;
+ DEBUG_TRACE("Ingress vlan tag[0] set : 0x%x\n", ecvi->process_response.ingress_vlan_tag[1]);
+ }
+
+ if (resp.egress_vlan[1].h_vlan_TCI) {
+ ecvi->process_response.egress_vlan_tag[1] = resp.egress_vlan[1].h_vlan_encapsulated_proto << 16 | resp.egress_vlan[1].h_vlan_TCI;
+ DEBUG_TRACE("Egress vlan tag[1] set : 0x%x\n", ecvi->process_response.egress_vlan_tag[1]);
+ }
+
+ /*
+ * QinQ tag is present. Let's pass this information to the frontend through the process action flag.
+ */
+ ecvi->process_response.process_actions |= ECM_CLASSIFIER_PROCESS_ACTION_OVS_VLAN_QINQ_TAG;
+ }
+ } else {
+ /*
+ * Sender type is destination, which means packet si coming from the counter originator side,
+ * we assign the ECM-ingress<>OVS-egress and ECM-egress<>OVS-ingress values.
+ */
+ if (resp.ingress_vlan[0].h_vlan_TCI) {
+ ecvi->process_response.egress_vlan_tag[0] = resp.ingress_vlan[0].h_vlan_encapsulated_proto << 16 | resp.ingress_vlan[0].h_vlan_TCI;
+ DEBUG_TRACE("Egress vlan tag[0] set : 0x%x\n", ecvi->process_response.egress_vlan_tag[0]);
+ }
+
+ if (resp.egress_vlan[0].h_vlan_TCI) {
+ ecvi->process_response.ingress_vlan_tag[0] = resp.egress_vlan[0].h_vlan_encapsulated_proto << 16 | resp.egress_vlan[0].h_vlan_TCI;
+ DEBUG_TRACE("Ingress vlan tag[0] set : 0x%x\n", ecvi->process_response.ingress_vlan_tag[0]);
+ }
+
+ if (result == ECM_CLASSIFIER_OVS_RESULT_ALLOW_VLAN_QINQ_ACCEL) {
+ if (resp.ingress_vlan[1].h_vlan_TCI) {
+ ecvi->process_response.egress_vlan_tag[1] = resp.ingress_vlan[1].h_vlan_encapsulated_proto << 16 | resp.ingress_vlan[1].h_vlan_TCI;
+ DEBUG_TRACE("Egress vlan tag[0] set : 0x%x\n", ecvi->process_response.egress_vlan_tag[1]);
+ }
+
+ if (resp.egress_vlan[1].h_vlan_TCI) {
+ ecvi->process_response.ingress_vlan_tag[1] = resp.egress_vlan[1].h_vlan_encapsulated_proto << 16 | resp.egress_vlan[1].h_vlan_TCI;
+ DEBUG_TRACE("Ingress vlan tag[1] set : 0x%x\n", ecvi->process_response.ingress_vlan_tag[1]);
+ }
+
+ /*
+ * QinQ tag is present. Let's pass this information to the frontend through the process action flag.
+ */
+ ecvi->process_response.process_actions |= ECM_CLASSIFIER_PROCESS_ACTION_OVS_VLAN_QINQ_TAG;
+ }
+ }
+ goto allow_accel;
+
+ case ECM_CLASSIFIER_OVS_RESULT_DENY_ACCEL:
+ /*
+ * External callback failed to process VLAN process. So, let's deny the acceleration
+ * and try more with the subsequent packets.
+ */
+ DEBUG_WARN("%p: External callback failed to process VLAN tags\n", aci);
+ goto deny_accel;
+
+ case ECM_CLASSIFIER_OVS_RESULT_ALLOW_ACCEL:
+
+ /*
+ * There is no VLAN tag in the flow. Just allow the acceleration.
+ */
+ DEBUG_WARN("%p: External callback didn't find any VLAN relation\n", aci);
+ spin_lock_bh(&ecm_classifier_ovs_lock);
+ goto allow_accel;
+
+ default:
+ DEBUG_ASSERT(false, "Unhandled result: %d\n", result);
+ }
+
+allow_accel:
+ /*
* Acceleration is permitted
*/
- spin_lock_bh(&ecm_classifier_ovs_lock);
ecvi->process_response.relevance = ECM_CLASSIFIER_RELEVANCE_YES;
ecvi->process_response.process_actions |= ECM_CLASSIFIER_PROCESS_ACTION_ACCEL_MODE;
ecvi->process_response.accel_mode = ECM_CLASSIFIER_ACCELERATION_MODE_ACCEL;
@@ -337,6 +829,18 @@
*process_response = ecvi->process_response;
spin_unlock_bh(&ecm_classifier_ovs_lock);
return;
+
+deny_accel:
+ /*
+ * ecm_classifier_ovs_lock MUST be held
+ */
+ spin_lock_bh(&ecm_classifier_ovs_lock);
+ ecvi->process_response.relevance = ECM_CLASSIFIER_RELEVANCE_YES;
+ ecvi->process_response.process_actions = ECM_CLASSIFIER_PROCESS_ACTION_ACCEL_MODE;
+ ecvi->process_response.accel_mode = ECM_CLASSIFIER_ACCELERATION_MODE_NO;
+ *process_response = ecvi->process_response;
+ spin_unlock_bh(&ecm_classifier_ovs_lock);
+ ecm_db_connection_deref(ci);
}
/*
@@ -408,7 +912,7 @@
struct net_device *indev, struct net_device *outdev,
uint8_t *smac, uint8_t *dmac,
ip_addr_t sip, ip_addr_t dip,
- uint16_t sport, uint16_t dport)
+ uint16_t sport, uint16_t dport, uint16_t tci, uint16_t tpid)
{
struct ovsmgr_dp_flow_stats stats;
@@ -418,6 +922,17 @@
ether_addr_copy(flow->smac, smac);
ether_addr_copy(flow->dmac, dmac);
+ /*
+ * Set default VLAN tci and vlan encapsulated proto.
+ */
+ flow->ingress_vlan.h_vlan_TCI = 0;
+ flow->ingress_vlan.h_vlan_encapsulated_proto = 0;
+
+ if (tci) {
+ flow->ingress_vlan.h_vlan_TCI = tci;
+ flow->ingress_vlan.h_vlan_encapsulated_proto = tpid;
+ }
+
flow->tuple.src_port = sport;
flow->tuple.dst_port = dport;
@@ -463,6 +978,7 @@
uint8_t dmac[ETH_ALEN];
uint16_t sport;
uint16_t dport;
+ uint16_t tpid = 0, tci = 0;
struct ecm_classifier_ovs_instance *ecvi = (struct ecm_classifier_ovs_instance *)aci;
@@ -474,6 +990,7 @@
return;
}
+ memset(&flow, 0, sizeof(flow));
/*
* Get the possible OVS bridge ports.
*/
@@ -489,6 +1006,15 @@
flow.is_routed = ecm_db_connection_is_routed_get(ci);
/*
+ * Get the tci and tpid values of the ingress side of the flow.
+ */
+ if (ecvi->process_response.ingress_vlan_tag[0] != ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED) {
+ tci = ecvi->process_response.ingress_vlan_tag[0] & 0xffff;
+ tpid = (ecvi->process_response.ingress_vlan_tag[0] >> 16) & 0xffff;
+ DEBUG_TRACE("%p: Ingress VLAN : %x:%x\n", aci, tci, tpid);
+ }
+
+ /*
* Bridge flow
*/
if (!flow.is_routed) {
@@ -504,25 +1030,38 @@
ecm_db_connection_address_get(ci, ECM_DB_OBJ_DIR_FROM, src_ip);
ecm_db_connection_address_get(ci, ECM_DB_OBJ_DIR_TO, dst_ip);
+ DEBUG_TRACE("%p: Flow direction stats update\n", aci);
ecm_classifier_ovs_stats_sync(&flow,
sync->flow_rx_packet_count, sync->flow_rx_byte_count,
sync->return_tx_packet_count, sync->return_tx_byte_count,
from_dev, to_dev,
smac, dmac,
src_ip, dst_ip,
- sport, dport);
+ sport, dport, tci, tpid);
/*
* Sync the return direction (eth2 to eth1)
* All the flow parameters are reversed.
*/
+ DEBUG_TRACE("%p: Return direction stats update\n", aci);
+
+ /*
+ * Reset the tci and tpid values and get the egress side of the flow.
+ */
+ tci = tpid = 0;
+ if (ecvi->process_response.egress_vlan_tag[0] != ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED) {
+ tci = ecvi->process_response.egress_vlan_tag[0] & 0xffff;
+ tpid = (ecvi->process_response.egress_vlan_tag[0] >> 16) & 0xffff;
+ DEBUG_TRACE("%p: Egress VLAN : %x:%x\n", aci, tci, tpid);
+ }
+
ecm_classifier_ovs_stats_sync(&flow,
sync->flow_tx_packet_count, sync->flow_tx_byte_count,
sync->return_rx_packet_count, sync->return_rx_byte_count,
to_dev, from_dev,
dmac, smac,
dst_ip, src_ip,
- dport, sport);
+ dport, sport, tci, tpid);
goto done;
}
@@ -562,7 +1101,7 @@
from_dev, br_dev,
smac, dmac,
src_ip, dst_ip,
- sport, dport);
+ sport, dport, tci, tpid);
/*
* Sync the return direction (ovs-br1 to eth1)
@@ -574,7 +1113,7 @@
br_dev, from_dev,
dmac, smac,
dst_ip, src_ip,
- dport, sport);
+ dport, sport, tci, tpid);
dev_put(br_dev);
}
@@ -608,7 +1147,7 @@
br_dev, to_dev,
smac, dmac,
src_ip, dst_ip,
- sport, dport);
+ sport, dport, tci, tpid);
/*
* Sync the return direction (eth2 to ovs-br2)
* All the flow parameters are reversed.
@@ -619,7 +1158,7 @@
to_dev, br_dev,
dmac, smac,
dst_ip, src_ip,
- dport, sport);
+ dport, sport, tci, tpid);
dev_put(br_dev);
}
@@ -818,6 +1357,36 @@
EXPORT_SYMBOL(ecm_classifier_ovs_instance_alloc);
/*
+ * ecm_classifier_ovs_register_callbacks()
+ */
+int ecm_classifier_ovs_register_callbacks(struct ecm_classifier_ovs_callbacks *ovs_cbs)
+{
+ spin_lock_bh(&ecm_classifier_ovs_lock);
+
+ if (unlikely(!ecm_classifier_ovs_enabled)) {
+ spin_unlock_bh(&ecm_classifier_ovs_lock);
+ return -1;
+ }
+
+ ovs.ovs_process = ovs_cbs->ovs_process;
+ spin_unlock_bh(&ecm_classifier_ovs_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(ecm_classifier_ovs_register_callbacks);
+
+/*
+ * ecm_classifier_ovs_unregister_callbacks()
+ */
+void ecm_classifier_ovs_unregister_callbacks(void)
+{
+ spin_lock_bh(&ecm_classifier_ovs_lock);
+ ovs.ovs_process = NULL;
+ spin_unlock_bh(&ecm_classifier_ovs_lock);
+}
+EXPORT_SYMBOL(ecm_classifier_ovs_unregister_callbacks);
+
+/*
* ecm_classifier_ovs_init()
*/
int ecm_classifier_ovs_init(struct dentry *dentry)
diff --git a/ecm_classifier_ovs_public.h b/ecm_classifier_ovs_public.h
new file mode 100644
index 0000000..910d9a5
--- /dev/null
+++ b/ecm_classifier_ovs_public.h
@@ -0,0 +1,52 @@
+/*
+ **************************************************************************
+ * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all copies.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ **************************************************************************
+ */
+
+/*
+ * Result values of the external inspection module to the ECM's ovs classifier.
+ * Based on the result the ovs classifier takes the action for the inspected connection.
+ */
+enum ecm_classifier_ovs_results {
+ ECM_CLASSIFIER_OVS_RESULT_ALLOW_VLAN_ACCEL, /* VLAN process succeeded */
+ ECM_CLASSIFIER_OVS_RESULT_ALLOW_VLAN_QINQ_ACCEL, /* VLAN process succeeded for QinQ */
+ ECM_CLASSIFIER_OVS_RESULT_ALLOW_ACCEL, /* No VLAN present, just accelerate */
+ ECM_CLASSIFIER_OVS_RESULT_DENY_ACCEL_EGRESS, /* Flow egress is not allowed for acceleration */
+ ECM_CLASSIFIER_OVS_RESULT_DENY_ACCEL /* Do not accelerate */
+};
+typedef enum ecm_classifier_ovs_results ecm_classifier_ovs_result_t;
+
+struct ecm_classifier_ovs_process_response {
+ struct vlan_hdr ingress_vlan[2];
+ struct vlan_hdr egress_vlan[2];
+};
+
+
+/*
+ * Callback function which processes the connection information.
+ */
+typedef ecm_classifier_ovs_result_t (*ecm_classifier_ovs_process_callback_t)(struct ovsmgr_dp_flow *flow,
+ struct sk_buff *skb,
+ struct ecm_classifier_ovs_process_response *resp);
+
+struct ecm_classifier_ovs_callbacks {
+ ecm_classifier_ovs_process_callback_t ovs_process;
+};
+
+/*
+ * Register/Unregister callback functions which are called from the external modules.
+ */
+int ecm_classifier_ovs_register_callbacks(struct ecm_classifier_ovs_callbacks *callbacks);
+void ecm_classifier_ovs_unregister_callbacks(void);
+
diff --git a/ecm_interface.c b/ecm_interface.c
index 0ab963a..2ac2e98 100644
--- a/ecm_interface.c
+++ b/ecm_interface.c
@@ -2415,6 +2415,8 @@
DEBUG_TRACE("%p: br_dev = %s, src_addr: " ECM_IP_ADDR_DOT_FMT " dest_addr: " ECM_IP_ADDR_DOT_FMT ", ip_version: %d, protocol: %d (smac:%pM, dmac:%pM)\n",
skb, br_dev->name, ECM_IP_ADDR_TO_DOT(src_ip), ECM_IP_ADDR_TO_DOT(dst_ip), ip_version, protocol, smac, dmac);
+ memset(&flow, 0, sizeof(flow));
+
flow.indev = br_dev;
flow.outdev = NULL;
diff --git a/examples/ecm_ovs.c b/examples/ecm_ovs.c
new file mode 100644
index 0000000..105e443
--- /dev/null
+++ b/examples/ecm_ovs.c
@@ -0,0 +1,134 @@
+/*
+ **************************************************************************
+ * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all copies.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ **************************************************************************
+ */
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/skbuff.h>
+#include <linux/ctype.h>
+
+#include <ovsmgr.h>
+#include "ecm_classifier_ovs_public.h"
+
+/*
+ * This is a test module for the ECM's ovs CLassifier.
+ * It is extracting the VLAN information based on the flow information.
+ */
+
+/*
+ * ecm_ovs_get_ovs()
+ * OVS get callback function registered with ECM.
+ */
+static ecm_classifier_ovs_result_t ecm_ovs_process(struct ovsmgr_dp_flow *flow, struct sk_buff *skb, struct ecm_classifier_ovs_process_response *resp)
+{
+ struct ovsmgr_vlan_info ovi;
+ enum ovsmgr_flow_status status;
+ pr_debug("ecm_ovs_process\n");
+
+ memset((void *)&ovi, 0, sizeof(ovi));
+
+ status = ovsmgr_flow_info_get(flow, skb, &ovi);
+ switch (status) {
+ case OVSMGR_FLOW_STATUS_DENY_ACCEL:
+ case OVSMGR_FLOW_STATUS_UNKNOWN:
+ pr_debug("%p: Deny accelerating the flow\n", flow);
+ return ECM_CLASSIFIER_OVS_RESULT_DENY_ACCEL;
+ case OVSMGR_FLOW_STATUS_DENY_ACCEL_EGRESS:
+ pr_debug("%p: Deny accelerating the flow, egress %s is not allowed\n", flow, flow->outdev->name);
+ return ECM_CLASSIFIER_OVS_RESULT_DENY_ACCEL_EGRESS;
+ case OVSMGR_FLOW_STATUS_ALLOW_VLAN_ACCEL:
+ case OVSMGR_FLOW_STATUS_ALLOW_VLAN_QINQ_ACCEL:
+ pr_debug("%p: Accelerate, VLAN data is valid\n", flow);
+ /*
+ * Outer ingress VLAN
+ */
+ resp->ingress_vlan[0].h_vlan_TCI = ovi.ingress[0].h_vlan_TCI;
+ resp->ingress_vlan[0].h_vlan_encapsulated_proto = ovi.ingress[0].h_vlan_encapsulated_proto;
+
+ /*
+ * Outer egress VLAN
+ */
+ resp->egress_vlan[0].h_vlan_TCI = ovi.egress[0].h_vlan_TCI;
+ resp->egress_vlan[0].h_vlan_encapsulated_proto = ovi.egress[0].h_vlan_encapsulated_proto;
+
+ if (status == OVSMGR_FLOW_STATUS_ALLOW_VLAN_QINQ_ACCEL) {
+ /*
+ * Inner ingress VLAN
+ */
+ resp->ingress_vlan[1].h_vlan_TCI = ovi.ingress[1].h_vlan_TCI;
+ resp->ingress_vlan[1].h_vlan_encapsulated_proto = ovi.ingress[1].h_vlan_encapsulated_proto;
+
+ /*
+ * Inner egress VLAN
+ */
+ resp->egress_vlan[1].h_vlan_TCI = ovi.egress[1].h_vlan_TCI;
+ resp->egress_vlan[1].h_vlan_encapsulated_proto = ovi.egress[1].h_vlan_encapsulated_proto;
+
+ return ECM_CLASSIFIER_OVS_RESULT_ALLOW_VLAN_QINQ_ACCEL;
+ }
+ return ECM_CLASSIFIER_OVS_RESULT_ALLOW_VLAN_ACCEL;
+ case OVSMGR_FLOW_STATUS_ALLOW_ACCEL:
+ return ECM_CLASSIFIER_OVS_RESULT_ALLOW_ACCEL;
+ }
+
+ return ECM_CLASSIFIER_OVS_RESULT_DENY_ACCEL;
+}
+
+static struct ecm_classifier_ovs_callbacks callbacks = {
+ .ovs_process = ecm_ovs_process,
+};
+
+/*
+ * ecm_ovs_init()
+ */
+static int __init ecm_ovs_init(void)
+{
+ int res;
+
+ pr_info("ECM OVS Test INIT\n");
+
+ /*
+ * Register the callbacks with the ECM ovs classifier.
+ */
+ res = ecm_classifier_ovs_register_callbacks(&callbacks);
+ if (res < 0) {
+ pr_warn("Failed to register callbacks for OVS classifier\n");
+ return res;
+ }
+
+ return 0;
+}
+
+/*
+ * ecm_ovs_exit()
+ */
+static void __exit ecm_ovs_exit(void)
+{
+ pr_info("ECM OVS Test EXIT\n");
+
+ /*
+ * Unregister the callbacks.
+ */
+ ecm_classifier_ovs_unregister_callbacks();
+}
+
+module_init(ecm_ovs_init)
+module_exit(ecm_ovs_exit)
+
+MODULE_DESCRIPTION("ECM OVS Test");
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("Dual BSD/GPL");
+#endif
+
diff --git a/frontends/include/ecm_front_end_types.h b/frontends/include/ecm_front_end_types.h
index 5dd33a1..3ba727c 100644
--- a/frontends/include/ecm_front_end_types.h
+++ b/frontends/include/ecm_front_end_types.h
@@ -1,6 +1,6 @@
/*
**************************************************************************
- * Copyright (c) 2014-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2020, The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
@@ -19,6 +19,11 @@
#include <linux/of.h>
/*
+ * Constant used with constructing acceleration rules.
+ */
+#define ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED 0xFFF
+
+/*
* Bridge device macros
*/
#define ecm_front_end_is_bridge_port(dev) (dev && (dev->priv_flags & IFF_BRIDGE_PORT))
diff --git a/frontends/nss/ecm_nss_common.h b/frontends/nss/ecm_nss_common.h
index 5953be3..2f3d949 100644
--- a/frontends/nss/ecm_nss_common.h
+++ b/frontends/nss/ecm_nss_common.h
@@ -1,6 +1,6 @@
/*
**************************************************************************
- * Copyright (c) 2015, 2018-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015, 2018-2020, The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
@@ -31,13 +31,6 @@
#endif
/*
- * Some constants used with constructing NSS acceleration rules.
- * GGG TODO These should be provided by the NSS driver itself!
- */
-#define ECM_NSS_CONNMGR_VLAN_ID_NOT_CONFIGURED 0xFFF
-#define ECM_NSS_CONNMGR_VLAN_MARKING_NOT_CONFIGURED 0xFFFF
-
-/*
* This macro converts ECM ip_addr_t to NSS IPv6 address
*/
#define ECM_IP_ADDR_TO_NSS_IPV6_ADDR(nss6, ipaddrt) \
diff --git a/frontends/nss/ecm_nss_multicast_ipv4.c b/frontends/nss/ecm_nss_multicast_ipv4.c
index c0bfe9c..730ed82 100644
--- a/frontends/nss/ecm_nss_multicast_ipv4.c
+++ b/frontends/nss/ecm_nss_multicast_ipv4.c
@@ -1,6 +1,6 @@
/*
**************************************************************************
- * Copyright (c) 2014-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2020, The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
@@ -484,8 +484,8 @@
return -1;
}
- create->ingress_vlan_tag[0] = ECM_NSS_CONNMGR_VLAN_ID_NOT_CONFIGURED;
- create->ingress_vlan_tag[1] = ECM_NSS_CONNMGR_VLAN_ID_NOT_CONFIGURED;
+ create->ingress_vlan_tag[0] = ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED;
+ create->ingress_vlan_tag[1] = ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED;
/*
* Set the source NSS interface identifier
@@ -522,8 +522,8 @@
*/
for (vif = 0; vif < ECM_DB_MULTICAST_IF_MAX; vif++) {
#ifdef ECM_INTERFACE_VLAN_ENABLE
- create->if_rule[vif].egress_vlan_tag[0] = ECM_NSS_CONNMGR_VLAN_ID_NOT_CONFIGURED;
- create->if_rule[vif].egress_vlan_tag[1] = ECM_NSS_CONNMGR_VLAN_ID_NOT_CONFIGURED;
+ create->if_rule[vif].egress_vlan_tag[0] = ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED;
+ create->if_rule[vif].egress_vlan_tag[1] = ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED;
#endif
/*
@@ -941,8 +941,8 @@
return;
}
- create->ingress_vlan_tag[0] = ECM_NSS_CONNMGR_VLAN_ID_NOT_CONFIGURED;
- create->ingress_vlan_tag[1] = ECM_NSS_CONNMGR_VLAN_ID_NOT_CONFIGURED;
+ create->ingress_vlan_tag[0] = ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED;
+ create->ingress_vlan_tag[1] = ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED;
from_nss_iface = from_ifaces[from_ifaces_first];
from_nss_iface_id = ecm_db_iface_ae_interface_identifier_get(from_nss_iface);
if (from_nss_iface_id < 0) {
@@ -1046,8 +1046,8 @@
to_nss_iface_id = -1;
- create->if_rule[vif].egress_vlan_tag[0] = ECM_NSS_CONNMGR_VLAN_ID_NOT_CONFIGURED;
- create->if_rule[vif].egress_vlan_tag[1] = ECM_NSS_CONNMGR_VLAN_ID_NOT_CONFIGURED;
+ create->if_rule[vif].egress_vlan_tag[0] = ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED;
+ create->if_rule[vif].egress_vlan_tag[1] = ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED;
ii_temp = ecm_db_multicast_if_heirarchy_get(to_ifaces, vif);
to_ii_first = ecm_db_multicast_if_first_get_at_index(to_ifaces_first, vif);
diff --git a/frontends/nss/ecm_nss_multicast_ipv6.c b/frontends/nss/ecm_nss_multicast_ipv6.c
index 5c2b63a..45ff7af 100644
--- a/frontends/nss/ecm_nss_multicast_ipv6.c
+++ b/frontends/nss/ecm_nss_multicast_ipv6.c
@@ -1,6 +1,6 @@
/*
**************************************************************************
- * Copyright (c) 2014-2019 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2020 The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
@@ -496,8 +496,8 @@
return -1;
}
- create->ingress_vlan_tag[0] = ECM_NSS_CONNMGR_VLAN_ID_NOT_CONFIGURED;
- create->ingress_vlan_tag[1] = ECM_NSS_CONNMGR_VLAN_ID_NOT_CONFIGURED;
+ create->ingress_vlan_tag[0] = ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED;
+ create->ingress_vlan_tag[1] = ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED;
/*
* Set the source NSS interface identifier
@@ -529,8 +529,8 @@
*/
for (vif = 0; vif < ECM_DB_MULTICAST_IF_MAX; vif++) {
#ifdef ECM_INTERFACE_VLAN_ENABLE
- create->if_rule[vif].egress_vlan_tag[0] = ECM_NSS_CONNMGR_VLAN_ID_NOT_CONFIGURED;
- create->if_rule[vif].egress_vlan_tag[1] = ECM_NSS_CONNMGR_VLAN_ID_NOT_CONFIGURED;
+ create->if_rule[vif].egress_vlan_tag[0] = ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED;
+ create->if_rule[vif].egress_vlan_tag[1] = ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED;
#endif
/*
* If there is no state change for an interface at this index,
@@ -947,8 +947,8 @@
return;
}
- create->ingress_vlan_tag[0] = ECM_NSS_CONNMGR_VLAN_ID_NOT_CONFIGURED;
- create->ingress_vlan_tag[1] = ECM_NSS_CONNMGR_VLAN_ID_NOT_CONFIGURED;
+ create->ingress_vlan_tag[0] = ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED;
+ create->ingress_vlan_tag[1] = ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED;
from_nss_iface = from_ifaces[from_ifaces_first];
from_nss_iface_id = ecm_db_iface_ae_interface_identifier_get(from_nss_iface);
@@ -1029,8 +1029,8 @@
to_nss_iface_id = -1;
#ifdef ECM_INTERFACE_VLAN_ENABLE
- create->if_rule[vif].egress_vlan_tag[0] = ECM_NSS_CONNMGR_VLAN_ID_NOT_CONFIGURED;
- create->if_rule[vif].egress_vlan_tag[1] = ECM_NSS_CONNMGR_VLAN_ID_NOT_CONFIGURED;
+ create->if_rule[vif].egress_vlan_tag[0] = ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED;
+ create->if_rule[vif].egress_vlan_tag[1] = ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED;
#endif
ii_temp = ecm_db_multicast_if_heirarchy_get(to_ifaces, vif);
diff --git a/frontends/nss/ecm_nss_non_ported_ipv4.c b/frontends/nss/ecm_nss_non_ported_ipv4.c
index 8c4bfb0..46b1a82 100644
--- a/frontends/nss/ecm_nss_non_ported_ipv4.c
+++ b/frontends/nss/ecm_nss_non_ported_ipv4.c
@@ -486,10 +486,10 @@
/*
* Initialize VLAN tag information
*/
- nircm->vlan_primary_rule.ingress_vlan_tag = ECM_NSS_CONNMGR_VLAN_ID_NOT_CONFIGURED;
- nircm->vlan_primary_rule.egress_vlan_tag = ECM_NSS_CONNMGR_VLAN_ID_NOT_CONFIGURED;
- nircm->vlan_secondary_rule.ingress_vlan_tag = ECM_NSS_CONNMGR_VLAN_ID_NOT_CONFIGURED;
- nircm->vlan_secondary_rule.egress_vlan_tag = ECM_NSS_CONNMGR_VLAN_ID_NOT_CONFIGURED;
+ nircm->vlan_primary_rule.ingress_vlan_tag = ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED;
+ nircm->vlan_primary_rule.egress_vlan_tag = ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED;
+ nircm->vlan_secondary_rule.ingress_vlan_tag = ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED;
+ nircm->vlan_secondary_rule.egress_vlan_tag = ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED;
/*
* Get the interface lists of the connection, we must have at least one interface in the list to continue
diff --git a/frontends/nss/ecm_nss_non_ported_ipv6.c b/frontends/nss/ecm_nss_non_ported_ipv6.c
index 92287f0..ee19926 100644
--- a/frontends/nss/ecm_nss_non_ported_ipv6.c
+++ b/frontends/nss/ecm_nss_non_ported_ipv6.c
@@ -404,10 +404,10 @@
/*
* Initialize VLAN tag information
*/
- nircm->vlan_primary_rule.ingress_vlan_tag = ECM_NSS_CONNMGR_VLAN_ID_NOT_CONFIGURED;
- nircm->vlan_primary_rule.egress_vlan_tag = ECM_NSS_CONNMGR_VLAN_ID_NOT_CONFIGURED;
- nircm->vlan_secondary_rule.ingress_vlan_tag = ECM_NSS_CONNMGR_VLAN_ID_NOT_CONFIGURED;
- nircm->vlan_secondary_rule.egress_vlan_tag = ECM_NSS_CONNMGR_VLAN_ID_NOT_CONFIGURED;
+ nircm->vlan_primary_rule.ingress_vlan_tag = ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED;
+ nircm->vlan_primary_rule.egress_vlan_tag = ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED;
+ nircm->vlan_secondary_rule.ingress_vlan_tag = ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED;
+ nircm->vlan_secondary_rule.egress_vlan_tag = ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED;
/*
* Get the interface lists of the connection, we must have at least one interface in the list to continue
diff --git a/frontends/nss/ecm_nss_ported_ipv4.c b/frontends/nss/ecm_nss_ported_ipv4.c
index c453e32..e80294a 100644
--- a/frontends/nss/ecm_nss_ported_ipv4.c
+++ b/frontends/nss/ecm_nss_ported_ipv4.c
@@ -413,10 +413,10 @@
/*
* Initialize VLAN tag information
*/
- nircm->vlan_primary_rule.ingress_vlan_tag = ECM_NSS_CONNMGR_VLAN_ID_NOT_CONFIGURED;
- nircm->vlan_primary_rule.egress_vlan_tag = ECM_NSS_CONNMGR_VLAN_ID_NOT_CONFIGURED;
- nircm->vlan_secondary_rule.ingress_vlan_tag = ECM_NSS_CONNMGR_VLAN_ID_NOT_CONFIGURED;
- nircm->vlan_secondary_rule.egress_vlan_tag = ECM_NSS_CONNMGR_VLAN_ID_NOT_CONFIGURED;
+ nircm->vlan_primary_rule.ingress_vlan_tag = ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED;
+ nircm->vlan_primary_rule.egress_vlan_tag = ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED;
+ nircm->vlan_secondary_rule.ingress_vlan_tag = ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED;
+ nircm->vlan_secondary_rule.egress_vlan_tag = ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED;
/*
* Get the interface lists of the connection, we must have at least one interface in the list to continue
@@ -1021,8 +1021,8 @@
#endif
if (ecm_nss_ipv4_vlan_passthrough_enable && !ecm_db_connection_is_routed_get(feci->ci) &&
- (nircm->vlan_primary_rule.ingress_vlan_tag == ECM_NSS_CONNMGR_VLAN_ID_NOT_CONFIGURED) &&
- (nircm->vlan_primary_rule.egress_vlan_tag == ECM_NSS_CONNMGR_VLAN_ID_NOT_CONFIGURED)) {
+ (nircm->vlan_primary_rule.ingress_vlan_tag == ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED) &&
+ (nircm->vlan_primary_rule.egress_vlan_tag == ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED)) {
int vlan_present = 0;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0))
vlan_present = vlan_tx_tag_present(skb);
@@ -1042,6 +1042,22 @@
}
}
+#ifdef ECM_CLASSIFIER_OVS_ENABLE
+ /*
+ * Copy the both primary and secondary (if exist) VLAN tags.
+ */
+ if (pr->process_actions & ECM_CLASSIFIER_PROCESS_ACTION_OVS_VLAN_TAG) {
+ nircm->vlan_primary_rule.ingress_vlan_tag = pr->ingress_vlan_tag[0];
+ nircm->vlan_primary_rule.egress_vlan_tag = pr->egress_vlan_tag[0];
+ nircm->valid_flags |= NSS_IPV4_RULE_CREATE_VLAN_VALID;
+ }
+
+ if (pr->process_actions & ECM_CLASSIFIER_PROCESS_ACTION_OVS_VLAN_QINQ_TAG) {
+ nircm->vlan_secondary_rule.ingress_vlan_tag = pr->ingress_vlan_tag[1];
+ nircm->vlan_secondary_rule.egress_vlan_tag = pr->egress_vlan_tag[1];
+ }
+#endif
+
protocol = ecm_db_connection_protocol_get(feci->ci);
/*
@@ -2608,6 +2624,24 @@
prevalent_pr.timer_group = aci_pr.timer_group;
}
+#ifdef ECM_CLASSIFIER_OVS_ENABLE
+ if (aci_pr.process_actions & ECM_CLASSIFIER_PROCESS_ACTION_OVS_VLAN_TAG) {
+ DEBUG_TRACE("%p: aci: %p, type: %d, ingress vlan tags 0: %u, egress vlan tags 0: %u\n",
+ ci, aci, aci->type_get(aci), aci_pr.ingress_vlan_tag[0], aci_pr.egress_vlan_tag[0]);
+ prevalent_pr.ingress_vlan_tag[0] = aci_pr.ingress_vlan_tag[0];
+ prevalent_pr.egress_vlan_tag[0] = aci_pr.egress_vlan_tag[0];
+ prevalent_pr.process_actions |= ECM_CLASSIFIER_PROCESS_ACTION_OVS_VLAN_TAG;
+ }
+
+ if (aci_pr.process_actions & ECM_CLASSIFIER_PROCESS_ACTION_OVS_VLAN_QINQ_TAG) {
+ DEBUG_TRACE("%p: aci: %p, type: %d, ingress vlan tags 1: %u, egress vlan tags 1: %u\n",
+ ci, aci, aci->type_get(aci), aci_pr.ingress_vlan_tag[1], aci_pr.egress_vlan_tag[1]);
+ prevalent_pr.ingress_vlan_tag[1] = aci_pr.ingress_vlan_tag[1];
+ prevalent_pr.egress_vlan_tag[1] = aci_pr.egress_vlan_tag[1];
+ prevalent_pr.process_actions |= ECM_CLASSIFIER_PROCESS_ACTION_OVS_VLAN_QINQ_TAG;
+ }
+#endif
+
#ifdef ECM_CLASSIFIER_DSCP_ENABLE
/*
* Qos tag (the last classifier i.e. the highest priority one) will 'win'
diff --git a/frontends/nss/ecm_nss_ported_ipv6.c b/frontends/nss/ecm_nss_ported_ipv6.c
index a56716b..dab55db 100644
--- a/frontends/nss/ecm_nss_ported_ipv6.c
+++ b/frontends/nss/ecm_nss_ported_ipv6.c
@@ -418,10 +418,10 @@
/*
* Initialize VLAN tag information
*/
- nircm->vlan_primary_rule.ingress_vlan_tag = ECM_NSS_CONNMGR_VLAN_ID_NOT_CONFIGURED;
- nircm->vlan_primary_rule.egress_vlan_tag = ECM_NSS_CONNMGR_VLAN_ID_NOT_CONFIGURED;
- nircm->vlan_secondary_rule.ingress_vlan_tag = ECM_NSS_CONNMGR_VLAN_ID_NOT_CONFIGURED;
- nircm->vlan_secondary_rule.egress_vlan_tag = ECM_NSS_CONNMGR_VLAN_ID_NOT_CONFIGURED;
+ nircm->vlan_primary_rule.ingress_vlan_tag = ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED;
+ nircm->vlan_primary_rule.egress_vlan_tag = ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED;
+ nircm->vlan_secondary_rule.ingress_vlan_tag = ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED;
+ nircm->vlan_secondary_rule.egress_vlan_tag = ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED;
/*
* Get the interface lists of the connection, we must have at least one interface in the list to continue
@@ -989,8 +989,8 @@
}
#endif
if (ecm_nss_ipv6_vlan_passthrough_enable && !ecm_db_connection_is_routed_get(feci->ci) &&
- (nircm->vlan_primary_rule.ingress_vlan_tag == ECM_NSS_CONNMGR_VLAN_ID_NOT_CONFIGURED) &&
- (nircm->vlan_primary_rule.egress_vlan_tag == ECM_NSS_CONNMGR_VLAN_ID_NOT_CONFIGURED)) {
+ (nircm->vlan_primary_rule.ingress_vlan_tag == ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED) &&
+ (nircm->vlan_primary_rule.egress_vlan_tag == ECM_FRONT_END_VLAN_ID_NOT_CONFIGURED)) {
int vlan_present = 0;
#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0))
vlan_present = vlan_tx_tag_present(skb);
@@ -1010,6 +1010,21 @@
}
}
+#ifdef ECM_CLASSIFIER_OVS_ENABLE
+ /*
+ * Copy the both primary and secondary (if exist) VLAN tags.
+ */
+ if (pr->process_actions & ECM_CLASSIFIER_PROCESS_ACTION_OVS_VLAN_TAG) {
+ nircm->vlan_primary_rule.ingress_vlan_tag = pr->ingress_vlan_tag[0];
+ nircm->vlan_primary_rule.egress_vlan_tag = pr->egress_vlan_tag[0];
+ nircm->valid_flags |= NSS_IPV6_RULE_CREATE_VLAN_VALID;
+ }
+
+ if (pr->process_actions & ECM_CLASSIFIER_PROCESS_ACTION_OVS_VLAN_QINQ_TAG) {
+ nircm->vlan_secondary_rule.ingress_vlan_tag = pr->ingress_vlan_tag[1];
+ nircm->vlan_secondary_rule.egress_vlan_tag = pr->egress_vlan_tag[1];
+ }
+#endif
protocol = ecm_db_connection_protocol_get(feci->ci);
/*
@@ -2395,6 +2410,24 @@
prevalent_pr.timer_group = aci_pr.timer_group;
}
+#ifdef ECM_CLASSIFIER_OVS_ENABLE
+ if (aci_pr.process_actions & ECM_CLASSIFIER_PROCESS_ACTION_OVS_VLAN_TAG) {
+ DEBUG_TRACE("%p: aci: %p, type: %d, ingress vlan tags 0: %u, egress vlan tags 0: %u\n",
+ ci, aci, aci->type_get(aci), aci_pr.ingress_vlan_tag[0], aci_pr.egress_vlan_tag[0]);
+ prevalent_pr.ingress_vlan_tag[0] = aci_pr.ingress_vlan_tag[0];
+ prevalent_pr.egress_vlan_tag[0] = aci_pr.egress_vlan_tag[0];
+ prevalent_pr.process_actions |= ECM_CLASSIFIER_PROCESS_ACTION_OVS_VLAN_TAG;
+ }
+
+ if (aci_pr.process_actions & ECM_CLASSIFIER_PROCESS_ACTION_OVS_VLAN_QINQ_TAG) {
+ DEBUG_TRACE("%p: aci: %p, type: %d, ingress vlan tags 1: %u, egress vlan tags 1: %u\n",
+ ci, aci, aci->type_get(aci), aci_pr.ingress_vlan_tag[1], aci_pr.egress_vlan_tag[1]);
+ prevalent_pr.ingress_vlan_tag[1] = aci_pr.ingress_vlan_tag[1];
+ prevalent_pr.egress_vlan_tag[1] = aci_pr.egress_vlan_tag[1];
+ prevalent_pr.process_actions |= ECM_CLASSIFIER_PROCESS_ACTION_OVS_VLAN_QINQ_TAG;
+ }
+#endif
+
#ifdef ECM_CLASSIFIER_DSCP_ENABLE
/*
* Qos tag (the last classifier i.e. the highest priority one) will 'win'