blob: 788bec02c1255c90f42754f00a7ead89d563249a [file] [log] [blame]
/*
**************************************************************************
* 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 <linux/etherdevice.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_dump_flow()
* Show OVS flow contents
*/
static void ecm_ovs_dump_flow(struct ovsmgr_dp_flow *flow)
{
pr_debug("OVS DP Flow:\n");
if (flow->indev) {
pr_debug("\tindev: %s\n", flow->indev->name);
} else {
pr_debug("\tindev: NULL\n");
}
if (flow->outdev) {
pr_debug("\toutdev: %s\n", flow->outdev->name);
} else {
pr_debug("\toutdev: NULL\n");
}
pr_debug("\tSMAC: %pM\n", &flow->smac);
pr_debug("\tSMAC: %pM\n", &flow->dmac);
pr_debug("\tingress_vlan: %d:%x\n", flow->ingress_vlan.h_vlan_TCI, flow->ingress_vlan.h_vlan_encapsulated_proto);
pr_debug("\tTuple: V: %d, P: %d, SP: %d, DP: %d\n", flow->tuple.ip_version, flow->tuple.protocol,
flow->tuple.src_port, flow->tuple.dst_port);
if (flow->tuple.ip_version == 4) {
pr_debug("\t\tSIP: %pI4, DIP: %pI4\n", &flow->tuple.ipv4.src, &flow->tuple.ipv4.dst);
} else {
pr_debug("\t\tSIP: %pI6, DIP: %pI6\n", &flow->tuple.ipv6.src, &flow->tuple.ipv6.dst);
}
}
/*
* 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_flow_info ofi;
enum ovsmgr_flow_status status;
pr_debug("ecm_ovs_process\n");
memset((void *)&ofi, 0, sizeof(ofi));
ecm_ovs_dump_flow(flow);
status = ovsmgr_flow_info_get(flow, skb, &ofi);
if ((status == OVSMGR_FLOW_STATUS_DENY_ACCEL) || (status == OVSMGR_FLOW_STATUS_UNKNOWN)) {
pr_debug("%px: Deny accelerating the flow\n", flow);
return ECM_CLASSIFIER_OVS_RESULT_DENY_ACCEL;
}
if (status == OVSMGR_FLOW_STATUS_DENY_ACCEL_EGRESS) {
pr_debug("%px: Deny accelerating the flow, egress %s is not allowed\n", flow, flow->outdev->name);
return ECM_CLASSIFIER_OVS_RESULT_DENY_ACCEL_EGRESS;
}
/*
* get return DSCP value for bridge flow
*/
if (!flow->is_routed) {
struct ovsmgr_flow_info return_ofi;
struct ovsmgr_dp_flow return_flow;
enum ovsmgr_flow_status return_status;
memset((void *)&return_ofi, 0, sizeof(return_ofi));
/*
* Create return flow
*/
return_flow.indev = flow->outdev;
return_flow.outdev = flow->indev;
return_flow.tuple.ip_version = flow->tuple.ip_version;
return_flow.tuple.protocol = flow->tuple.protocol;
ether_addr_copy(return_flow.smac, flow->dmac);
ether_addr_copy(return_flow.dmac, flow->smac);
return_flow.ingress_vlan.h_vlan_TCI = ofi.egress[0].h_vlan_TCI;
return_flow.ingress_vlan.h_vlan_encapsulated_proto = ofi.egress[0].h_vlan_encapsulated_proto;
return_flow.tuple.src_port = flow->tuple.dst_port;
return_flow.tuple.dst_port = flow->tuple.src_port;
if (return_flow.tuple.ip_version == 4) {
return_flow.tuple.ipv4.src = flow->tuple.ipv4.dst;
return_flow.tuple.ipv4.dst = flow->tuple.ipv4.src;
} else {
memcpy(&return_flow.tuple.ipv6.src, &flow->tuple.ipv6.dst, sizeof(return_flow.tuple.ipv6.src));
memcpy(&return_flow.tuple.ipv6.dst, &flow->tuple.ipv6.src, sizeof(return_flow.tuple.ipv6.dst));
}
resp->flow_dscp = ofi.dscp;
ecm_ovs_dump_flow(&return_flow);
return_status = ovsmgr_flow_info_get(&return_flow, skb, &return_ofi);
if ((return_status == OVSMGR_FLOW_STATUS_DENY_ACCEL) || (return_status == OVSMGR_FLOW_STATUS_UNKNOWN)) {
pr_info("%px: Deny accelerating the return flow\n", &return_flow);
goto process_flow;
}
resp->return_dscp = return_ofi.dscp;
}
process_flow:
resp->dscp = ofi.dscp;
switch (status) {
case OVSMGR_FLOW_STATUS_ALLOW_VLAN_ACCEL:
case OVSMGR_FLOW_STATUS_ALLOW_VLAN_QINQ_ACCEL:
pr_debug("%px: Accelerate, VLAN data is valid\n", flow);
/*
* Outer ingress VLAN
*/
resp->ingress_vlan[0].h_vlan_TCI = ofi.ingress[0].h_vlan_TCI;
resp->ingress_vlan[0].h_vlan_encapsulated_proto = ofi.ingress[0].h_vlan_encapsulated_proto;
/*
* Outer egress VLAN
*/
resp->egress_vlan[0].h_vlan_TCI = ofi.egress[0].h_vlan_TCI;
resp->egress_vlan[0].h_vlan_encapsulated_proto = ofi.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 = ofi.ingress[1].h_vlan_TCI;
resp->ingress_vlan[1].h_vlan_encapsulated_proto = ofi.ingress[1].h_vlan_encapsulated_proto;
/*
* Inner egress VLAN
*/
resp->egress_vlan[1].h_vlan_TCI = ofi.egress[1].h_vlan_TCI;
resp->egress_vlan[1].h_vlan_encapsulated_proto = ofi.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;
default:
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