blob: d054d8eb1cf81257d807ff013b3a7379ccba1aae [file] [log] [blame]
/*
**************************************************************************
* Copyright (c) 2018, 2020-2021, 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.
**************************************************************************
*/
/*
* Mark Classifier.
* This module provides an interface to customer's external module to interract with the ECM.
* It allows the customer to make smart decisions on the packets before offloading the connection to the
* acceleration engine.
*/
#include <linux/version.h>
#include <linux/types.h>
#include <linux/ip.h>
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/debugfs.h>
#include <linux/ctype.h>
#include <net/tcp.h>
#include <net/ipv6.h>
#include <linux/inet.h>
#include <linux/in.h>
#include <linux/etherdevice.h>
/*
* Debug output levels
* 0 = OFF
* 1 = ASSERTS / ERRORS
* 2 = 1 + WARN
* 3 = 2 + INFO
* 4 = 3 + TRACE
*/
#define DEBUG_LEVEL ECM_CLASSIFIER_MARK_DEBUG_LEVEL
#include "ecm_types.h"
#include "ecm_db_types.h"
#include "ecm_state.h"
#include "ecm_tracker.h"
#include "ecm_classifier.h"
#include "ecm_front_end_types.h"
#include "ecm_tracker_datagram.h"
#include "ecm_tracker_udp.h"
#include "ecm_tracker_tcp.h"
#include "ecm_db.h"
#include "ecm_classifier_mark.h"
#include "ecm_classifier_mark_public.h"
#include "ecm_front_end_common.h"
#include "ecm_front_end_ipv4.h"
#ifdef ECM_IPV6_ENABLE
#include "ecm_front_end_ipv6.h"
#endif
/*
* Magic numbers
*/
#define ECM_CLASSIFIER_MARK_INSTANCE_MAGIC 0x2567
/*
* struct ecm_classifier_mark_instance
* State per connection for MARK classifier
*/
struct ecm_classifier_mark_instance {
struct ecm_classifier_instance base; /* Base type */
uint32_t ci_serial; /* RO: Serial of the connection */
struct ecm_classifier_mark_instance *next; /* Next classifier state instance (for accouting and reporting purposes) */
struct ecm_classifier_mark_instance *prev; /* Next classifier state instance (for accouting and reporting purposes) */
uint32_t mark; /* Mark value which this classifier extracted from the buffer */
struct ecm_classifier_process_response process_response;
/* Last process response computed */
int refs; /* Integer to trap we never go negative */
#if (DEBUG_LEVEL > 0)
uint16_t magic;
#endif
};
/*
* Operational control. By default it is disabled.
*/
static int ecm_classifier_mark_enabled; /* Operational behaviour */
/*
* Management thread control
*/
static bool ecm_classifier_mark_terminate_pending; /* True when the user wants us to terminate */
/*
* Debugfs dentry object.
*/
static struct dentry *ecm_classifier_mark_dentry;
/*
* Locking of the classifier structures
*/
static DEFINE_SPINLOCK(ecm_classifier_mark_lock); /* Protect SMP access. */
/*
* List of our classifier instances
*/
static struct ecm_classifier_mark_instance *ecm_classifier_mark_instances = NULL;
/* list of all active instances */
static int ecm_classifier_mark_count = 0; /* Tracks number of instances allocated */
/*
* Callbacks to the external modules.
*/
ecm_classifier_mark_get_callback_t mark_get_cb[ECM_CLASSIFIER_MARK_TYPE_MAX] = { NULL };
/* Callbacks which gets the mark from the packets */
ecm_classifier_mark_sync_to_ipv4_callback_t sync_to_ipv4_cb[ECM_CLASSIFIER_MARK_TYPE_MAX] = { NULL };
/* Callbacks which syncs the IPv4 stats to the registered external subsystems */
ecm_classifier_mark_sync_to_ipv6_callback_t sync_to_ipv6_cb[ECM_CLASSIFIER_MARK_TYPE_MAX] = { NULL };
/* Callbacks which syncs the IPv6 stats to the registered external subsystems */
/*
* _ecm_classifier_mark_ref()
* Ref
*/
static void _ecm_classifier_mark_ref(struct ecm_classifier_mark_instance *ecmi)
{
ecmi->refs++;
DEBUG_TRACE("%px: ecmi ref %d\n", ecmi, ecmi->refs);
DEBUG_ASSERT(ecmi->refs > 0, "%px: ref wrap\n", ecmi);
}
/*
* ecm_classifier_mark_ref()
* Ref
*/
static void ecm_classifier_mark_ref(struct ecm_classifier_instance *ci)
{
struct ecm_classifier_mark_instance *ecmi;
ecmi = (struct ecm_classifier_mark_instance *)ci;
DEBUG_CHECK_MAGIC(ecmi, ECM_CLASSIFIER_MARK_INSTANCE_MAGIC, "%px: magic failed", ecmi);
spin_lock_bh(&ecm_classifier_mark_lock);
_ecm_classifier_mark_ref(ecmi);
spin_unlock_bh(&ecm_classifier_mark_lock);
}
/*
* ecm_classifier_mark_deref()
* Deref
*/
static int ecm_classifier_mark_deref(struct ecm_classifier_instance *ci)
{
struct ecm_classifier_mark_instance *ecmi;
ecmi = (struct ecm_classifier_mark_instance *)ci;
DEBUG_CHECK_MAGIC(ecmi, ECM_CLASSIFIER_MARK_INSTANCE_MAGIC, "%px: magic failed", ecmi);
spin_lock_bh(&ecm_classifier_mark_lock);
ecmi->refs--;
DEBUG_ASSERT(ecmi->refs >= 0, "%px: refs wrapped\n", ecmi);
DEBUG_TRACE("%px: Mark classifier deref %d\n", ecmi, ecmi->refs);
if (ecmi->refs) {
int refs = ecmi->refs;
spin_unlock_bh(&ecm_classifier_mark_lock);
return refs;
}
/*
* Object to be destroyed
*/
ecm_classifier_mark_count--;
DEBUG_ASSERT(ecm_classifier_mark_count >= 0, "%px: ecm_classifier_mark_count wrap\n", ecmi);
/*
* UnLink the instance from our list
*/
if (ecmi->next) {
ecmi->next->prev = ecmi->prev;
}
if (ecmi->prev) {
ecmi->prev->next = ecmi->next;
} else {
DEBUG_ASSERT(ecm_classifier_mark_instances == ecmi, "%px: list bad %px\n", ecmi, ecm_classifier_mark_instances);
ecm_classifier_mark_instances = ecmi->next;
}
ecmi->next = NULL;
ecmi->prev = NULL;
spin_unlock_bh(&ecm_classifier_mark_lock);
/*
* Final
*/
DEBUG_INFO("%px: Final mark classifier instance\n", ecmi);
kfree(ecmi);
return 0;
}
/*
* ecm_classifier_mark_process()
* Process new packet
*
* NOTE: This function would only ever be called if all other classifiers have failed.
*/
static void ecm_classifier_mark_process(struct ecm_classifier_instance *aci, ecm_tracker_sender_type_t sender,
struct ecm_tracker_ip_header *ip_hdr, struct sk_buff *skb,
struct ecm_classifier_process_response *process_response)
{
struct ecm_classifier_mark_instance *ecmi = (struct ecm_classifier_mark_instance *)aci;
ecm_classifier_mark_result_t result;
struct ecm_db_connection_instance *ci;
bool skb_header_adjusted = false;
uint32_t l2_encap_len;
uint16_t l2_encap_proto;
uint16_t skb_proto;
uint32_t mark = 0;
ecm_classifier_mark_get_callback_t cb = NULL;
ecm_classifier_mark_type_t type = ECM_CLASSIFIER_MARK_TYPE_L2_ENCAP;
/* TODO: By default we use this type. In the future the type will be gotten from the connection instance*/
DEBUG_CHECK_MAGIC(ecmi, ECM_CLASSIFIER_MARK_INSTANCE_MAGIC, "%px: invalid state magic\n", ecmi);
/*
* Not relevant to the connection if not enabled.
*/
if (unlikely(!ecm_classifier_mark_enabled)) {
/*
* Not relevant.
*/
DEBUG_WARN("%px: Mark classifier is not enabled.\n", aci);
goto not_relevant;
}
/*
* Get connection
*/
ci = ecm_db_connection_serial_find_and_ref(ecmi->ci_serial);
if (!ci) {
/*
* Connection has gone from under us
*/
DEBUG_WARN("%px: connection instance gone while processing classifier.\n", aci);
goto not_relevant;
}
/*
* Get the L2 encap protocol of the connection.
*/
l2_encap_proto = ecm_db_connection_l2_encap_proto_get(ci);
if (!l2_encap_proto) {
/*
* The caller is not interested in the inspection. Not relevant.
*/
DEBUG_WARN("%px: L2 encap protocol is zero.\n", aci);
ecm_db_connection_deref(ci);
goto not_relevant;
}
/*
* Is there an external callback to get the mark value from the packet?
*/
rcu_read_lock();
cb = rcu_dereference(mark_get_cb[type]);
if (!cb) {
/*
* Not relevant.
*/
rcu_read_unlock();
DEBUG_WARN("%px: No inspection callback set with type: %d.\n", aci, type);
ecm_db_connection_deref(ci);
goto not_relevant;
}
/*
* Before giving the skb to the external callback for getting the mark value,
* let's adjust the L2 encap header.
* If the skb's protocol is not equal to the connection's L2 encap protocol,
* this means the skb's L2 header is pulled off. So, let's push it back.
*/
if (l2_encap_proto != ntohs(skb->protocol)) {
DEBUG_TRACE("%px: Connections L2 encap protocol: %x is different than skb's protocol: %x, need header adjustment.\n",
aci, l2_encap_proto, ntohs(skb->protocol));
l2_encap_len = ecm_front_end_l2_encap_header_len(l2_encap_proto);
skb_header_adjusted = true;
ecm_front_end_push_l2_encap_header(skb, l2_encap_len);
skb_proto = skb->protocol;
skb->protocol = htons(l2_encap_proto);
}
/*
* Call the external callback and get the result.
*/
result = cb(skb, &mark);
rcu_read_unlock();
/*
* Pull the skb's L2 header, if it is adjusted in this classifier.
*/
if (skb_header_adjusted) {
DEBUG_TRACE("%px: Adjust the L2 header back.\n", aci);
ecm_front_end_pull_l2_encap_header(skb, l2_encap_len);
skb->protocol = skb_proto;
}
/*
* Handle the result
*/
switch (result) {
case ECM_CLASSIFIER_MARK_RESULT_NOT_YET:
/*
* Deny accel but don't change the permit state - we try again later
*/
DEBUG_WARN("%px: External callback has not decided yet.\n", aci);
goto deny_accel;
case ECM_CLASSIFIER_MARK_RESULT_NOT_RELEVANT:
/*
* The callback told us that the classifier is not relevant to this connection.
*/
DEBUG_WARN("%px: External callback decided not relevant.\n", aci);
ecm_db_connection_deref(ci);
goto not_relevant;
case ECM_CLASSIFIER_MARK_RESULT_SUCCESS:
/*
* Copy the returned mark value to the connection and the classifier instance.
* We copy the mark value to the classifier instance as well here for quick access
* in the sync functions.
*/
DEBUG_WARN("%px: External callback returned the mark value: %x\n", aci, mark);
ecm_db_connection_mark_set(ci, mark);
ecmi->mark = mark;
break;
default:
DEBUG_ASSERT(false, "Unhandled result: %d\n", result);
}
/*
* Acceleration is permitted
*/
spin_lock_bh(&ecm_classifier_mark_lock);
ecmi->process_response.relevance = ECM_CLASSIFIER_RELEVANCE_YES;
ecmi->process_response.process_actions = ECM_CLASSIFIER_PROCESS_ACTION_ACCEL_MODE;
ecmi->process_response.accel_mode = ECM_CLASSIFIER_ACCELERATION_MODE_ACCEL;
*process_response = ecmi->process_response;
spin_unlock_bh(&ecm_classifier_mark_lock);
ecm_db_connection_deref(ci);
return;
not_relevant:
/*
* ecm_classifier_mark_lock MUST be held
*/
spin_lock_bh(&ecm_classifier_mark_lock);
ecmi->process_response.relevance = ECM_CLASSIFIER_RELEVANCE_NO;
*process_response = ecmi->process_response;
spin_unlock_bh(&ecm_classifier_mark_lock);
return;
deny_accel:
/*
* ecm_classifier_mark_lock MUST be held
*/
spin_lock_bh(&ecm_classifier_mark_lock);
ecmi->process_response.relevance = ECM_CLASSIFIER_RELEVANCE_YES;
ecmi->process_response.process_actions = ECM_CLASSIFIER_PROCESS_ACTION_ACCEL_MODE;
ecmi->process_response.accel_mode = ECM_CLASSIFIER_ACCELERATION_MODE_NO;
*process_response = ecmi->process_response;
spin_unlock_bh(&ecm_classifier_mark_lock);
ecm_db_connection_deref(ci);
}
/*
* ecm_classifier_mark_type_get()
* Get type of classifier this is
*/
static ecm_classifier_type_t ecm_classifier_mark_type_get(struct ecm_classifier_instance *aci)
{
struct ecm_classifier_mark_instance *ecmi;
ecmi = (struct ecm_classifier_mark_instance *)aci;
DEBUG_CHECK_MAGIC(ecmi, ECM_CLASSIFIER_MARK_INSTANCE_MAGIC, "%px: magic failed", ecmi);
return ECM_CLASSIFIER_TYPE_MARK;
}
/*
* ecm_classifier_mark_reclassify_allowed()
* Get whether reclassification is allowed
*/
static bool ecm_classifier_mark_reclassify_allowed(struct ecm_classifier_instance *aci)
{
struct ecm_classifier_mark_instance *ecmi;
ecmi = (struct ecm_classifier_mark_instance *)aci;
DEBUG_CHECK_MAGIC(ecmi, ECM_CLASSIFIER_MARK_INSTANCE_MAGIC, "%px: magic failed", ecmi);
return true;
}
/*
* ecm_classifier_mark_reclassify()
* Reclassify
*/
static void ecm_classifier_mark_reclassify(struct ecm_classifier_instance *aci)
{
struct ecm_classifier_mark_instance *ecmi;
ecmi = (struct ecm_classifier_mark_instance *)aci;
DEBUG_CHECK_MAGIC(ecmi, ECM_CLASSIFIER_MARK_INSTANCE_MAGIC, "%px: magic failed", ecmi);
/*
* Revert back to MAYBE relevant - we will evaluate when we get the next process() call.
*/
spin_lock_bh(&ecm_classifier_mark_lock);
ecmi->process_response.relevance = ECM_CLASSIFIER_RELEVANCE_MAYBE;
spin_unlock_bh(&ecm_classifier_mark_lock);
}
/*
* ecm_classifier_mark_last_process_response_get()
* Get result code returned by the last process call
*/
static void ecm_classifier_mark_last_process_response_get(struct ecm_classifier_instance *aci,
struct ecm_classifier_process_response *process_response)
{
struct ecm_classifier_mark_instance *ecmi;
ecmi = (struct ecm_classifier_mark_instance *)aci;
DEBUG_CHECK_MAGIC(ecmi, ECM_CLASSIFIER_MARK_INSTANCE_MAGIC, "%px: magic failed", ecmi);
spin_lock_bh(&ecm_classifier_mark_lock);
*process_response = ecmi->process_response;
spin_unlock_bh(&ecm_classifier_mark_lock);
}
/*
* ecm_classifier_mark_sync_to_v4()
* Front end is pushing accel engine state to us
*/
static void ecm_classifier_mark_sync_to_v4(struct ecm_classifier_instance *aci, struct ecm_classifier_rule_sync *sync)
{
int protocol;
int src_port;
int dst_port;
ip_addr_t src_ip;
ip_addr_t dst_ip;
__be32 src_ip4;
__be32 dest_ip4;
struct ecm_db_connection_instance *ci;
ecm_classifier_mark_sync_to_ipv4_callback_t cb = NULL;
ecm_classifier_mark_type_t type = ECM_CLASSIFIER_MARK_TYPE_L2_ENCAP;
/* TODO: By default we use this type. In the future the type will be gotten from the connection instance */
struct ecm_classifier_mark_instance *ecmi = (struct ecm_classifier_mark_instance *)aci;
DEBUG_CHECK_MAGIC(ecmi, ECM_CLASSIFIER_MARK_INSTANCE_MAGIC, "%px: magic failed", ecmi);
/*
* Nothing to update.
* We only care about flows that are actively being accelerated.
*/
if (!(sync->tx_packet_count[ECM_CONN_DIR_FLOW] || sync->tx_packet_count[ECM_CONN_DIR_RETURN])) {
return;
}
/*
* Handle only the stats sync. We don't care about the evict or flush syncs.
*/
if (sync->reason != ECM_FRONT_END_IPV4_RULE_SYNC_REASON_STATS) {
return;
}
ci = ecm_db_connection_serial_find_and_ref(ecmi->ci_serial);
if (!ci) {
DEBUG_TRACE("%px: No ci found for %u\n", ecmi, ecmi->ci_serial);
return;
}
protocol = ecm_db_connection_protocol_get(ci);
src_port = htons(ecm_db_connection_port_get(ci, ECM_DB_OBJ_DIR_FROM));
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_deref(ci);
ECM_IP_ADDR_TO_NIN4_ADDR(src_ip4, src_ip);
ECM_IP_ADDR_TO_NIN4_ADDR(dest_ip4, dst_ip);
rcu_read_lock();
cb = rcu_dereference(sync_to_ipv4_cb[type]);
if (!cb) {
rcu_read_unlock();
DEBUG_WARN("%px: IPv4 mark sync callback is not set\n", ecmi);
return;
}
cb(ecmi->mark, src_ip4, src_port, dest_ip4, dst_port, protocol);
rcu_read_unlock();
}
/*
* ecm_classifier_mark_sync_from_v4()
* Front end is retrieving accel engine state from us
*/
static void ecm_classifier_mark_sync_from_v4(struct ecm_classifier_instance *aci, struct ecm_classifier_rule_create *ecrc)
{
struct ecm_classifier_mark_instance *ecmi __attribute__((unused));
ecmi = (struct ecm_classifier_mark_instance *)aci;
DEBUG_CHECK_MAGIC(ecmi, ECM_CLASSIFIER_MARK_INSTANCE_MAGIC, "%px: magic failed", ecmi);
}
/*
* ecm_classifier_mark_sync_to_v6()
* Front end is pushing accel engine state to us
*/
static void ecm_classifier_mark_sync_to_v6(struct ecm_classifier_instance *aci, struct ecm_classifier_rule_sync *sync)
{
int protocol;
int src_port;
int dst_port;
ip_addr_t src_ip;
ip_addr_t dst_ip;
struct in6_addr src_ip6;
struct in6_addr dest_ip6;
struct ecm_db_connection_instance *ci;
ecm_classifier_mark_sync_to_ipv6_callback_t cb = NULL;
ecm_classifier_mark_type_t type = ECM_CLASSIFIER_MARK_TYPE_L2_ENCAP;
/* TODO: By default we use this type. In the future the type will be gotten from the connection instance */
struct ecm_classifier_mark_instance *ecmi = (struct ecm_classifier_mark_instance *)aci;
DEBUG_CHECK_MAGIC(ecmi, ECM_CLASSIFIER_MARK_INSTANCE_MAGIC, "%px: magic failed", ecmi);
/*
* Nothing to update.
* We only care about flows that are actively being accelerated.
*/
if (!(sync->tx_packet_count[ECM_CONN_DIR_FLOW] || sync->tx_packet_count[ECM_CONN_DIR_RETURN])) {
return;
}
/*
* Handle only the stats sync. We don't care about the evict or flush syncs.
*/
if (sync->reason != ECM_FRONT_END_IPV4_RULE_SYNC_REASON_STATS) {
return;
}
ci = ecm_db_connection_serial_find_and_ref(ecmi->ci_serial);
if (!ci) {
DEBUG_TRACE("%px: No ci found for %u\n", ecmi, ecmi->ci_serial);
return;
}
protocol = ecm_db_connection_protocol_get(ci);
src_port = htons(ecm_db_connection_port_get(ci, ECM_DB_OBJ_DIR_FROM));
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_deref(ci);
ECM_IP_ADDR_TO_NIN6_ADDR(src_ip6, src_ip);
ECM_IP_ADDR_TO_NIN6_ADDR(dest_ip6, dst_ip);
rcu_read_lock();
cb = rcu_dereference(sync_to_ipv6_cb[type]);
if (!cb) {
rcu_read_unlock();
DEBUG_WARN("%px: IPv6 mark sync callback is not set\n", ecmi);
return;
}
cb(ecmi->mark, &src_ip6, src_port, &dest_ip6, dst_port, protocol);
rcu_read_unlock();
}
/*
* ecm_classifier_mark_sync_from_v6()
* Front end is retrieving accel engine state from us
*/
static void ecm_classifier_mark_sync_from_v6(struct ecm_classifier_instance *aci, struct ecm_classifier_rule_create *ecrc)
{
struct ecm_classifier_mark_instance *ecmi __attribute__((unused));
ecmi = (struct ecm_classifier_mark_instance *)aci;
DEBUG_CHECK_MAGIC(ecmi, ECM_CLASSIFIER_MARK_INSTANCE_MAGIC, "%px: magic failed", ecmi);
}
#ifdef ECM_STATE_OUTPUT_ENABLE
/*
* ecm_classifier_mark_state_get()
* Gets the state of this classfier and outputs it to debugfs.
*/
static int ecm_classifier_mark_state_get(struct ecm_classifier_instance *ci, struct ecm_state_file_instance *sfi)
{
int result;
struct ecm_classifier_mark_instance *ecmi;
struct ecm_classifier_process_response process_response;
uint32_t mark;
ecmi = (struct ecm_classifier_mark_instance *)ci;
DEBUG_CHECK_MAGIC(ecmi, ECM_CLASSIFIER_MARK_INSTANCE_MAGIC, "%px: magic failed", ecmi);
if ((result = ecm_state_prefix_add(sfi, "mark"))) {
return result;
}
spin_lock_bh(&ecm_classifier_mark_lock);
mark = ecmi->mark;
process_response = ecmi->process_response;
spin_unlock_bh(&ecm_classifier_mark_lock);
if ((result = ecm_state_write(sfi, "value", "%d", mark))) {
return result;
}
/*
* Output our last process response
*/
if ((result = ecm_classifier_process_response_state_get(sfi, &process_response))) {
return result;
}
return ecm_state_prefix_remove(sfi);
}
#endif
/*
* ecm_classifier_mark_instance_alloc()
* Allocate an instance of the mark classifier
*/
struct ecm_classifier_mark_instance *ecm_classifier_mark_instance_alloc(struct ecm_db_connection_instance *ci)
{
struct ecm_classifier_mark_instance *ecmi;
/*
* Allocate the instance
*/
ecmi = (struct ecm_classifier_mark_instance *)kzalloc(sizeof(struct ecm_classifier_mark_instance), GFP_ATOMIC | __GFP_NOWARN);
if (!ecmi) {
DEBUG_WARN("i%px: Failed to allocate Mark Classifier instance\n", ci);
return NULL;
}
DEBUG_SET_MAGIC(ecmi, ECM_CLASSIFIER_MARK_INSTANCE_MAGIC);
ecmi->refs = 1;
/*
* Methods generic to all classifiers.
*/
ecmi->base.process = ecm_classifier_mark_process;
ecmi->base.sync_from_v4 = ecm_classifier_mark_sync_from_v4;
ecmi->base.sync_to_v4 = ecm_classifier_mark_sync_to_v4;
ecmi->base.sync_from_v6 = ecm_classifier_mark_sync_from_v6;
ecmi->base.sync_to_v6 = ecm_classifier_mark_sync_to_v6;
ecmi->base.type_get = ecm_classifier_mark_type_get;
ecmi->base.reclassify_allowed = ecm_classifier_mark_reclassify_allowed;
ecmi->base.reclassify = ecm_classifier_mark_reclassify;
ecmi->base.last_process_response_get = ecm_classifier_mark_last_process_response_get;
#ifdef ECM_STATE_OUTPUT_ENABLE
ecmi->base.state_get = ecm_classifier_mark_state_get;
#endif
ecmi->base.ref = ecm_classifier_mark_ref;
ecmi->base.deref = ecm_classifier_mark_deref;
ecmi->ci_serial = ecm_db_connection_serial_get(ci);
ecmi->process_response.process_actions = 0;
ecmi->process_response.relevance = ECM_CLASSIFIER_RELEVANCE_MAYBE;
/*
* Final check if we are pending termination
*/
spin_lock_bh(&ecm_classifier_mark_lock);
if (ecm_classifier_mark_terminate_pending) {
spin_unlock_bh(&ecm_classifier_mark_lock);
DEBUG_WARN("%px: Terminating\n", ci);
kfree(ecmi);
return NULL;
}
/*
* Link the new instance into our list at the head
*/
ecmi->next = ecm_classifier_mark_instances;
if (ecm_classifier_mark_instances) {
ecm_classifier_mark_instances->prev = ecmi;
}
ecm_classifier_mark_instances = ecmi;
/*
* Increment stats
*/
ecm_classifier_mark_count++;
DEBUG_ASSERT(ecm_classifier_mark_count > 0, "%px: ecm_classifier_mark_count wrap for instance: %px\n", ci, ecmi);
spin_unlock_bh(&ecm_classifier_mark_lock);
DEBUG_INFO("%px: Mark classifier instance alloc: %px\n", ci, ecmi);
return ecmi;
}
EXPORT_SYMBOL(ecm_classifier_mark_instance_alloc);
/*
* ecm_classifier_mark_register_callbacks()
* Register external callbacks.
*/
int ecm_classifier_mark_register_callbacks(ecm_classifier_mark_type_t type,
ecm_classifier_mark_get_callback_t mark_get,
ecm_classifier_mark_sync_to_ipv4_callback_t sync_to_ipv4,
ecm_classifier_mark_sync_to_ipv6_callback_t sync_to_ipv6)
{
spin_lock_bh(&ecm_classifier_mark_lock);
if (ecm_classifier_mark_terminate_pending) {
spin_unlock_bh(&ecm_classifier_mark_lock);
DEBUG_WARN("Terminating\n");
return -1;
}
spin_unlock_bh(&ecm_classifier_mark_lock);
DEBUG_ASSERT(!mark_get_cb[type] && !sync_to_ipv4_cb[type] && !sync_to_ipv6_cb[type],
"Mark callbacks are already registered\n");
rcu_assign_pointer(mark_get_cb[type], mark_get);
rcu_assign_pointer(sync_to_ipv4_cb[type], sync_to_ipv4);
rcu_assign_pointer(sync_to_ipv6_cb[type], sync_to_ipv6);
return 0;
}
EXPORT_SYMBOL(ecm_classifier_mark_register_callbacks);
/*
* ecm_classifier_mark_unregister_callbacks()
* Unregister external callbacks.
*/
void ecm_classifier_mark_unregister_callbacks(ecm_classifier_mark_type_t type)
{
rcu_assign_pointer(mark_get_cb[type], NULL);
rcu_assign_pointer(sync_to_ipv4_cb[type], NULL);
rcu_assign_pointer(sync_to_ipv6_cb[type], NULL);
}
EXPORT_SYMBOL(ecm_classifier_mark_unregister_callbacks);
/*
* ecm_classifier_mark_init()
*/
int ecm_classifier_mark_init(struct dentry *dentry)
{
DEBUG_INFO("Mark classifier Module init\n");
ecm_classifier_mark_dentry = debugfs_create_dir("ecm_classifier_mark", dentry);
if (!ecm_classifier_mark_dentry) {
DEBUG_ERROR("Failed to create ecm mark directory in debugfs\n");
return -1;
}
if (!debugfs_create_u32("enabled", S_IRUGO | S_IWUSR, ecm_classifier_mark_dentry,
(u32 *)&ecm_classifier_mark_enabled)) {
DEBUG_ERROR("Failed to create mark enabled file in debugfs\n");
debugfs_remove_recursive(ecm_classifier_mark_dentry);
return -1;
}
return 0;
}
EXPORT_SYMBOL(ecm_classifier_mark_init);
/*
* ecm_classifier_mark_exit()
*/
void ecm_classifier_mark_exit(void)
{
DEBUG_INFO("Mark classifier Module exit\n");
spin_lock_bh(&ecm_classifier_mark_lock);
ecm_classifier_mark_terminate_pending = true;
spin_unlock_bh(&ecm_classifier_mark_lock);
/*
* Remove the debugfs files recursively.
*/
if (ecm_classifier_mark_dentry) {
debugfs_remove_recursive(ecm_classifier_mark_dentry);
}
}
EXPORT_SYMBOL(ecm_classifier_mark_exit);