blob: 6a3a97c18fc7a7503aa2c9662fae08db6dfc725b [file] [log] [blame]
/*
**************************************************************************
* Copyright (c) 2014, 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/types.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/icmp.h>
#include <linux/sysctl.h>
#include <linux/kthread.h>
#include <linux/sysdev.h>
#include <linux/fs.h>
#include <linux/pkt_sched.h>
#include <linux/string.h>
#include <net/route.h>
#include <net/ip.h>
#include <net/tcp.h>
#include <asm/unaligned.h>
#include <asm/uaccess.h> /* for put_user */
#include <net/ipv6.h>
#include <linux/inet.h>
#include <linux/in.h>
#include <linux/udp.h>
#include <linux/tcp.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_bridge.h>
#include <net/netfilter/nf_conntrack.h>
#include <net/netfilter/nf_conntrack_helper.h>
#include <net/netfilter/nf_conntrack_l4proto.h>
#include <net/netfilter/nf_conntrack_l3proto.h>
#include <net/netfilter/nf_conntrack_core.h>
#include <net/netfilter/ipv4/nf_conntrack_ipv4.h>
#include <net/netfilter/ipv4/nf_defrag_ipv4.h>
/*
* Debug output levels
* 0 = OFF
* 1 = ASSERTS / ERRORS
* 2 = 1 + WARN
* 3 = 2 + INFO
* 4 = 3 + TRACE
*/
#define DEBUG_LEVEL ECM_DB_DEBUG_LEVEL
#include <nss_api_if.h>
#include "ecm_types.h"
#include "ecm_db_types.h"
#include "ecm_tracker.h"
#include "ecm_classifier.h"
#include "ecm_front_end_types.h"
#include "ecm_classifier_default.h"
#include "ecm_db.h"
/*
* Magic numbers
*/
#define ECM_DB_CONNECTION_INSTANCE_MAGIC 0xff23
#define ECM_DB_HOST_INSTANCE_MAGIC 0x2873
#define ECM_DB_MAPPING_INSTANCE_MAGIC 0x8765
#define ECM_DB_LISTENER_INSTANCE_MAGIC 0x9876
#define ECM_DB_NODE_INSTANCE_MAGIC 0x3312
#define ECM_DB_IFACE_INSTANCE_MAGIC 0xAEF1
#define ECM_DB_STATE_FILE_INSTANCE_MAGIC 0xB3FE
/*
* Global lists.
* All instances are inserted into global list - this allows easy iteration of all instances of a particular type.
* The list is doubly linked for fast removal. The list is in no particular order.
*/
struct ecm_db_connection_instance *ecm_db_connections = NULL;
struct ecm_db_mapping_instance *ecm_db_mappings = NULL;
struct ecm_db_host_instance *ecm_db_hosts = NULL;
struct ecm_db_node_instance *ecm_db_nodes = NULL;
struct ecm_db_iface_instance *ecm_db_interfaces = NULL;
/*
* Connection hash table
*/
#define ECM_DB_CONNECTION_HASH_SLOTS 32768
static struct ecm_db_connection_instance *ecm_db_connection_table[ECM_DB_CONNECTION_HASH_SLOTS];
/* Slots of the connection hash table */
static int ecm_db_connection_table_lengths[ECM_DB_CONNECTION_HASH_SLOTS];
/* Tracks how long each chain is */
static int ecm_db_connection_count = 0; /* Number of connections allocated */
static int ecm_db_connection_serial = 0; /* Serial number - ensures each connection has a unique serial number.
* Serial numbers are used mainly by classifiers that keep their own state
* and can 'link' their state to the right connection using a serial number.
* In the XML state files a key can be set up on serial for fast association between
* state data.
* The serial number is also used as a soft linkage to other subsystems such as NA.
*/
typedef uint32_t ecm_db_connection_hash_t;
/*
* Connection serial number hash table
*/
#define ECM_DB_CONNECTION_SERIAL_HASH_SLOTS 32768
static struct ecm_db_connection_instance *ecm_db_connection_serial_table[ECM_DB_CONNECTION_SERIAL_HASH_SLOTS];
/* Slots of the connection serial hash table */
static int ecm_db_connection_serial_table_lengths[ECM_DB_CONNECTION_SERIAL_HASH_SLOTS];
/* Tracks how long each chain is */
typedef uint32_t ecm_db_connection_serial_hash_t;
/*
* Mapping hash table
*/
#define ECM_DB_MAPPING_HASH_SLOTS 32768
static struct ecm_db_mapping_instance *ecm_db_mapping_table[ECM_DB_MAPPING_HASH_SLOTS];
/* Slots of the mapping hash table */
static int ecm_db_mapping_table_lengths[ECM_DB_MAPPING_HASH_SLOTS];
/* Tracks how long each chain is */
static int ecm_db_mapping_count = 0; /* Number of mappings allocated */
typedef uint32_t ecm_db_mapping_hash_t;
/*
* Host hash table
*/
#define ECM_DB_HOST_HASH_SLOTS 32768
static struct ecm_db_host_instance *ecm_db_host_table[ECM_DB_HOST_HASH_SLOTS];
/* Slots of the host hash table */
static int ecm_db_host_table_lengths[ECM_DB_HOST_HASH_SLOTS];
/* Tracks how long each chain is */
static int ecm_db_host_count = 0; /* Number of hosts allocated */
typedef uint32_t ecm_db_host_hash_t;
/*
* Node hash table
*/
#define ECM_DB_NODE_HASH_SLOTS 32768
static struct ecm_db_node_instance *ecm_db_node_table[ECM_DB_NODE_HASH_SLOTS];
/* Slots of the node hash table */
static int ecm_db_node_table_lengths[ECM_DB_NODE_HASH_SLOTS];
/* Tracks how long each chain is */
static int ecm_db_node_count = 0; /* Number of nodes allocated */
typedef uint32_t ecm_db_node_hash_t;
/*
* Interface hash table
*/
#define ECM_DB_IFACE_HASH_SLOTS 8
static struct ecm_db_iface_instance *ecm_db_iface_table[ECM_DB_IFACE_HASH_SLOTS];
/* Slots of the interface hash table */
static int ecm_db_iface_table_lengths[ECM_DB_IFACE_HASH_SLOTS];
/* Tracks how long each chain is */
static int ecm_db_iface_count = 0; /* Number of interfaces allocated */
typedef uint32_t ecm_db_iface_hash_t;
/*
* Listeners
*/
static int ecm_db_listeners_count = 0; /* Number of listeners allocated */
static struct ecm_db_listener_instance *ecm_db_listeners = NULL;
/* Event listeners */
/*
* ecm_db_iface_xml_state_get_method_t
* Used to obtain interface XML state
*/
typedef int (*ecm_db_iface_xml_state_get_method_t)(struct ecm_db_iface_instance *ii, char *buf, int buf_sz);
/*
* struct ecm_db_iface_instance
*/
struct ecm_db_iface_instance {
struct ecm_db_iface_instance *next; /* Next instance in global list */
struct ecm_db_iface_instance *prev; /* Previous instance in global list */
struct ecm_db_iface_instance *hash_next; /* Next Interface in the chain of Interfaces */
struct ecm_db_iface_instance *hash_prev; /* previous Interface in the chain of Interfaces */
ecm_db_iface_type_t type; /* RO: Type of interface */
struct ecm_db_node_instance *nodes; /* Nodes associated with this Interface */
int node_count; /* Number of Nodes in the nodes list */
uint32_t time_added; /* RO: DB time stamp when the Interface was added into the database */
int32_t interface_identifier; /* RO: The operating system dependent identifier of this interface */
int32_t nss_interface_identifier; /* RO: The NSS identifier of this interface */
char name[IFNAMSIZ]; /* Name of interface */
int32_t mtu; /* Interface MTU */
uint64_t from_data_total; /* Total of data sent by this Interface */
uint64_t to_data_total; /* Total of data sent to this Interface */
uint64_t from_packet_total; /* Total of packets sent by this Interface */
uint64_t to_packet_total; /* Total of packets sent to this Interface */
uint64_t from_data_total_dropped;
uint64_t to_data_total_dropped;
uint64_t from_packet_total_dropped;
uint64_t to_packet_total_dropped;
/*
* For convenience interfaces keep lists of connections that have been established
* from them and to them.
* In fact the same connection could be listed as from & to on the same interface (think: WLAN<>WLAN AP function)
* Interfaces keep this information for rapid iteration of connections e.g. when an interface 'goes down' we
* can defunct all associated connections or destroy any NSS rules.
*/
struct ecm_db_connection_instance *from_connections; /* list of connections made from this interface */
struct ecm_db_connection_instance *to_connections; /* list of connections made to this interface */
struct ecm_db_connection_instance *from_nat_connections; /* list of NAT connections made from this interface */
struct ecm_db_connection_instance *to_nat_connections; /* list of NAT connections made to this interface */
/*
* Interface specific information.
* type identifies which information is applicable.
*/
union {
struct ecm_db_interface_info_ethernet ethernet; /* type == ECM_DB_IFACE_TYPE_ETHERNET */
struct ecm_db_interface_info_vlan vlan; /* type == ECM_DB_IFACE_TYPE_VLAN */
struct ecm_db_interface_info_lag lag; /* type == ECM_DB_IFACE_TYPE_LAG */
struct ecm_db_interface_info_bridge bridge; /* type == ECM_DB_IFACE_TYPE_BRIDGE */
struct ecm_db_interface_info_pppoe pppoe; /* type == ECM_DB_IFACE_TYPE_PPPOE */
struct ecm_db_interface_info_unknown unknown; /* type == ECM_DB_IFACE_TYPE_UNKNOWN */
struct ecm_db_interface_info_loopback loopback; /* type == ECM_DB_IFACE_TYPE_LOOPBACK */
struct ecm_db_interface_info_ipsec_tunnel ipsec_tunnel; /* type == ECM_DB_IFACE_TYPE_IPSEC_TUNNEL */
struct ecm_db_interface_info_sit sit; /* type == ECM_DB_IFACE_TYPE_SIT (6-in-4) */
struct ecm_db_interface_info_tunipip6 tunipip6; /* type == ECM_DB_IFACE_TYPE_TUNIPIP6 (IPIP v6 Tunnel i.e. TUNNEL6) */
} type_info;
ecm_db_iface_xml_state_get_method_t xml_state_get; /* Type specific state method to return XML state for it */
ecm_db_iface_final_callback_t final; /* Callback to owner when object is destroyed */
void *arg; /* Argument returned to owner in callbacks */
uint32_t flags;
int refs; /* Integer to trap we never go negative */
ecm_db_iface_hash_t hash_index;
#if (DEBUG_LEVEL > 0)
uint16_t magic;
#endif
};
/*
* Interface flags
*/
#define ECM_DB_IFACE_FLAGS_INSERTED 1 /* Interface is inserted into connection database tables */
/*
* struct ecm_db_node_instance
*/
struct ecm_db_node_instance {
struct ecm_db_node_instance *next; /* Next instance in global list */
struct ecm_db_node_instance *prev; /* Previous instance in global list */
struct ecm_db_node_instance *hash_next; /* Next host in the chain of nodes */
struct ecm_db_node_instance *hash_prev; /* previous host in the chain of nodes */
uint8_t address[ETH_ALEN]; /* RO: MAC Address of this node */
struct ecm_db_host_instance *hosts; /* Hosts associated with this node */
int host_count; /* Number of hosts in the hosts list */
uint32_t time_added; /* RO: DB time stamp when the node was added into the database */
uint64_t from_data_total; /* Total of data sent by this node */
uint64_t to_data_total; /* Total of data sent to this node */
uint64_t from_packet_total; /* Total of packets sent by this node */
uint64_t to_packet_total; /* Total of packets sent to this node */
uint64_t from_data_total_dropped;
uint64_t to_data_total_dropped;
uint64_t from_packet_total_dropped;
uint64_t to_packet_total_dropped;
struct ecm_db_iface_instance *iface; /* The interface to which this node relates */
struct ecm_db_node_instance *node_next; /* The next node within the same iface nodes list */
struct ecm_db_node_instance *node_prev; /* The previous node within the same iface nodes list */
ecm_db_node_final_callback_t final; /* Callback to owner when object is destroyed */
void *arg; /* Argument returned to owner in callbacks */
uint8_t flags;
int refs; /* Integer to trap we never go negative */
ecm_db_node_hash_t hash_index;
#if (DEBUG_LEVEL > 0)
uint16_t magic;
#endif
};
/*
* Node flags
*/
#define ECM_DB_NODE_FLAGS_INSERTED 1 /* Node is inserted into connection database tables */
/*
* struct ecm_db_host_instance
*/
struct ecm_db_host_instance {
struct ecm_db_host_instance *next; /* Next instance in global list */
struct ecm_db_host_instance *prev; /* Previous instance in global list */
struct ecm_db_host_instance *hash_next; /* Next host in the chain of hosts */
struct ecm_db_host_instance *hash_prev; /* previous host in the chain of hosts */
ip_addr_t address; /* RO: IPv4/v6 Address of this host */
bool on_link; /* RO: false when this host is reached via a gateway */
struct ecm_db_mapping_instance *mappings; /* Mappings made on this host */
int mapping_count; /* Number of mappings in the mapping list */
uint32_t time_added; /* RO: DB time stamp when the host was added into the database */
struct ecm_db_node_instance *node; /* The node to which this host relates */
struct ecm_db_host_instance *host_next; /* The next host within the nodes hosts list */
struct ecm_db_host_instance *host_prev; /* The previous host within the nodes hosts list */
uint64_t from_data_total; /* Total of data sent by this host */
uint64_t to_data_total; /* Total of data sent to this host */
uint64_t from_packet_total; /* Total of packets sent by this host */
uint64_t to_packet_total; /* Total of packets sent to this host */
uint64_t from_data_total_dropped;
uint64_t to_data_total_dropped;
uint64_t from_packet_total_dropped;
uint64_t to_packet_total_dropped;
ecm_db_host_final_callback_t final; /* Callback to owner when object is destroyed */
void *arg; /* Argument returned to owner in callbacks */
uint32_t flags;
int refs; /* Integer to trap we never go negative */
ecm_db_host_hash_t hash_index;
#if (DEBUG_LEVEL > 0)
uint16_t magic;
#endif
};
/*
* Host flags
*/
#define ECM_DB_HOST_FLAGS_INSERTED 1 /* Host is inserted into connection database tables */
/*
* struct ecm_db_mapping_instance
*/
struct ecm_db_mapping_instance {
struct ecm_db_mapping_instance *next; /* Next instance in global list */
struct ecm_db_mapping_instance *prev; /* Previous instance in global list */
struct ecm_db_mapping_instance *hash_next; /* Next mapping in the chain of mappings */
struct ecm_db_mapping_instance *hash_prev; /* previous mapping in the chain of mappings */
uint32_t time_added; /* RO: DB time stamp when the connection was added into the database */
struct ecm_db_host_instance *host; /* The host to which this mapping relates */
int port; /* RO: The port number on the host - only applicable for mapping protocols that are port based */
struct ecm_db_mapping_instance *mapping_next; /* Next mapping in the list of mappings for the host */
struct ecm_db_mapping_instance *mapping_prev; /* previous mapping in the list of mappings for the host */
struct ecm_db_connection_instance *from_connections; /* list of connections made from this host mapping */
struct ecm_db_connection_instance *to_connections; /* list of connections made to this host mapping */
struct ecm_db_connection_instance *from_nat_connections; /* list of NAT connections made from this host mapping */
struct ecm_db_connection_instance *to_nat_connections; /* list of NAT connections made to this host mapping */
/*
* Connection counts
*/
int tcp_from;
int tcp_to;
int udp_from;
int udp_to;
int tcp_nat_from;
int tcp_nat_to;
int udp_nat_from;
int udp_nat_to;
/*
* Total counts
*/
int from;
int to;
int nat_from;
int nat_to;
/*
* Data totals
*/
uint64_t from_data_total; /* Total of data sent by this mapping */
uint64_t to_data_total; /* Total of data sent to this mapping */
uint64_t from_packet_total; /* Total of packets sent by this mapping */
uint64_t to_packet_total; /* Total of packets sent to this mapping */
uint64_t from_data_total_dropped;
uint64_t to_data_total_dropped;
uint64_t from_packet_total_dropped;
uint64_t to_packet_total_dropped;
ecm_db_mapping_final_callback_t final; /* Callback to owner when object is destroyed */
void *arg; /* Argument returned to owner in callbacks */
uint32_t flags;
int refs; /* Integer to trap we never go negative */
ecm_db_mapping_hash_t hash_index;
#if (DEBUG_LEVEL > 0)
uint16_t magic;
#endif
};
/*
* Mapping flags
*/
#define ECM_DB_MAPPING_FLAGS_INSERTED 1 /* Mapping is inserted into connection database tables */
/*
* struct ecm_db_timer_group
* A timer group - all group members within the same group have the same TTL reset value.
*
* Expiry of entries occurs from tail to head.
*/
struct ecm_db_timer_group {
struct ecm_db_timer_group_entry *head; /* Most recently used entry in this timer group */
struct ecm_db_timer_group_entry *tail; /* Least recently used entry in this timer group. */
uint32_t time; /* Time in seconds a group entry will be given to live when 'touched' */
ecm_db_timer_group_t tg; /* RO: The group id */
#if (DEBUG_LEVEL > 0)
uint16_t magic;
#endif
};
/*
* Timers and cleanup
*/
static uint32_t ecm_db_time = 0; /* Time in seconds since start */
static struct ecm_db_timer_group ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_MAX];
/* Timer groups */
static struct timer_list ecm_db_timer; /* Timer to drive timer groups */
/*
* struct ecm_db_connection_instance
*/
struct ecm_db_connection_instance {
struct ecm_db_connection_instance *next; /* Next instance in global list */
struct ecm_db_connection_instance *prev; /* Previous instance in global list */
struct ecm_db_connection_instance *hash_next; /* Next connection in chain */
struct ecm_db_connection_instance *hash_prev; /* Previous connection in chain */
ecm_db_connection_hash_t hash_index; /* The hash table slot whose chain of connections this is inserted into */
struct ecm_db_connection_instance *serial_hash_next; /* Next connection in serial hash chain */
struct ecm_db_connection_instance *serial_hash_prev; /* Previous connection in serial hash chain */
ecm_db_connection_hash_t serial_hash_index; /* The hash table slot whose chain of connections this is inserted into */
uint32_t time_added; /* RO: DB time stamp when the connection was added into the database */
int protocol; /* RO: Protocol of the connection */
ecm_db_direction_t direction; /* RO: 'Direction' of connection establishment. */
bool is_routed; /* RO: True when connection is routed, false when not */
/*
* Connection endpoint mapping
*/
struct ecm_db_mapping_instance *mapping_from; /* The connection was established from this mapping */
struct ecm_db_mapping_instance *mapping_to; /* The connection was established to this mapping */
struct ecm_db_connection_instance *from_next; /* Next connection made from the same mapping */
struct ecm_db_connection_instance *from_prev; /* Previous connection made from the same mapping */
struct ecm_db_connection_instance *to_next; /* Next connection made to the same mapping */
struct ecm_db_connection_instance *to_prev; /* Previous connection made to the same mapping */
/*
* Connection endpoint mapping for NAT purposes
* NOTE: For non-NAT connections these would be identical to the endpoint mappings.
*/
struct ecm_db_mapping_instance *mapping_nat_from; /* The connection was established from this mapping */
struct ecm_db_mapping_instance *mapping_nat_to; /* The connection was established to this mapping */
struct ecm_db_connection_instance *from_nat_next; /* Next connection made from the same mapping */
struct ecm_db_connection_instance *from_nat_prev; /* Previous connection made from the same mapping */
struct ecm_db_connection_instance *to_nat_next; /* Next connection made to the same mapping */
struct ecm_db_connection_instance *to_nat_prev; /* Previous connection made to the same mapping */
/*
* Connection endpoint interface
*/
struct ecm_db_connection_instance *iface_from_next; /* Next connection made from the same interface */
struct ecm_db_connection_instance *iface_from_prev; /* Previous connection made from the same interface */
struct ecm_db_connection_instance *iface_to_next; /* Next connection made to the same interface */
struct ecm_db_connection_instance *iface_to_prev; /* Previous connection made to the same interface */
/*
* Connection endpoint interface for NAT purposes
* NOTE: For non-NAT connections these would be identical to the endpoint interface.
*/
struct ecm_db_connection_instance *iface_from_nat_next; /* Next connection made from the same interface */
struct ecm_db_connection_instance *iface_from_nat_prev; /* Previous connection made from the same interface */
struct ecm_db_connection_instance *iface_to_nat_next; /* Next connection made to the same interface */
struct ecm_db_connection_instance *iface_to_nat_prev; /* Previous connection made to the same interface */
/*
* From / To interfaces list
*/
struct ecm_db_iface_instance *from_interfaces[ECM_DB_IFACE_HEIRARCHY_MAX];
/* The outermost to innnermost interface this connection is using in the from path.
* Relationships are recorded from [ECM_DB_IFACE_HEIRARCHY_MAX - 1] to [0]
*/
int32_t from_interface_first; /* The index of the first interface in the list */
bool from_interface_set; /* True when a list has been set - even if there is NO list, it's still deliberately set that way. */
struct ecm_db_iface_instance *to_interfaces[ECM_DB_IFACE_HEIRARCHY_MAX];
/* The outermost to innnermost interface this connection is using in the to path */
int32_t to_interface_first; /* The index of the first interface in the list */
bool to_interface_set; /* True when a list has been set - even if there is NO list, it's still deliberately set that way. */
/*
* From / To NAT interfaces list
*/
struct ecm_db_iface_instance *from_nat_interfaces[ECM_DB_IFACE_HEIRARCHY_MAX];
/* The outermost to innnermost interface this connection is using in the from path.
* Relationships are recorded from [ECM_DB_IFACE_HEIRARCHY_MAX - 1] to [0]
*/
int32_t from_nat_interface_first; /* The index of the first interface in the list */
bool from_nat_interface_set; /* True when a list has been set - even if there is NO list, it's still deliberately set that way. */
struct ecm_db_iface_instance *to_nat_interfaces[ECM_DB_IFACE_HEIRARCHY_MAX];
/* The outermost to innnermost interface this connection is using in the to path */
int32_t to_nat_interface_first; /* The index of the first interface in the list */
bool to_nat_interface_set; /* True when a list has been set - even if there is NO list, it's still deliberately set that way. */
/*
* Time values in seconds
*/
struct ecm_db_timer_group_entry defunct_timer; /* Used to defunct the connection on inactivity */
/*
* Byte and packet counts
*/
uint64_t from_data_total; /* Totals of data as sent by the 'from' side of this connection */
uint64_t to_data_total; /* Totals of data as sent by the 'to' side of this connection */
uint64_t from_packet_total; /* Totals of packets as sent by the 'from' side of this connection */
uint64_t to_packet_total; /* Totals of packets as sent by the 'to' side of this connection */
uint64_t from_data_total_dropped; /* Total data sent by the 'from' side that we purposely dropped - the 'to' side has not seen this data */
uint64_t to_data_total_dropped; /* Total data sent by the 'to' side that we purposely dropped - the 'from' side has not seen this data */
uint64_t from_packet_total_dropped; /* Total packets sent by the 'from' side that we purposely dropped - the 'to' side has not seen this data */
uint64_t to_packet_total_dropped; /* Total packets sent by the 'to' side that we purposely dropped - the 'from' side has not seen this data */
/*
* Classifiers attached to this connection
*/
struct ecm_classifier_default_instance *dci; /* The default classifier */
struct ecm_classifier_instance *assignments; /* A list of all classifiers that are still assigned to this connection.
* When a connection is created, instances of every type of classifier are assigned to the connection.
* Classifiers are added in ascending order of priority - so the most important processes a packet last.
* Classifiers may drop out of this list (become unassigned) at any time.
*/
struct ecm_classifier_instance *assignments_by_type[ECM_CLASSIFIER_TYPES];
/* All assignments are also recorded in this array, since there can be only one of each type, this array allows
* rapid retrieval of a classifier type, saving having to iterate the assignments list.
*/
uint16_t classifier_generation; /* Used to detect when a re-evaluation of this connection is necessary */
struct ecm_front_end_connection_instance *feci; /* Front end instance specific to this connection */
ecm_db_connection_final_callback_t final; /* Callback to owner when object is destroyed */
void *arg; /* Argument returned to owner in callbacks */
uint32_t serial; /* Serial number for the connection - unique for run lifetime */
uint32_t flags;
int refs; /* Integer to trap we never go negative */
#if (DEBUG_LEVEL > 0)
uint16_t magic;
#endif
};
/*
* Connection flags
*/
#define ECM_DB_CONNECTION_FLAGS_INSERTED 1 /* Connection is inserted into connection database tables */
/*
* struct ecm_db_listener_instance
* listener instances
*/
struct ecm_db_listener_instance {
struct ecm_db_listener_instance *next;
struct ecm_db_listener_instance *event_next;
uint32_t flags;
void *arg;
int refs; /* Integer to trap we never go negative */
ecm_db_mapping_final_callback_t final; /* Final callback for this instance */
ecm_db_iface_listener_added_callback_t iface_added;
ecm_db_iface_listener_removed_callback_t iface_removed;
ecm_db_node_listener_added_callback_t node_added;
ecm_db_node_listener_removed_callback_t node_removed;
ecm_db_host_listener_added_callback_t host_added;
ecm_db_host_listener_removed_callback_t host_removed;
ecm_db_mapping_listener_added_callback_t mapping_added;
ecm_db_mapping_listener_removed_callback_t mapping_removed;
ecm_db_connection_listener_added_callback_t connection_added;
ecm_db_connection_listener_removed_callback_t connection_removed;
#if (DEBUG_LEVEL > 0)
uint16_t magic;
#endif
};
/*
* Listener flags
*/
#define ECM_DB_LISTENER_FLAGS_INSERTED 1 /* Is inserted into database */
/*
* Simple stats
*/
static int ecm_db_connection_count_by_protocol[256]; /* Each IP protocol has its own count */
/*
* Locking of the database - concurrency control
*/
static spinlock_t ecm_db_lock; /* Protect the table from SMP access. */
/*
* Connection validity
*/
static uint16_t ecm_db_classifier_generation = 0; /* Generation counter to detect out of date connections that should be reclassified */
/*
* SysFS linkage
*/
static struct sys_device ecm_db_sys_dev; /* SysFS linkage */
/*
* Management thread control
*/
static bool ecm_db_terminate_pending = false; /* When true the user has requested termination */
/*
* Character device stuff - used to communicate status back to user space
*/
#define ECM_DB_STATE_FILE_BUFFER_SIZE 8192
static int ecm_db_dev_major_id = 0; /* Major ID of registered char dev from which we can dump out state to userspace */
#define ECM_DB_STATE_FILE_OUTPUT_CONNECTIONS 1
#define ECM_DB_STATE_FILE_OUTPUT_MAPPINGS 2
#define ECM_DB_STATE_FILE_OUTPUT_HOSTS 4
#define ECM_DB_STATE_FILE_OUTPUT_NODES 8
#define ECM_DB_STATE_FILE_OUTPUT_INTERFACES 16
#define ECM_DB_STATE_FILE_OUTPUT_CONNECTIONS_CHAIN 32
#define ECM_DB_STATE_FILE_OUTPUT_MAPPINGS_CHAIN 64
#define ECM_DB_STATE_FILE_OUTPUT_HOSTS_CHAIN 128
#define ECM_DB_STATE_FILE_OUTPUT_NODES_CHAIN 256
#define ECM_DB_STATE_FILE_OUTPUT_INTERFACES_CHAIN 512
#define ECM_DB_STATE_FILE_OUTPUT_PROTOCOL_COUNTS 1024
/*
* struct ecm_db_state_file_instance
* Structure used as state per open instance of our db state file
*/
struct ecm_db_state_file_instance {
int output_mask;
struct ecm_db_connection_instance *ci;
struct ecm_db_mapping_instance *mi;
struct ecm_db_host_instance *hi;
struct ecm_db_node_instance *ni;
struct ecm_db_iface_instance *ii;
int connection_hash_index;
int mapping_hash_index;
int host_hash_index;
int node_hash_index;
int iface_hash_index;
int protocol;
bool doc_start_written;
bool doc_end_written;
char msg_buffer[ECM_DB_STATE_FILE_BUFFER_SIZE]; /* Used to hold the current state message being output */
char *msgp; /* Points into the msg buffer as we output it piece by piece */
int msg_len; /* Length of the buffer still to be written out */
#if (DEBUG_LEVEL > 0)
uint16_t magic;
#endif
};
static int ecm_db_state_file_output_mask = ECM_DB_STATE_FILE_OUTPUT_CONNECTIONS;
/* Bit mask specifies which data to output in the state file */
/*
* ecm_db_interface_type_names[]
* Array that maps the interface type to a string
*/
static char *ecm_db_interface_type_names[ECM_DB_IFACE_TYPE_COUNT] = {
"ETHERNET",
"PPPoE",
"LINK-AGGREGATION",
"VLAN",
"BRIDGE",
"LOOPBACK",
"IPSEC_TUNNEL",
"UNKNOWN",
"SIT",
"TUNIPIP6",
};
/*
* ecm_db_interface_type_to_string()
* Return a string buffer containing the type name of the interface
*/
char *ecm_db_interface_type_to_string(ecm_db_iface_type_t type)
{
DEBUG_ASSERT((type >= 0) && (type < ECM_DB_IFACE_TYPE_COUNT), "Invalid type: %d\n", type);
return ecm_db_interface_type_names[(int)type];
}
EXPORT_SYMBOL(ecm_db_interface_type_to_string);
/*
* ecm_db_iface_nss_interface_identifier_get()
* Return the NSS interface number of this ecm interface
*/
int32_t ecm_db_iface_nss_interface_identifier_get(struct ecm_db_iface_instance *ii)
{
DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed", ii);
return ii->nss_interface_identifier;
}
EXPORT_SYMBOL(ecm_db_iface_nss_interface_identifier_get);
/*
* ecm_db_iface_interface_identifier_get()
* Return the interface number of this ecm interface
*/
int32_t ecm_db_iface_interface_identifier_get(struct ecm_db_iface_instance *ii)
{
DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed", ii);
return ii->interface_identifier;
}
EXPORT_SYMBOL(ecm_db_iface_interface_identifier_get);
/*
* ecm_db_iface_mtu_reset()
* Reset the mtu
*/
int32_t ecm_db_iface_mtu_reset(struct ecm_db_iface_instance *ii, int32_t mtu)
{
int32_t mtu_old;
DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed", ii);
spin_lock_bh(&ecm_db_lock);
mtu_old = ii->mtu;
ii->mtu = mtu;
spin_unlock_bh(&ecm_db_lock);
DEBUG_INFO("%p: Mtu change from %d to %d\n", ii, mtu_old, mtu);
return mtu_old;
}
EXPORT_SYMBOL(ecm_db_iface_mtu_reset);
/*
* ecm_db_connection_front_end_get_and_ref()
* Return ref to the front end instance of the connection
*/
struct ecm_front_end_connection_instance *ecm_db_connection_front_end_get_and_ref(struct ecm_db_connection_instance *ci)
{
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
ci->feci->ref(ci->feci);
return ci->feci;
}
EXPORT_SYMBOL(ecm_db_connection_front_end_get_and_ref);
/*
* ecm_db_connection_defunct_callback()
* Invoked by the expiration of the defunct_timer contained in a connection instance
*/
static void ecm_db_connection_defunct_callback(void *arg)
{
struct ecm_db_connection_instance *ci = (struct ecm_db_connection_instance *)arg;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
DEBUG_INFO("%p: defunct timer expired\n", ci);
ecm_db_connection_deref(ci);
}
/*
* ecm_db_connection_defunct_timer_reset()
* Set/change the timer group associated with a connection. Returns false if the connection has become defunct and the new group cannot be set for that reason.
*/
bool ecm_db_connection_defunct_timer_reset(struct ecm_db_connection_instance *ci, ecm_db_timer_group_t tg)
{
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
return ecm_db_timer_group_entry_reset(&ci->defunct_timer, tg);
}
EXPORT_SYMBOL(ecm_db_connection_defunct_timer_reset);
/*
* ecm_db_connection_defunct_timer_touch()
* Update the connections defunct timer to stop it timing out. Returns false if the connection defunct timer has expired.
*/
bool ecm_db_connection_defunct_timer_touch(struct ecm_db_connection_instance *ci)
{
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
return ecm_db_timer_group_entry_touch(&ci->defunct_timer);
}
EXPORT_SYMBOL(ecm_db_connection_defunct_timer_touch);
/*
* ecm_db_connection_timer_group_get()
* Return the timer group id
*/
ecm_db_timer_group_t ecm_db_connection_timer_group_get(struct ecm_db_connection_instance *ci)
{
ecm_db_timer_group_t tg;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
spin_lock_bh(&ecm_db_lock);
tg = ci->defunct_timer.group;
spin_unlock_bh(&ecm_db_lock);
return tg;
}
EXPORT_SYMBOL(ecm_db_connection_timer_group_get);
/*
* ecm_db_connection_make_defunct()
* Make connection defunct.
*/
void ecm_db_connection_make_defunct(struct ecm_db_connection_instance *ci)
{
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
if (ecm_db_timer_group_entry_remove(&ci->defunct_timer)) {
ecm_db_connection_deref(ci);
}
}
EXPORT_SYMBOL(ecm_db_connection_make_defunct);
/*
* ecm_db_connection_data_totals_update()
* Update the total data (and packets) sent/received by the given host
*/
void ecm_db_connection_data_totals_update(struct ecm_db_connection_instance *ci, bool is_from, uint64_t size, uint64_t packets)
{
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
spin_lock_bh(&ecm_db_lock);
if (is_from) {
/*
* Update totals sent by the FROM side of connection
*/
ci->from_data_total += size;
ci->mapping_from->from_data_total += size;
ci->mapping_from->host->from_data_total += size;
ci->mapping_from->host->node->from_data_total += size;
ci->from_packet_total += packets;
ci->mapping_from->from_packet_total += packets;
ci->mapping_from->host->from_packet_total += packets;
ci->mapping_from->host->node->from_packet_total += packets;
/*
* Data from the host is essentially TO the interface on which the host is reachable
*/
ci->mapping_from->host->node->iface->to_data_total += size;
ci->mapping_from->host->node->iface->to_packet_total += packets;
/*
* Update totals sent TO the other side of the connection
*/
ci->mapping_to->to_data_total += size;
ci->mapping_to->host->to_data_total += size;
ci->mapping_to->host->node->to_data_total += size;
ci->mapping_to->to_packet_total += packets;
ci->mapping_to->host->to_packet_total += packets;
ci->mapping_to->host->node->to_packet_total += packets;
/*
* Sending to the other side means FROM the interface we reach that host
*/
ci->mapping_to->host->node->iface->from_data_total += size;
ci->mapping_to->host->node->iface->from_packet_total += packets;
spin_unlock_bh(&ecm_db_lock);
return;
}
/*
* Update totals sent by the TO side of this connection
*/
ci->to_data_total += size;
ci->mapping_to->from_data_total += size;
ci->mapping_to->host->from_data_total += size;
ci->mapping_to->host->node->from_data_total += size;
ci->to_packet_total += packets;
ci->mapping_to->from_packet_total += packets;
ci->mapping_to->host->from_packet_total += packets;
ci->mapping_to->host->node->from_packet_total += packets;
/*
* Data from the host is essentially TO the interface on which the host is reachable
*/
ci->mapping_to->host->node->iface->to_data_total += size;
ci->mapping_to->host->node->iface->to_packet_total += packets;
/*
* Update totals sent TO the other side of the connection
*/
ci->mapping_from->to_data_total += size;
ci->mapping_from->host->to_data_total += size;
ci->mapping_from->host->node->to_data_total += size;
ci->mapping_from->to_packet_total += packets;
ci->mapping_from->host->to_packet_total += packets;
ci->mapping_from->host->node->to_packet_total += packets;
/*
* Sending to the other side means FROM the interface we reach that host
*/
ci->mapping_from->host->node->iface->from_data_total += size;
ci->mapping_from->host->node->iface->from_packet_total += packets;
spin_unlock_bh(&ecm_db_lock);
}
EXPORT_SYMBOL(ecm_db_connection_data_totals_update);
/*
* ecm_db_connection_data_totals_update_dropped()
* Update the total data (and packets) sent by the given host but which we dropped
*/
void ecm_db_connection_data_totals_update_dropped(struct ecm_db_connection_instance *ci, bool is_from, uint64_t size, uint64_t packets)
{
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
if (is_from) {
/*
* Update dropped totals sent by the FROM side
*/
spin_lock_bh(&ecm_db_lock);
ci->from_data_total_dropped += size;
ci->mapping_from->from_data_total_dropped += size;
ci->mapping_from->host->from_data_total_dropped += size;
ci->mapping_from->host->node->from_data_total_dropped += size;
ci->from_packet_total_dropped += packets;
ci->mapping_from->from_packet_total_dropped += packets;
ci->mapping_from->host->from_packet_total_dropped += packets;
ci->mapping_from->host->node->from_packet_total_dropped += packets;
/*
* Data from the host is essentially TO the interface on which the host is reachable
*/
ci->mapping_from->host->node->iface->to_data_total_dropped += size;
ci->mapping_from->host->node->iface->to_packet_total_dropped += packets;
spin_unlock_bh(&ecm_db_lock);
return;
}
/*
* Update dropped totals sent by the TO side of this connection
*/
spin_lock_bh(&ecm_db_lock);
ci->to_data_total_dropped += size;
ci->mapping_to->from_data_total_dropped += size;
ci->mapping_to->host->from_data_total_dropped += size;
ci->mapping_to->host->node->from_data_total_dropped += size;
ci->to_packet_total_dropped += packets;
ci->mapping_to->from_packet_total_dropped += packets;
ci->mapping_to->host->from_packet_total_dropped += packets;
ci->mapping_to->host->node->from_packet_total_dropped += packets;
/*
* Data from the host is essentially TO the interface on which the host is reachable
*/
ci->mapping_to->host->node->iface->to_data_total_dropped += size;
ci->mapping_to->host->node->iface->to_packet_total_dropped += packets;
spin_unlock_bh(&ecm_db_lock);
}
EXPORT_SYMBOL(ecm_db_connection_data_totals_update_dropped);
/*
* ecm_db_connection_data_stats_get()
* Return data stats for the instance
*/
void ecm_db_connection_data_stats_get(struct ecm_db_connection_instance *ci, uint64_t *from_data_total, uint64_t *to_data_total,
uint64_t *from_packet_total, uint64_t *to_packet_total,
uint64_t *from_data_total_dropped, uint64_t *to_data_total_dropped,
uint64_t *from_packet_total_dropped, uint64_t *to_packet_total_dropped)
{
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
spin_lock_bh(&ecm_db_lock);
if (from_data_total) {
*from_data_total = ci->from_data_total;
}
if (to_data_total) {
*to_data_total = ci->to_data_total;
}
if (from_packet_total) {
*from_packet_total = ci->from_packet_total;
}
if (to_packet_total) {
*to_packet_total = ci->to_packet_total;
}
if (from_data_total_dropped) {
*from_data_total_dropped = ci->from_data_total_dropped;
}
if (to_data_total_dropped) {
*to_data_total_dropped = ci->to_data_total_dropped;
}
if (from_packet_total_dropped) {
*from_packet_total_dropped = ci->from_packet_total_dropped;
}
if (to_packet_total_dropped) {
*to_packet_total_dropped = ci->to_packet_total_dropped;
}
spin_unlock_bh(&ecm_db_lock);
}
EXPORT_SYMBOL(ecm_db_connection_data_stats_get);
/*
* ecm_db_mapping_data_stats_get()
* Return data stats for the instance
*/
void ecm_db_mapping_data_stats_get(struct ecm_db_mapping_instance *mi, uint64_t *from_data_total, uint64_t *to_data_total,
uint64_t *from_packet_total, uint64_t *to_packet_total,
uint64_t *from_data_total_dropped, uint64_t *to_data_total_dropped,
uint64_t *from_packet_total_dropped, uint64_t *to_packet_total_dropped)
{
DEBUG_CHECK_MAGIC(mi, ECM_DB_MAPPING_INSTANCE_MAGIC, "%p: magic failed", mi);
spin_lock_bh(&ecm_db_lock);
if (from_data_total) {
*from_data_total = mi->from_data_total;
}
if (to_data_total) {
*to_data_total = mi->to_data_total;
}
if (from_packet_total) {
*from_packet_total = mi->from_packet_total;
}
if (to_packet_total) {
*to_packet_total = mi->to_packet_total;
}
if (from_data_total_dropped) {
*from_data_total_dropped = mi->from_data_total_dropped;
}
if (to_data_total_dropped) {
*to_data_total_dropped = mi->to_data_total_dropped;
}
if (from_packet_total_dropped) {
*from_packet_total_dropped = mi->from_packet_total_dropped;
}
if (to_packet_total_dropped) {
*to_packet_total_dropped = mi->to_packet_total_dropped;
}
spin_unlock_bh(&ecm_db_lock);
}
EXPORT_SYMBOL(ecm_db_mapping_data_stats_get);
/*
* ecm_db_host_data_stats_get()
* Return data stats for the instance
*/
void ecm_db_host_data_stats_get(struct ecm_db_host_instance *hi, uint64_t *from_data_total, uint64_t *to_data_total,
uint64_t *from_packet_total, uint64_t *to_packet_total,
uint64_t *from_data_total_dropped, uint64_t *to_data_total_dropped,
uint64_t *from_packet_total_dropped, uint64_t *to_packet_total_dropped)
{
DEBUG_CHECK_MAGIC(hi, ECM_DB_HOST_INSTANCE_MAGIC, "%p: magic failed", hi);
spin_lock_bh(&ecm_db_lock);
if (from_data_total) {
*from_data_total = hi->from_data_total;
}
if (to_data_total) {
*to_data_total = hi->to_data_total;
}
if (from_packet_total) {
*from_packet_total = hi->from_packet_total;
}
if (to_packet_total) {
*to_packet_total = hi->to_packet_total;
}
if (from_data_total_dropped) {
*from_data_total_dropped = hi->from_data_total_dropped;
}
if (to_data_total_dropped) {
*to_data_total_dropped = hi->to_data_total_dropped;
}
if (from_packet_total_dropped) {
*from_packet_total_dropped = hi->from_packet_total_dropped;
}
if (to_packet_total_dropped) {
*to_packet_total_dropped = hi->to_packet_total_dropped;
}
spin_unlock_bh(&ecm_db_lock);
}
EXPORT_SYMBOL(ecm_db_host_data_stats_get);
/*
* ecm_db_node_data_stats_get()
* Return data stats for the instance
*/
void ecm_db_node_data_stats_get(struct ecm_db_node_instance *ni, uint64_t *from_data_total, uint64_t *to_data_total,
uint64_t *from_packet_total, uint64_t *to_packet_total,
uint64_t *from_data_total_dropped, uint64_t *to_data_total_dropped,
uint64_t *from_packet_total_dropped, uint64_t *to_packet_total_dropped)
{
DEBUG_CHECK_MAGIC(ni, ECM_DB_NODE_INSTANCE_MAGIC, "%p: magic failed", ni);
spin_lock_bh(&ecm_db_lock);
if (from_data_total) {
*from_data_total = ni->from_data_total;
}
if (to_data_total) {
*to_data_total = ni->to_data_total;
}
if (from_packet_total) {
*from_packet_total = ni->from_packet_total;
}
if (to_packet_total) {
*to_packet_total = ni->to_packet_total;
}
if (from_data_total_dropped) {
*from_data_total_dropped = ni->from_data_total_dropped;
}
if (to_data_total_dropped) {
*to_data_total_dropped = ni->to_data_total_dropped;
}
if (from_packet_total_dropped) {
*from_packet_total_dropped = ni->from_packet_total_dropped;
}
if (to_packet_total_dropped) {
*to_packet_total_dropped = ni->to_packet_total_dropped;
}
spin_unlock_bh(&ecm_db_lock);
}
EXPORT_SYMBOL(ecm_db_node_data_stats_get);
/*
* ecm_db_iface_data_stats_get()
* Return data stats for the instance
*/
void ecm_db_iface_data_stats_get(struct ecm_db_iface_instance *ii, uint64_t *from_data_total, uint64_t *to_data_total,
uint64_t *from_packet_total, uint64_t *to_packet_total,
uint64_t *from_data_total_dropped, uint64_t *to_data_total_dropped,
uint64_t *from_packet_total_dropped, uint64_t *to_packet_total_dropped)
{
DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed", ii);
spin_lock_bh(&ecm_db_lock);
if (from_data_total) {
*from_data_total = ii->from_data_total;
}
if (to_data_total) {
*to_data_total = ii->to_data_total;
}
if (from_packet_total) {
*from_packet_total = ii->from_packet_total;
}
if (to_packet_total) {
*to_packet_total = ii->to_packet_total;
}
if (from_data_total_dropped) {
*from_data_total_dropped = ii->from_data_total_dropped;
}
if (to_data_total_dropped) {
*to_data_total_dropped = ii->to_data_total_dropped;
}
if (from_packet_total_dropped) {
*from_packet_total_dropped = ii->from_packet_total_dropped;
}
if (to_packet_total_dropped) {
*to_packet_total_dropped = ii->to_packet_total_dropped;
}
spin_unlock_bh(&ecm_db_lock);
}
EXPORT_SYMBOL(ecm_db_iface_data_stats_get);
/*
* ecm_db_connection_serial_get()
* Return serial
*/
uint32_t ecm_db_connection_serial_get(struct ecm_db_connection_instance *ci)
{
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
return ci->serial;
}
EXPORT_SYMBOL(ecm_db_connection_serial_get);
/*
* ecm_db_connection_from_address_get()
* Return ip address address
*/
void ecm_db_connection_from_address_get(struct ecm_db_connection_instance *ci, ip_addr_t addr)
{
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
DEBUG_CHECK_MAGIC(ci->mapping_from, ECM_DB_MAPPING_INSTANCE_MAGIC, "%p: magic failed", ci->mapping_from);
DEBUG_CHECK_MAGIC(ci->mapping_from->host, ECM_DB_HOST_INSTANCE_MAGIC, "%p: magic failed", ci->mapping_from->host);
ECM_IP_ADDR_COPY(addr, ci->mapping_from->host->address);
}
EXPORT_SYMBOL(ecm_db_connection_from_address_get);
/*
* ecm_db_connection_from_address_nat_get()
* Return NAT ip address address
*/
void ecm_db_connection_from_address_nat_get(struct ecm_db_connection_instance *ci, ip_addr_t addr)
{
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
DEBUG_CHECK_MAGIC(ci->mapping_from, ECM_DB_MAPPING_INSTANCE_MAGIC, "%p: magic failed", ci->mapping_from);
DEBUG_CHECK_MAGIC(ci->mapping_from->host, ECM_DB_HOST_INSTANCE_MAGIC, "%p: magic failed", ci->mapping_from->host);
ECM_IP_ADDR_COPY(addr, ci->mapping_nat_from->host->address);
}
EXPORT_SYMBOL(ecm_db_connection_from_address_nat_get);
/*
* ecm_db_connection_to_address_get()
* Return ip address address
*/
void ecm_db_connection_to_address_get(struct ecm_db_connection_instance *ci, ip_addr_t addr)
{
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
DEBUG_CHECK_MAGIC(ci->mapping_to, ECM_DB_MAPPING_INSTANCE_MAGIC, "%p: magic failed", ci->mapping_to);
DEBUG_CHECK_MAGIC(ci->mapping_to->host, ECM_DB_HOST_INSTANCE_MAGIC, "%p: magic failed", ci->mapping_to->host);
ECM_IP_ADDR_COPY(addr, ci->mapping_to->host->address);
}
EXPORT_SYMBOL(ecm_db_connection_to_address_get);
/*
* ecm_db_connection_to_address_nat_get()
* Return NAT ip address address
*/
void ecm_db_connection_to_address_nat_get(struct ecm_db_connection_instance *ci, ip_addr_t addr)
{
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
DEBUG_CHECK_MAGIC(ci->mapping_to, ECM_DB_MAPPING_INSTANCE_MAGIC, "%p: magic failed", ci->mapping_to);
DEBUG_CHECK_MAGIC(ci->mapping_to->host, ECM_DB_HOST_INSTANCE_MAGIC, "%p: magic failed", ci->mapping_to->host);
ECM_IP_ADDR_COPY(addr, ci->mapping_nat_to->host->address);
}
EXPORT_SYMBOL(ecm_db_connection_to_address_nat_get);
/*
* ecm_db_connection_to_port_get()
* Return port
*/
int ecm_db_connection_to_port_get(struct ecm_db_connection_instance *ci)
{
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
DEBUG_CHECK_MAGIC(ci->mapping_to, ECM_DB_MAPPING_INSTANCE_MAGIC, "%p: magic failed", ci->mapping_to);
return ci->mapping_to->port;
}
EXPORT_SYMBOL(ecm_db_connection_to_port_get);
/*
* ecm_db_connection_to_port_nat_get()
* Return port
*/
int ecm_db_connection_to_port_nat_get(struct ecm_db_connection_instance *ci)
{
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
DEBUG_CHECK_MAGIC(ci->mapping_to, ECM_DB_MAPPING_INSTANCE_MAGIC, "%p: magic failed", ci->mapping_to);
return ci->mapping_nat_to->port;
}
EXPORT_SYMBOL(ecm_db_connection_to_port_nat_get);
/*
* ecm_db_connection_from_port_get()
* Return port
*/
int ecm_db_connection_from_port_get(struct ecm_db_connection_instance *ci)
{
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
DEBUG_CHECK_MAGIC(ci->mapping_from, ECM_DB_MAPPING_INSTANCE_MAGIC, "%p: magic failed", ci->mapping_from);
return ci->mapping_from->port;
}
EXPORT_SYMBOL(ecm_db_connection_from_port_get);
/*
* ecm_db_connection_from_port_nat_get()
* Return port
*/
int ecm_db_connection_from_port_nat_get(struct ecm_db_connection_instance *ci)
{
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
DEBUG_CHECK_MAGIC(ci->mapping_from, ECM_DB_MAPPING_INSTANCE_MAGIC, "%p: magic failed", ci->mapping_from);
return ci->mapping_nat_from->port;
}
EXPORT_SYMBOL(ecm_db_connection_from_port_nat_get);
/*
* ecm_db_connection_to_node_address_get()
* Return address of the node used when sending packets to the 'to' side.
*/
void ecm_db_connection_to_node_address_get(struct ecm_db_connection_instance *ci, uint8_t *address_buffer)
{
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
memcpy(address_buffer, ci->mapping_to->host->node->address, ETH_ALEN);
}
EXPORT_SYMBOL(ecm_db_connection_to_node_address_get);
/*
* ecm_db_connection_from_node_address_get()
* Return address of the node used when sending packets to the 'from' side.
*/
void ecm_db_connection_from_node_address_get(struct ecm_db_connection_instance *ci, uint8_t *address_buffer)
{
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
memcpy(address_buffer, ci->mapping_from->host->node->address, ETH_ALEN);
}
EXPORT_SYMBOL(ecm_db_connection_from_node_address_get);
/*
* ecm_db_connection_to_nat_node_address_get()
* Return address of the node used when sending packets to the 'to' NAT side.
*/
void ecm_db_connection_to_nat_node_address_get(struct ecm_db_connection_instance *ci, uint8_t *address_buffer)
{
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
memcpy(address_buffer, ci->mapping_nat_to->host->node->address, ETH_ALEN);
}
EXPORT_SYMBOL(ecm_db_connection_to_nat_node_address_get);
/*
* ecm_db_connection_from_nat_node_address_get()
* Return address of the node used when sending packets to the 'from' NAT side.
*/
void ecm_db_connection_from_nat_node_address_get(struct ecm_db_connection_instance *ci, uint8_t *address_buffer)
{
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
memcpy(address_buffer, ci->mapping_nat_from->host->node->address, ETH_ALEN);
}
EXPORT_SYMBOL(ecm_db_connection_from_nat_node_address_get);
/*
* ecm_db_connection_to_iface_name_get()
* Return name of interface on which the 'to' side may be reached
*/
void ecm_db_connection_to_iface_name_get(struct ecm_db_connection_instance *ci, char *name_buffer)
{
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
strcpy(name_buffer, ci->mapping_to->host->node->iface->name);
}
EXPORT_SYMBOL(ecm_db_connection_to_iface_name_get);
/*
* ecm_db_connection_from_iface_name_get()
* Return name of interface on which the 'from' side may be reached
*/
void ecm_db_connection_from_iface_name_get(struct ecm_db_connection_instance *ci, char *name_buffer)
{
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
strcpy(name_buffer, ci->mapping_from->host->node->iface->name);
}
EXPORT_SYMBOL(ecm_db_connection_from_iface_name_get);
/*
* ecm_db_connection_to_iface_mtu_get()
* Return MTU of interface on which the 'to' side may be reached
*/
int ecm_db_connection_to_iface_mtu_get(struct ecm_db_connection_instance *ci)
{
int mtu;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
spin_lock_bh(&ecm_db_lock);
mtu = ci->mapping_to->host->node->iface->mtu;
spin_unlock_bh(&ecm_db_lock);
return mtu;
}
EXPORT_SYMBOL(ecm_db_connection_to_iface_mtu_get);
/*
* ecm_db_connection_to_iface_type_get()
* Return type of interface on which the 'to' side may be reached
*/
ecm_db_iface_type_t ecm_db_connection_to_iface_type_get(struct ecm_db_connection_instance *ci)
{
ecm_db_iface_type_t type;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
spin_lock_bh(&ecm_db_lock);
type = ci->mapping_to->host->node->iface->type;
spin_unlock_bh(&ecm_db_lock);
return type;
}
EXPORT_SYMBOL(ecm_db_connection_to_iface_type_get);
/*
* ecm_db_connection_from_iface_mtu_get()
* Return MTU of interface on which the 'from' side may be reached
*/
int ecm_db_connection_from_iface_mtu_get(struct ecm_db_connection_instance *ci)
{
int mtu;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
spin_lock_bh(&ecm_db_lock);
mtu = ci->mapping_from->host->node->iface->mtu;
spin_unlock_bh(&ecm_db_lock);
return mtu;
}
EXPORT_SYMBOL(ecm_db_connection_from_iface_mtu_get);
/*
* ecm_db_connection_from_iface_type_get()
* Return type of interface on which the 'from' side may be reached
*/
ecm_db_iface_type_t ecm_db_connection_from_iface_type_get(struct ecm_db_connection_instance *ci)
{
ecm_db_iface_type_t type;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
spin_lock_bh(&ecm_db_lock);
type = ci->mapping_from->host->node->iface->type;
spin_unlock_bh(&ecm_db_lock);
return type;
}
EXPORT_SYMBOL(ecm_db_connection_from_iface_type_get);
/*
* ecm_db_connection_iface_type_get()
* Return type of interface
*/
ecm_db_iface_type_t ecm_db_connection_iface_type_get(struct ecm_db_iface_instance *ii)
{
DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed", ii);
return ii->type;
}
EXPORT_SYMBOL(ecm_db_connection_iface_type_get);
/*
* ecm_db_connection_classifier_generation_changed()
* Returns true if the classifier generation has changed for this connection.
*
* NOTE: The generation index will be reset on return from this call so action any true result immediately.
*/
bool ecm_db_connection_classifier_generation_changed(struct ecm_db_connection_instance *ci)
{
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
spin_lock_bh(&ecm_db_lock);
if (ci->classifier_generation == ecm_db_classifier_generation) {
spin_unlock_bh(&ecm_db_lock);
return false;
}
ci->classifier_generation = ecm_db_classifier_generation;
spin_unlock_bh(&ecm_db_lock);
return true;
}
EXPORT_SYMBOL(ecm_db_connection_classifier_generation_changed);
/*
* ecm_db_connection_classifier_peek_generation_changed()
* Returns true if the classifier generation has changed for this connection.
*
* NOTE: The generation index will NOT be reset on return from this call.
*/
bool ecm_db_connection_classifier_peek_generation_changed(struct ecm_db_connection_instance *ci)
{
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
spin_lock_bh(&ecm_db_lock);
if (ci->classifier_generation == ecm_db_classifier_generation) {
spin_unlock_bh(&ecm_db_lock);
return false;
}
spin_unlock_bh(&ecm_db_lock);
return true;
}
EXPORT_SYMBOL(ecm_db_connection_classifier_peek_generation_changed);
/*
* ecm_db_connection_classifier_generation_change()
* Cause a specific connection to be re-generated
*/
void ecm_db_connection_classifier_generation_change(struct ecm_db_connection_instance *ci)
{
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
spin_lock_bh(&ecm_db_lock);
ci->classifier_generation = ecm_db_classifier_generation - 1;
spin_unlock_bh(&ecm_db_lock);
}
EXPORT_SYMBOL(ecm_db_connection_classifier_generation_change);
/*
* ecm_db_classifier_generation_change()
* Bump the generation index to cause a re-classification of connections
*
* NOTE: Any connections that see activity after a call to this could be put back to undetermined qos state
* and driven back through the classifiers.
*/
void ecm_db_classifier_generation_change(void)
{
spin_lock_bh(&ecm_db_lock);
ecm_db_classifier_generation++;
spin_unlock_bh(&ecm_db_lock);
}
EXPORT_SYMBOL(ecm_db_classifier_generation_change);
/*
* ecm_db_connection_direction_get()
* Return direction of the connection.
*
* NOTE: an EGRESS connection means that packets being sent to mapping_to should have qos applied.
* INGRESS means that packets being sent to mapping_from should have qos applied.
*/
ecm_db_direction_t ecm_db_connection_direction_get(struct ecm_db_connection_instance *ci)
{
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
return ci->direction;
}
EXPORT_SYMBOL(ecm_db_connection_direction_get);
/*
* ecm_db_mapping_port_count_get()
* Return port count stats for a mapping.
*/
void ecm_db_mapping_port_count_get(struct ecm_db_mapping_instance *mi,
int *tcp_from, int *tcp_to, int *udp_from, int *udp_to, int *from, int *to,
int *tcp_nat_from, int *tcp_nat_to, int *udp_nat_from, int *udp_nat_to, int *nat_from, int *nat_to)
{
DEBUG_CHECK_MAGIC(mi, ECM_DB_MAPPING_INSTANCE_MAGIC, "%p: magic failed", mi);
spin_lock_bh(&ecm_db_lock);
*tcp_from = mi->tcp_from;
*tcp_to = mi->tcp_to;
*udp_from = mi->udp_from;
*udp_to = mi->udp_to;
*from = mi->from;
*to = mi->to;
*tcp_nat_from = mi->tcp_nat_from;
*tcp_nat_to = mi->tcp_nat_to;
*udp_nat_from = mi->udp_nat_from;
*udp_nat_to = mi->udp_nat_to;
*nat_from = mi->nat_from;
*nat_to = mi->nat_to;
spin_unlock_bh(&ecm_db_lock);
}
EXPORT_SYMBOL(ecm_db_mapping_port_count_get);
/*
* ecm_db_connection_is_routed_get()
* Return whether connection is a routed path or not
*/
bool ecm_db_connection_is_routed_get(struct ecm_db_connection_instance *ci)
{
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
return ci->is_routed;
}
EXPORT_SYMBOL(ecm_db_connection_is_routed_get);
/*
* ecm_db_connection_protocol_get()
* Return protocol of connection
*/
int ecm_db_connection_protocol_get(struct ecm_db_connection_instance *ci)
{
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
return ci->protocol;
}
EXPORT_SYMBOL(ecm_db_connection_protocol_get);
/*
* ecm_db_host_address_get()
* Return address of host
*/
void ecm_db_host_address_get(struct ecm_db_host_instance *hi, ip_addr_t addr)
{
DEBUG_CHECK_MAGIC(hi, ECM_DB_HOST_INSTANCE_MAGIC, "%p: magic failed", hi);
ECM_IP_ADDR_COPY(addr, hi->address);
}
EXPORT_SYMBOL(ecm_db_host_address_get);
/*
* ecm_db_host_node_address_get()
* Return node address of the host
*/
void ecm_db_host_node_address_get(struct ecm_db_host_instance *hi, uint8_t *address_buffer)
{
DEBUG_CHECK_MAGIC(hi, ECM_DB_HOST_INSTANCE_MAGIC, "%p: magic failed", hi);
memcpy(address_buffer, hi->node->address, ETH_ALEN);
}
EXPORT_SYMBOL(ecm_db_host_node_address_get);
/*
* ecm_db_host_on_link_get()
* Return on link status of host
*/
bool ecm_db_host_on_link_get(struct ecm_db_host_instance *hi)
{
DEBUG_CHECK_MAGIC(hi, ECM_DB_HOST_INSTANCE_MAGIC, "%p: magic failed", hi);
return hi->on_link;
}
EXPORT_SYMBOL(ecm_db_host_on_link_get);
/*
* ecm_db_mapping_adress_get()
* Return address
*/
void ecm_db_mapping_adress_get(struct ecm_db_mapping_instance *mi, ip_addr_t addr)
{
DEBUG_CHECK_MAGIC(mi, ECM_DB_MAPPING_INSTANCE_MAGIC, "%p: magic failed", mi);
ECM_IP_ADDR_COPY(addr, mi->host->address);
}
EXPORT_SYMBOL(ecm_db_mapping_adress_get);
/*
* ecm_db_mapping_port_get()
* Return port
*/
int ecm_db_mapping_port_get(struct ecm_db_mapping_instance *mi)
{
DEBUG_CHECK_MAGIC(mi, ECM_DB_MAPPING_INSTANCE_MAGIC, "%p: magic failed", mi);
return mi->port;
}
EXPORT_SYMBOL(ecm_db_mapping_port_get);
/*
* ecm_db_node_adress_get()
* Return address
*/
void ecm_db_node_adress_get(struct ecm_db_node_instance *ni, uint8_t *address_buffer)
{
DEBUG_CHECK_MAGIC(ni, ECM_DB_NODE_INSTANCE_MAGIC, "%p: magic failed", ni);
memcpy(address_buffer, ni->address, ETH_ALEN);
}
EXPORT_SYMBOL(ecm_db_node_adress_get);
/*
* _ecm_db_timer_group_entry_remove()
* Remove the entry from its timer group, returns false if the entry has already expired.
*/
static bool _ecm_db_timer_group_entry_remove(struct ecm_db_timer_group_entry *tge)
{
struct ecm_db_timer_group *timer_group;
/*
* If not in a timer group then it is already removed
*/
if (tge->group == ECM_DB_TIMER_GROUPS_MAX) {
return false;
}
/*
* Remove the connection from its current group
*/
timer_group = &ecm_db_timer_groups[tge->group];
/*
* Somewhere in the list?
*/
if (tge->prev) {
tge->prev->next = tge->next;
} else {
/*
* First in the group
*/
DEBUG_ASSERT(timer_group->head == tge, "%p: bad head, expecting %p, got %p\n", timer_group, tge, timer_group->head);
timer_group->head = tge->next;
}
if (tge->next) {
tge->next->prev = tge->prev;
} else {
/*
* No next so this must be the last item - we need to adjust the tail pointer
*/
DEBUG_ASSERT(timer_group->tail == tge, "%p: bad tail, expecting %p got %p\n", timer_group, tge, timer_group->tail);
timer_group->tail = tge->prev;
}
/*
* No longer a part of a timer group
*/
tge->group = ECM_DB_TIMER_GROUPS_MAX;
return true;
}
/*
* ecm_db_timer_group_entry_remove()
* Remove the connection from its timer group, returns false if the entry has already expired.
*/
bool ecm_db_timer_group_entry_remove(struct ecm_db_timer_group_entry *tge)
{
bool res;
spin_lock_bh(&ecm_db_lock);
res = _ecm_db_timer_group_entry_remove(tge);
spin_unlock_bh(&ecm_db_lock);
return res;
}
EXPORT_SYMBOL(ecm_db_timer_group_entry_remove);
/*
* _ecm_db_timer_group_entry_set()
* Set the timer group to which this entry will be a member
*/
void _ecm_db_timer_group_entry_set(struct ecm_db_timer_group_entry *tge, ecm_db_timer_group_t tg)
{
struct ecm_db_timer_group *timer_group;
DEBUG_ASSERT(tge->group == ECM_DB_TIMER_GROUPS_MAX, "%p: already set\n", tge);
/*
* Set group
*/
tge->group = tg;
timer_group = &ecm_db_timer_groups[tge->group];
tge->timeout = timer_group->time + ecm_db_time;
/*
* Insert into a timer group at the head (as this is now touched)
*/
tge->prev = NULL;
tge->next = timer_group->head;
if (!timer_group->head) {
/*
* As there is no head there is also no tail so we need to set that
*/
timer_group->tail = tge;
} else {
/*
* As there is a head already there must be a tail. Since we insert before
* the current head we don't adjust the tail.
*/
timer_group->head->prev = tge;
}
timer_group->head = tge;
}
/*
* ecm_db_timer_group_entry_reset()
* Re-set the timer group to which this entry will be a member.
*
* Returns false if the timer cannot be reset because it has expired
*/
bool ecm_db_timer_group_entry_reset(struct ecm_db_timer_group_entry *tge, ecm_db_timer_group_t tg)
{
spin_lock_bh(&ecm_db_lock);
/*
* Remove it from its current group, if any
*/
if (!_ecm_db_timer_group_entry_remove(tge)) {
spin_unlock_bh(&ecm_db_lock);
return false;
}
/*
* Set new group
*/
_ecm_db_timer_group_entry_set(tge, tg);
spin_unlock_bh(&ecm_db_lock);
return true;
}
EXPORT_SYMBOL(ecm_db_timer_group_entry_reset);
/*
* ecm_db_timer_group_entry_set()
* Set the timer group to which this entry will be a member
*/
void ecm_db_timer_group_entry_set(struct ecm_db_timer_group_entry *tge, ecm_db_timer_group_t tg)
{
spin_lock_bh(&ecm_db_lock);
_ecm_db_timer_group_entry_set(tge, tg);
spin_unlock_bh(&ecm_db_lock);
}
EXPORT_SYMBOL(ecm_db_timer_group_entry_set);
/*
* ecm_db_timer_group_entry_init()
* Initialise a timer entry ready for setting
*/
void ecm_db_timer_group_entry_init(struct ecm_db_timer_group_entry *tge, ecm_db_timer_group_entry_callback_t fn, void *arg)
{
memset(tge, 0, sizeof(struct ecm_db_timer_group_entry));
tge->group = ECM_DB_TIMER_GROUPS_MAX;
tge->arg = arg;
tge->fn = fn;
}
EXPORT_SYMBOL(ecm_db_timer_group_entry_init);
/*
* ecm_db_timer_group_entry_touch()
* Update the timeout, if the timer is not running this has no effect.
* It returns false if the timer is not running.
*/
bool ecm_db_timer_group_entry_touch(struct ecm_db_timer_group_entry *tge)
{
struct ecm_db_timer_group *timer_group;
spin_lock_bh(&ecm_db_lock);
/*
* If not in a timer group then do nothing
*/
if (tge->group == ECM_DB_TIMER_GROUPS_MAX) {
spin_unlock_bh(&ecm_db_lock);
return false;
}
/*
* Update time to live
*/
timer_group = &ecm_db_timer_groups[tge->group];
/*
* Link out of its current position.
*/
if (!tge->prev) {
/*
* Already at the head, just update the time
*/
tge->timeout = timer_group->time + ecm_db_time;
spin_unlock_bh(&ecm_db_lock);
return true;
}
/*
* tge->prev is not null, so:
* 1) it is in a timer list
* 2) is not at the head of the list
* 3) there is a head already (so more than one item on the list)
* 4) there is a prev pointer.
* Somewhere in the group list - unlink it.
*/
tge->prev->next = tge->next;
if (tge->next) {
tge->next->prev = tge->prev;
} else {
/*
* Since there is no next this must be the tail
*/
DEBUG_ASSERT(timer_group->tail == tge, "%p: bad tail, expecting %p got %p\n", timer_group, tge, timer_group->tail);
timer_group->tail = tge->prev;
}
/*
* Link in to head.
*/
tge->timeout = timer_group->time + ecm_db_time;
tge->prev = NULL;
tge->next = timer_group->head;
timer_group->head->prev = tge;
timer_group->head = tge;
spin_unlock_bh(&ecm_db_lock);
return true;
}
EXPORT_SYMBOL(ecm_db_timer_group_entry_touch);
/*
* _ecm_db_connection_ref()
*/
static void _ecm_db_connection_ref(struct ecm_db_connection_instance *ci)
{
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
ci->refs++;
DEBUG_TRACE("%p: connection ref %d\n", ci, ci->refs);
DEBUG_ASSERT(ci->refs > 0, "%p: ref wrap\n", ci);
}
/*
* ecm_db_connection_ref()
*/
void ecm_db_connection_ref(struct ecm_db_connection_instance *ci)
{
spin_lock_bh(&ecm_db_lock);
_ecm_db_connection_ref(ci);
spin_unlock_bh(&ecm_db_lock);
}
EXPORT_SYMBOL(ecm_db_connection_ref);
/*
* _ecm_db_mapping_ref()
*/
static void _ecm_db_mapping_ref(struct ecm_db_mapping_instance *mi)
{
DEBUG_CHECK_MAGIC(mi, ECM_DB_MAPPING_INSTANCE_MAGIC, "%p: magic failed\n", mi);
mi->refs++;
DEBUG_TRACE("%p: mapping ref %d\n", mi, mi->refs);
DEBUG_ASSERT(mi->refs > 0, "%p: ref wrap\n", mi);
}
/*
* ecm_db_mapping_ref()
*/
void ecm_db_mapping_ref(struct ecm_db_mapping_instance *mi)
{
spin_lock_bh(&ecm_db_lock);
_ecm_db_mapping_ref(mi);
spin_unlock_bh(&ecm_db_lock);
}
EXPORT_SYMBOL(ecm_db_mapping_ref);
/*
* _ecm_db_host_ref()
*/
static void _ecm_db_host_ref(struct ecm_db_host_instance *hi)
{
DEBUG_CHECK_MAGIC(hi, ECM_DB_HOST_INSTANCE_MAGIC, "%p: magic failed\n", hi);
hi->refs++;
DEBUG_TRACE("%p: host ref %d\n", hi, hi->refs);
DEBUG_ASSERT(hi->refs > 0, "%p: ref wrap\n", hi);
}
/*
* ecm_db_host_ref()
*/
void ecm_db_host_ref(struct ecm_db_host_instance *hi)
{
spin_lock_bh(&ecm_db_lock);
_ecm_db_host_ref(hi);
spin_unlock_bh(&ecm_db_lock);
}
EXPORT_SYMBOL(ecm_db_host_ref);
/*
* _ecm_db_node_ref()
*/
static void _ecm_db_node_ref(struct ecm_db_node_instance *ni)
{
DEBUG_CHECK_MAGIC(ni, ECM_DB_NODE_INSTANCE_MAGIC, "%p: magic failed\n", ni);
ni->refs++;
DEBUG_TRACE("%p: node ref %d\n", ni, ni->refs);
DEBUG_ASSERT(ni->refs > 0, "%p: ref wrap\n", ni);
}
/*
* ecm_db_node_ref()
*/
void ecm_db_node_ref(struct ecm_db_node_instance *ni)
{
spin_lock_bh(&ecm_db_lock);
_ecm_db_node_ref(ni);
spin_unlock_bh(&ecm_db_lock);
}
EXPORT_SYMBOL(ecm_db_node_ref);
/*
* _ecm_db_iface_ref()
*/
static void _ecm_db_iface_ref(struct ecm_db_iface_instance *ii)
{
DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed\n", ii);
ii->refs++;
DEBUG_TRACE("%p: iface ref %d\n", ii, ii->refs);
DEBUG_ASSERT(ii->refs > 0, "%p: ref wrap\n", ii);
}
/*
* ecm_db_iface_ref()
*/
void ecm_db_iface_ref(struct ecm_db_iface_instance *ii)
{
spin_lock_bh(&ecm_db_lock);
_ecm_db_iface_ref(ii);
spin_unlock_bh(&ecm_db_lock);
}
EXPORT_SYMBOL(ecm_db_iface_ref);
/*
* _ecm_db_listener_ref()
*/
static void _ecm_db_listener_ref(struct ecm_db_listener_instance *li)
{
DEBUG_CHECK_MAGIC(li, ECM_DB_LISTENER_INSTANCE_MAGIC, "%p: magic failed", li);
li->refs++;
DEBUG_ASSERT(li->refs > 0, "%p: ref wrap\n", li);
}
/*
* ecm_db_listener_ref()
*/
void ecm_db_listener_ref(struct ecm_db_listener_instance *li)
{
spin_lock_bh(&ecm_db_lock);
_ecm_db_listener_ref(li);
spin_unlock_bh(&ecm_db_lock);
}
EXPORT_SYMBOL(ecm_db_listener_ref);
/*
* ecm_db_connections_get_and_ref_first()
* Obtain a ref to the first connection instance, if any
*/
static struct ecm_db_connection_instance *ecm_db_connections_get_and_ref_first(void)
{
struct ecm_db_connection_instance *ci;
spin_lock_bh(&ecm_db_lock);
ci = ecm_db_connections;
if (ci) {
_ecm_db_connection_ref(ci);
}
spin_unlock_bh(&ecm_db_lock);
return ci;
}
EXPORT_SYMBOL(ecm_db_connections_get_and_ref_first);
/*
* ecm_db_connection_get_and_ref_next()
* Return the next connection in the list given a connection
*/
struct ecm_db_connection_instance *ecm_db_connection_get_and_ref_next(struct ecm_db_connection_instance *ci)
{
struct ecm_db_connection_instance *cin;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
spin_lock_bh(&ecm_db_lock);
cin = ci->next;
if (cin) {
_ecm_db_connection_ref(cin);
}
spin_unlock_bh(&ecm_db_lock);
return cin;
}
EXPORT_SYMBOL(ecm_db_connection_get_and_ref_next);
/*
* ecm_db_mappings_get_and_ref_first()
* Obtain a ref to the first mapping instance, if any
*/
struct ecm_db_mapping_instance *ecm_db_mappings_get_and_ref_first(void)
{
struct ecm_db_mapping_instance *mi;
spin_lock_bh(&ecm_db_lock);
mi = ecm_db_mappings;
if (mi) {
_ecm_db_mapping_ref(mi);
}
spin_unlock_bh(&ecm_db_lock);
return mi;
}
EXPORT_SYMBOL(ecm_db_mappings_get_and_ref_first);
/*
* ecm_db_mapping_get_and_ref_next()
* Return the next mapping in the list given a mapping
*/
struct ecm_db_mapping_instance *ecm_db_mapping_get_and_ref_next(struct ecm_db_mapping_instance *mi)
{
struct ecm_db_mapping_instance *min;
DEBUG_CHECK_MAGIC(mi, ECM_DB_MAPPING_INSTANCE_MAGIC, "%p: magic failed", mi);
spin_lock_bh(&ecm_db_lock);
min = mi->next;
if (min) {
_ecm_db_mapping_ref(min);
}
spin_unlock_bh(&ecm_db_lock);
return min;
}
EXPORT_SYMBOL(ecm_db_mapping_get_and_ref_next);
/*
* ecm_db_hosts_get_and_ref_first()
* Obtain a ref to the first host instance, if any
*/
struct ecm_db_host_instance *ecm_db_hosts_get_and_ref_first(void)
{
struct ecm_db_host_instance *hi;
spin_lock_bh(&ecm_db_lock);
hi = ecm_db_hosts;
if (hi) {
_ecm_db_host_ref(hi);
}
spin_unlock_bh(&ecm_db_lock);
return hi;
}
EXPORT_SYMBOL(ecm_db_hosts_get_and_ref_first);
/*
* ecm_db_host_get_and_ref_next()
* Return the next host in the list given a host
*/
struct ecm_db_host_instance *ecm_db_host_get_and_ref_next(struct ecm_db_host_instance *hi)
{
struct ecm_db_host_instance *hin;
DEBUG_CHECK_MAGIC(hi, ECM_DB_HOST_INSTANCE_MAGIC, "%p: magic failed", hi);
spin_lock_bh(&ecm_db_lock);
hin = hi->next;
if (hin) {
_ecm_db_host_ref(hin);
}
spin_unlock_bh(&ecm_db_lock);
return hin;
}
EXPORT_SYMBOL(ecm_db_host_get_and_ref_next);
/*
* ecm_db_listeners_get_and_ref_first()
* Obtain a ref to the first listener instance, if any
*/
static struct ecm_db_listener_instance *ecm_db_listeners_get_and_ref_first(void)
{
struct ecm_db_listener_instance *li;
spin_lock_bh(&ecm_db_lock);
li = ecm_db_listeners;
if (li) {
_ecm_db_listener_ref(li);
}
spin_unlock_bh(&ecm_db_lock);
return li;
}
/*
* ecm_db_listener_get_and_ref_next()
* Return the next listener in the list given a listener
*/
static struct ecm_db_listener_instance *ecm_db_listener_get_and_ref_next(struct ecm_db_listener_instance *li)
{
struct ecm_db_listener_instance *lin;
DEBUG_CHECK_MAGIC(li, ECM_DB_LISTENER_INSTANCE_MAGIC, "%p: magic failed", li);
spin_lock_bh(&ecm_db_lock);
lin = li->next;
if (lin) {
_ecm_db_listener_ref(lin);
}
spin_unlock_bh(&ecm_db_lock);
return lin;
}
/*
* ecm_db_nodes_get_and_ref_first()
* Obtain a ref to the first node instance, if any
*/
struct ecm_db_node_instance *ecm_db_nodes_get_and_ref_first(void)
{
struct ecm_db_node_instance *ni;
spin_lock_bh(&ecm_db_lock);
ni = ecm_db_nodes;
if (ni) {
_ecm_db_node_ref(ni);
}
spin_unlock_bh(&ecm_db_lock);
return ni;
}
EXPORT_SYMBOL(ecm_db_nodes_get_and_ref_first);
/*
* ecm_db_node_get_and_ref_next()
* Return the next node in the list given a node
*/
struct ecm_db_node_instance *ecm_db_node_get_and_ref_next(struct ecm_db_node_instance *ni)
{
struct ecm_db_node_instance *nin;
DEBUG_CHECK_MAGIC(ni, ECM_DB_NODE_INSTANCE_MAGIC, "%p: magic failed", ni);
spin_lock_bh(&ecm_db_lock);
nin = ni->next;
if (nin) {
_ecm_db_node_ref(nin);
}
spin_unlock_bh(&ecm_db_lock);
return nin;
}
EXPORT_SYMBOL(ecm_db_node_get_and_ref_next);
/*
* ecm_db_interfaces_get_and_ref_first()
* Obtain a ref to the first iface instance, if any
*/
struct ecm_db_iface_instance *ecm_db_interfaces_get_and_ref_first(void)
{
struct ecm_db_iface_instance *ii;
spin_lock_bh(&ecm_db_lock);
ii = ecm_db_interfaces;
if (ii) {
_ecm_db_iface_ref(ii);
}
spin_unlock_bh(&ecm_db_lock);
return ii;
}
EXPORT_SYMBOL(ecm_db_interfaces_get_and_ref_first);
/*
* ecm_db_interface_get_and_ref_next()
* Return the next iface in the list given a iface
*/
struct ecm_db_iface_instance *ecm_db_interface_get_and_ref_next(struct ecm_db_iface_instance *ii)
{
struct ecm_db_iface_instance *iin;
DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed", ii);
spin_lock_bh(&ecm_db_lock);
iin = ii->next;
if (iin) {
_ecm_db_iface_ref(iin);
}
spin_unlock_bh(&ecm_db_lock);
return iin;
}
EXPORT_SYMBOL(ecm_db_interface_get_and_ref_next);
/*
* ecm_db_connection_deref()
* Release reference to connection. Connection is removed from database on final deref and destroyed.
*/
int ecm_db_connection_deref(struct ecm_db_connection_instance *ci)
{
int32_t i;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
spin_lock_bh(&ecm_db_lock);
ci->refs--;
DEBUG_TRACE("%p: connection deref %d\n", ci, ci->refs);
DEBUG_ASSERT(ci->refs >= 0, "%p: ref wrap\n", ci);
if (ci->refs > 0) {
int refs = ci->refs;
spin_unlock_bh(&ecm_db_lock);
return refs;
}
/*
* Remove from database if inserted
*/
if (!ci->flags & ECM_DB_CONNECTION_FLAGS_INSERTED) {
spin_unlock_bh(&ecm_db_lock);
} else {
struct ecm_db_listener_instance *li;
struct ecm_db_iface_instance *iface_from;
struct ecm_db_iface_instance *iface_to;
struct ecm_db_iface_instance *iface_nat_from;
struct ecm_db_iface_instance *iface_nat_to;
/*
* Remove it from the connection hash table
*/
if (!ci->hash_prev) {
DEBUG_ASSERT(ecm_db_connection_table[ci->hash_index] == ci, "%p: hash table bad\n", ci);
ecm_db_connection_table[ci->hash_index] = ci->hash_next;
} else {
ci->hash_prev->hash_next = ci->hash_next;
}
if (ci->hash_next) {
ci->hash_next->hash_prev = ci->hash_prev;
}
ecm_db_connection_table_lengths[ci->hash_index]--;
DEBUG_ASSERT(ecm_db_connection_table_lengths[ci->hash_index] >= 0, "%p: invalid table len %d\n", ci, ecm_db_connection_table_lengths[ci->hash_index]);
/*
* Remove it from the connection serial hash table
*/
if (!ci->serial_hash_prev) {
DEBUG_ASSERT(ecm_db_connection_serial_table[ci->serial_hash_index] == ci, "%p: hash table bad\n", ci);
ecm_db_connection_serial_table[ci->serial_hash_index] = ci->serial_hash_next;
} else {
ci->serial_hash_prev->serial_hash_next = ci->serial_hash_next;
}
if (ci->serial_hash_next) {
ci->serial_hash_next->serial_hash_prev = ci->serial_hash_prev;
}
ecm_db_connection_serial_table_lengths[ci->serial_hash_index]--;
DEBUG_ASSERT(ecm_db_connection_serial_table_lengths[ci->serial_hash_index] >= 0, "%p: invalid table len %d\n", ci, ecm_db_connection_serial_table_lengths[ci->serial_hash_index]);
/*
* Remove from the global list
*/
if (!ci->prev) {
DEBUG_ASSERT(ecm_db_connections == ci, "%p: conn table bad\n", ci);
ecm_db_connections = ci->next;
} else {
ci->prev->next = ci->next;
}
if (ci->next) {
ci->next->prev = ci->prev;
}
/*
* Remove connection from the "from" mapping connection list
*/
if (!ci->from_prev) {
DEBUG_ASSERT(ci->mapping_from->from_connections == ci, "%p: from conn table bad\n", ci);
ci->mapping_from->from_connections = ci->from_next;
} else {
ci->from_prev->from_next = ci->from_next;
}
if (ci->from_next) {
ci->from_next->from_prev = ci->from_prev;
}
/*
* Remove connection from the "to" mapping connection list
*/
if (!ci->to_prev) {
DEBUG_ASSERT(ci->mapping_to->to_connections == ci, "%p: to conn table bad\n", ci);
ci->mapping_to->to_connections = ci->to_next;
} else {
ci->to_prev->to_next = ci->to_next;
}
if (ci->to_next) {
ci->to_next->to_prev = ci->to_prev;
}
/*
* Remove connection from the "from" NAT mapping connection list
*/
if (!ci->from_nat_prev) {
DEBUG_ASSERT(ci->mapping_nat_from->from_nat_connections == ci, "%p: nat from conn table bad\n", ci);
ci->mapping_nat_from->from_nat_connections = ci->from_nat_next;
} else {
ci->from_nat_prev->from_nat_next = ci->from_nat_next;
}
if (ci->from_nat_next) {
ci->from_nat_next->from_nat_prev = ci->from_nat_prev;
}
/*
* Remove connection from the "to" NAT mapping connection list
*/
if (!ci->to_nat_prev) {
DEBUG_ASSERT(ci->mapping_nat_to->to_nat_connections == ci, "%p: nat to conn table bad\n", ci);
ci->mapping_nat_to->to_nat_connections = ci->to_nat_next;
} else {
ci->to_nat_prev->to_nat_next = ci->to_nat_next;
}
if (ci->to_nat_next) {
ci->to_nat_next->to_nat_prev = ci->to_nat_prev;
}
/*
* Remove connection from the "from" iface connection list
*/
iface_from = ci->mapping_from->host->node->iface;
if (!ci->iface_from_prev) {
DEBUG_ASSERT(iface_from->from_connections == ci, "%p: iface from conn table bad\n", ci);
iface_from->from_connections = ci->iface_from_next;
} else {
ci->iface_from_prev->iface_from_next = ci->iface_from_next;
}
if (ci->iface_from_next) {
ci->iface_from_next->iface_from_prev = ci->iface_from_prev;
}
/*
* Remove connection from the "to" iface connection list
*/
iface_to = ci->mapping_to->host->node->iface;
if (!ci->iface_to_prev) {
DEBUG_ASSERT(iface_to->to_connections == ci, "%p: to conn table bad\n", ci);
iface_to->to_connections = ci->iface_to_next;
} else {
ci->iface_to_prev->iface_to_next = ci->iface_to_next;
}
if (ci->iface_to_next) {
ci->iface_to_next->iface_to_prev = ci->iface_to_prev;
}
/*
* Remove connection from the "from" NAT iface connection list
*/
iface_nat_from = ci->mapping_nat_from->host->node->iface;
if (!ci->iface_from_nat_prev) {
DEBUG_ASSERT(iface_nat_from->from_nat_connections == ci, "%p: nat from conn table bad\n", ci);
iface_nat_from->from_nat_connections = ci->iface_from_nat_next;
} else {
ci->iface_from_nat_prev->iface_from_nat_next = ci->iface_from_nat_next;
}
if (ci->iface_from_nat_next) {
ci->iface_from_nat_next->iface_from_nat_prev = ci->iface_from_nat_prev;
}
/*
* Remove connection from the "to" NAT iface connection list
*/
iface_nat_to = ci->mapping_nat_to->host->node->iface;
if (!ci->iface_to_nat_prev) {
DEBUG_ASSERT(iface_nat_to->to_nat_connections == ci, "%p: nat to conn table bad\n", ci);
iface_nat_to->to_nat_connections = ci->iface_to_nat_next;
} else {
ci->iface_to_nat_prev->iface_to_nat_next = ci->iface_to_nat_next;
}
if (ci->iface_to_nat_next) {
ci->iface_to_nat_next->iface_to_nat_prev = ci->iface_to_nat_prev;
}
/*
* Update the counters in the mappings
*/
if (ci->protocol == IPPROTO_UDP) {
ci->mapping_from->udp_from--;
ci->mapping_to->udp_to--;
ci->mapping_nat_from->udp_nat_from--;
ci->mapping_nat_to->udp_nat_to--;
} else if (ci->protocol == IPPROTO_TCP) {
ci->mapping_from->tcp_from--;
ci->mapping_to->tcp_to--;
ci->mapping_nat_from->tcp_nat_from--;
ci->mapping_nat_to->tcp_nat_to--;
}
ci->mapping_from->from--;
ci->mapping_to->to--;
ci->mapping_nat_from->nat_from--;
ci->mapping_nat_to->nat_to--;
/*
* Assert that the defunt timer has been detached
*/
DEBUG_ASSERT(ci->defunct_timer.group == ECM_DB_TIMER_GROUPS_MAX, "%p: unexpected timer group %d\n", ci, ci->defunct_timer.group);
/*
* Decrement protocol counter stats
*/
ecm_db_connection_count_by_protocol[ci->protocol]--;
DEBUG_ASSERT(ecm_db_connection_count_by_protocol[ci->protocol] >= 0, "%p: Invalid protocol count %d\n", ci, ecm_db_connection_count_by_protocol[ci->protocol]);
spin_unlock_bh(&ecm_db_lock);
/*
* Throw removed event to listeners
*/
DEBUG_TRACE("%p: Throw connection removed event\n", ci);
li = ecm_db_listeners_get_and_ref_first();
while (li) {
struct ecm_db_listener_instance *lin;
if (li->connection_removed) {
li->connection_removed(li->arg, ci);
}
/*
* Get next listener
*/
lin = ecm_db_listener_get_and_ref_next(li);
ecm_db_listener_deref(li);
li = lin;
}
}
/*
* Throw final event
*/
if (ci->final) {
ci->final(ci->arg);
}
/*
* Release instances to the objects referenced by the connection
*/
while (ci->assignments) {
struct ecm_classifier_instance *classi = ci->assignments;
ci->assignments = classi->ca_next;
classi->deref(classi);
}
if (ci->dci) {
ci->dci->base.deref((struct ecm_classifier_instance *)ci->dci);
}
if (ci->mapping_from) {
ecm_db_mapping_deref(ci->mapping_from);
}
if (ci->mapping_to) {
ecm_db_mapping_deref(ci->mapping_to);
}
if (ci->mapping_nat_from) {
ecm_db_mapping_deref(ci->mapping_nat_from);
}
if (ci->mapping_nat_to) {
ecm_db_mapping_deref(ci->mapping_nat_to);
}
if (ci->feci) {
ci->feci->deref(ci->feci);
}
/*
* Remove references to the interfaces in our heirarchy lists
*/
for (i = ci->from_interface_first; i < ECM_DB_IFACE_HEIRARCHY_MAX; ++i) {
DEBUG_TRACE("%p: from interface %d remove: %p\n", ci, i, ci->from_interfaces[i]);
ecm_db_iface_deref(ci->from_interfaces[i]);
}
for (i = ci->to_interface_first; i < ECM_DB_IFACE_HEIRARCHY_MAX; ++i) {
DEBUG_TRACE("%p: to interface %d remove: %p\n", ci, i, ci->to_interfaces[i]);
ecm_db_iface_deref(ci->to_interfaces[i]);
}
for (i = ci->from_nat_interface_first; i < ECM_DB_IFACE_HEIRARCHY_MAX; ++i) {
DEBUG_TRACE("%p: from nat interface %d remove: %p\n", ci, i, ci->from_nat_interfaces[i]);
ecm_db_iface_deref(ci->from_nat_interfaces[i]);
}
for (i = ci->to_nat_interface_first; i < ECM_DB_IFACE_HEIRARCHY_MAX; ++i) {
DEBUG_TRACE("%p: to nat interface %d remove: %p\n", ci, i, ci->to_nat_interfaces[i]);
ecm_db_iface_deref(ci->to_nat_interfaces[i]);
}
/*
* We can now destroy the instance
*/
DEBUG_CLEAR_MAGIC(ci);
kfree(ci);
/*
* Decrease global connection count
*/
spin_lock_bh(&ecm_db_lock);
ecm_db_connection_count--;
DEBUG_ASSERT(ecm_db_connection_count >= 0, "%p: connection count wrap\n", ci);
spin_unlock_bh(&ecm_db_lock);
return 0;
}
EXPORT_SYMBOL(ecm_db_connection_deref);
/*
* ecm_db_mapping_deref()
* Release ref to mapping, possibly removing it from the database and destroying it.
*/
int ecm_db_mapping_deref(struct ecm_db_mapping_instance *mi)
{
DEBUG_CHECK_MAGIC(mi, ECM_DB_MAPPING_INSTANCE_MAGIC, "%p: magic failed\n", mi);
spin_lock_bh(&ecm_db_lock);
mi->refs--;
DEBUG_TRACE("%p: mapping deref %d\n", mi, mi->refs);
DEBUG_ASSERT(mi->refs >= 0, "%p: ref wrap\n", mi);
if (mi->refs > 0) {
int refs = mi->refs;
spin_unlock_bh(&ecm_db_lock);
return refs;
}
DEBUG_ASSERT(!mi->from_connections && !mi->tcp_from && !mi->udp_from && !mi->from, "%p: from not null: %p, %d, %d, %d\n",
mi, mi->from_connections, mi->tcp_from, mi->udp_from, mi->from);
DEBUG_ASSERT(!mi->to_connections && !mi->tcp_to && !mi->udp_to && !mi->to, "%p: to not null: %p, %d, %d, %d\n",
mi, mi->to_connections, mi->tcp_to, mi->udp_to, mi->to);
DEBUG_ASSERT(!mi->from_nat_connections && !mi->tcp_nat_from && !mi->udp_nat_from && !mi->nat_from, "%p: nat_from not null: %p, %d, %d, %d\n",
mi, mi->from_nat_connections, mi->tcp_nat_from, mi->udp_nat_from, mi->nat_from);
DEBUG_ASSERT(!mi->to_nat_connections && !mi->tcp_nat_to && !mi->udp_nat_to && !mi->nat_to, "%p: nat_to not null: %p, %d, %d, %d\n",
mi, mi->to_nat_connections, mi->tcp_nat_to, mi->udp_nat_to, mi->nat_to);
/*
* Remove from database if inserted
*/
if (!mi->flags & ECM_DB_MAPPING_FLAGS_INSERTED) {
spin_unlock_bh(&ecm_db_lock);
} else {
struct ecm_db_listener_instance *li;
/*
* Remove from the global list
*/
if (!mi->prev) {
DEBUG_ASSERT(ecm_db_mappings == mi, "%p: mapping table bad\n", mi);
ecm_db_mappings = mi->next;
} else {
mi->prev->next = mi->next;
}
if (mi->next) {
mi->next->prev = mi->prev;
}
/*
* Unlink it from the mapping hash table
*/
if (!mi->hash_prev) {
DEBUG_ASSERT(ecm_db_mapping_table[mi->hash_index] == mi, "%p: hash table bad\n", mi);
ecm_db_mapping_table[mi->hash_index] = mi->hash_next;
} else {
mi->hash_prev->hash_next = mi->hash_next;
}
if (mi->hash_next) {
mi->hash_next->hash_prev = mi->hash_prev;
}
mi->hash_next = NULL;
mi->hash_prev = NULL;
ecm_db_mapping_table_lengths[mi->hash_index]--;
DEBUG_ASSERT(ecm_db_mapping_table_lengths[mi->hash_index] >= 0, "%p: invalid table len %d\n", mi, ecm_db_mapping_table_lengths[mi->hash_index]);
/*
* Unlink it from the host mapping list
*/
if (!mi->mapping_prev) {
DEBUG_ASSERT(mi->host->mappings == mi, "%p: mapping table bad\n", mi);
mi->host->mappings = mi->mapping_next;
} else {
mi->mapping_prev->mapping_next = mi->mapping_next;
}
if (mi->mapping_next) {
mi->mapping_next->mapping_prev = mi->mapping_prev;
}
mi->mapping_next = NULL;
mi->mapping_prev = NULL;
mi->host->mapping_count--;
spin_unlock_bh(&ecm_db_lock);
/*
* Throw removed event to listeners
*/
DEBUG_TRACE("%p: Throw mapping removed event\n", mi);
li = ecm_db_listeners_get_and_ref_first();
while (li) {
struct ecm_db_listener_instance *lin;
if (li->mapping_removed) {
li->mapping_removed(li->arg, mi);
}
/*
* Get next listener
*/
lin = ecm_db_listener_get_and_ref_next(li);
ecm_db_listener_deref(li);
li = lin;
}
}
/*
* Throw final event
*/
if (mi->final) {
mi->final(mi->arg);
}
/*
* Now release the host instance if the mapping had one
*/
if (mi->host) {
ecm_db_host_deref(mi->host);
}
/*
* We can now destroy the instance
*/
DEBUG_CLEAR_MAGIC(mi);
kfree(mi);
/*
* Decrease global mapping count
*/
spin_lock_bh(&ecm_db_lock);
ecm_db_mapping_count--;
DEBUG_ASSERT(ecm_db_mapping_count >= 0, "%p: mapping count wrap\n", mi);
spin_unlock_bh(&ecm_db_lock);
return 0;
}
EXPORT_SYMBOL(ecm_db_mapping_deref);
/*
* ecm_db_host_deref()
* Release a ref to a host instance, possibly causing removal from the database and destruction of the instance
*/
int ecm_db_host_deref(struct ecm_db_host_instance *hi)
{
DEBUG_CHECK_MAGIC(hi, ECM_DB_HOST_INSTANCE_MAGIC, "%p: magic failed\n", hi);
spin_lock_bh(&ecm_db_lock);
hi->refs--;
DEBUG_TRACE("%p: host deref %d\n", hi, hi->refs);
DEBUG_ASSERT(hi->refs >= 0, "%p: ref wrap\n", hi);
if (hi->refs > 0) {
int refs = hi->refs;
spin_unlock_bh(&ecm_db_lock);
return refs;
}
DEBUG_ASSERT((hi->mappings == NULL) && (hi->mapping_count == 0), "%p: mappings not null\n", hi);
/*
* Remove from database if inserted
*/
if (!hi->flags & ECM_DB_HOST_FLAGS_INSERTED) {
spin_unlock_bh(&ecm_db_lock);
} else {
struct ecm_db_listener_instance *li;
/*
* Remove from the global list
*/
if (!hi->prev) {
DEBUG_ASSERT(ecm_db_hosts == hi, "%p: host table bad\n", hi);
ecm_db_hosts = hi->next;
} else {
hi->prev->next = hi->next;
}
if (hi->next) {
hi->next->prev = hi->prev;
}
/*
* Unlink it from the host hash table
*/
if (!hi->hash_prev) {
DEBUG_ASSERT(ecm_db_host_table[hi->hash_index] == hi, "%p: hash table bad\n", hi);
ecm_db_host_table[hi->hash_index] = hi->hash_next;
} else {
hi->hash_prev->hash_next = hi->hash_next;
}
if (hi->hash_next) {
hi->hash_next->hash_prev = hi->hash_prev;
}
hi->hash_next = NULL;
hi->hash_prev = NULL;
ecm_db_host_table_lengths[hi->hash_index]--;
DEBUG_ASSERT(ecm_db_host_table_lengths[hi->hash_index] >= 0, "%p: invalid table len %d\n", hi, ecm_db_host_table_lengths[hi->hash_index]);
/*
* Unlink it from the node host list
*/
if (!hi->host_prev) {
DEBUG_ASSERT(hi->node->hosts == hi, "%p: hosts table bad\n", hi);
hi->node->hosts = hi->host_next;
} else {
hi->host_prev->host_next = hi->host_next;
}
if (hi->host_next) {
hi->host_next->host_prev = hi->host_prev;
}
hi->host_next = NULL;
hi->host_prev = NULL;
hi->node->host_count--;
spin_unlock_bh(&ecm_db_lock);
/*
* Throw removed event to listeners
*/
DEBUG_TRACE("%p: Throw host removed event\n", hi);
li = ecm_db_listeners_get_and_ref_first();
while (li) {
struct ecm_db_listener_instance *lin;
if (li->host_removed) {
li->host_removed(li->arg, hi);
}
/*
* Get next listener
*/
lin = ecm_db_listener_get_and_ref_next(li);
ecm_db_listener_deref(li);
li = lin;
}
}
/*
* Throw final event
*/
if (hi->final) {
hi->final(hi->arg);
}
/*
* Now release the node instance if the host had one
*/
if (hi->node) {
ecm_db_node_deref(hi->node);
}
/*
* We can now destroy the instance
*/
DEBUG_CLEAR_MAGIC(hi);
kfree(hi);
/*
* Decrease global host count
*/
spin_lock_bh(&ecm_db_lock);
ecm_db_host_count--;
DEBUG_ASSERT(ecm_db_host_count >= 0, "%p: host count wrap\n", hi);
spin_unlock_bh(&ecm_db_lock);
return 0;
}
EXPORT_SYMBOL(ecm_db_host_deref);
/*
* ecm_db_node_deref()
* Deref a node. Removing it on the last ref and destroying it.
*/
int ecm_db_node_deref(struct ecm_db_node_instance *ni)
{
DEBUG_CHECK_MAGIC(ni, ECM_DB_NODE_INSTANCE_MAGIC, "%p: magic failed\n", ni);
spin_lock_bh(&ecm_db_lock);
ni->refs--;
DEBUG_TRACE("%p: node deref %d\n", ni, ni->refs);
DEBUG_ASSERT(ni->refs >= 0, "%p: ref wrap\n", ni);
if (ni->refs > 0) {
int refs = ni->refs;
spin_unlock_bh(&ecm_db_lock);
return refs;
}
DEBUG_ASSERT((ni->hosts == NULL) && (ni->host_count == 0), "%p: hosts not null\n", ni);
/*
* Remove from database if inserted
*/
if (!ni->flags & ECM_DB_NODE_FLAGS_INSERTED) {
spin_unlock_bh(&ecm_db_lock);
} else {
struct ecm_db_listener_instance *li;
/*
* Remove from the global list
*/
if (!ni->prev) {
DEBUG_ASSERT(ecm_db_nodes == ni, "%p: node table bad\n", ni);
ecm_db_nodes = ni->next;
} else {
ni->prev->next = ni->next;
}
if (ni->next) {
ni->next->prev = ni->prev;
}
/*
* Link out of hash table
*/
if (!ni->hash_prev) {
DEBUG_ASSERT(ecm_db_node_table[ni->hash_index] == ni, "%p: hash table bad\n", ni);
ecm_db_node_table[ni->hash_index] = ni->hash_next;
} else {
ni->hash_prev->hash_next = ni->hash_next;
}
if (ni->hash_next) {
ni->hash_next->hash_prev = ni->hash_prev;
}
ni->hash_next = NULL;
ni->hash_prev = NULL;
ecm_db_node_table_lengths[ni->hash_index]--;
DEBUG_ASSERT(ecm_db_node_table_lengths[ni->hash_index] >= 0, "%p: invalid table len %d\n", ni, ecm_db_node_table_lengths[ni->hash_index]);
/*
* Unlink it from the iface node list
*/
if (!ni->node_prev) {
DEBUG_ASSERT(ni->iface->nodes == ni, "%p: nodes table bad\n", ni);
ni->iface->nodes = ni->node_next;
} else {
ni->node_prev->node_next = ni->node_next;
}
if (ni->node_next) {
ni->node_next->node_prev = ni->node_prev;
}
ni->node_next = NULL;
ni->node_prev = NULL;
ni->iface->node_count--;
spin_unlock_bh(&ecm_db_lock);
/*
* Throw removed event to listeners
*/
DEBUG_TRACE("%p: Throw node removed event\n", ni);
li = ecm_db_listeners_get_and_ref_first();
while (li) {
struct ecm_db_listener_instance *lin;
if (li->node_removed) {
li->node_removed(li->arg, ni);
}
/*
* Get next listener
*/
lin = ecm_db_listener_get_and_ref_next(li);
ecm_db_listener_deref(li);
li = lin;
}
}
/*
* Throw final event
*/
if (ni->final) {
ni->final(ni->arg);
}
/*
* Now release the iface instance if the node had one
*/
if (ni->iface) {
ecm_db_iface_deref(ni->iface);
}
/*
* We can now destroy the instance
*/
DEBUG_CLEAR_MAGIC(ni);
kfree(ni);
/*
* Decrease global node count
*/
spin_lock_bh(&ecm_db_lock);
ecm_db_node_count--;
DEBUG_ASSERT(ecm_db_node_count >= 0, "%p: node count wrap\n", ni);
spin_unlock_bh(&ecm_db_lock);
return 0;
}
EXPORT_SYMBOL(ecm_db_node_deref);
/*
* ecm_db_iface_deref()
* Deref a interface instance, removing it from the database on the last ref release
*/
int ecm_db_iface_deref(struct ecm_db_iface_instance *ii)
{
DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed\n", ii);
/*
* Decrement reference count
*/
spin_lock_bh(&ecm_db_lock);
ii->refs--;
DEBUG_TRACE("%p: iface deref %d\n", ii, ii->refs);
DEBUG_ASSERT(ii->refs >= 0, "%p: ref wrap\n", ii);
if (ii->refs > 0) {
int refs = ii->refs;
spin_unlock_bh(&ecm_db_lock);
return refs;
}
DEBUG_ASSERT((ii->nodes == NULL) && (ii->node_count == 0), "%p: nodes not null\n", ii);
/*
* Remove from database if inserted
*/
if (!ii->flags & ECM_DB_IFACE_FLAGS_INSERTED) {
spin_unlock_bh(&ecm_db_lock);
} else {
struct ecm_db_listener_instance *li;
/*
* Remove from the global list
*/
if (!ii->prev) {
DEBUG_ASSERT(ecm_db_interfaces == ii, "%p: interface table bad\n", ii);
ecm_db_interfaces = ii->next;
} else {
ii->prev->next = ii->next;
}
if (ii->next) {
ii->next->prev = ii->prev;
}
/*
* Link out of hash table
*/
if (!ii->hash_prev) {
DEBUG_ASSERT(ecm_db_iface_table[ii->hash_index] == ii, "%p: hash table bad got %p for hash index %u\n", ii, ecm_db_iface_table[ii->hash_index], ii->hash_index);
ecm_db_iface_table[ii->hash_index] = ii->hash_next;
} else {
ii->hash_prev->hash_next = ii->hash_next;
}
if (ii->hash_next) {
ii->hash_next->hash_prev = ii->hash_prev;
}
ii->hash_next = NULL;
ii->hash_prev = NULL;
ecm_db_iface_table_lengths[ii->hash_index]--;
DEBUG_ASSERT(ecm_db_iface_table_lengths[ii->hash_index] >= 0, "%p: invalid table len %d\n", ii, ecm_db_iface_table_lengths[ii->hash_index]);
spin_unlock_bh(&ecm_db_lock);
/*
* Throw removed event to listeners
*/
DEBUG_TRACE("%p: Throw iface removed event\n", ii);
li = ecm_db_listeners_get_and_ref_first();
while (li) {
struct ecm_db_listener_instance *lin;
if (li->iface_removed) {
li->iface_removed(li->arg, ii);
}
/*
* Get next listener
*/
lin = ecm_db_listener_get_and_ref_next(li);
ecm_db_listener_deref(li);
li = lin;
}
}
/*
* Throw final event
*/
if (ii->final) {
ii->final(ii->arg);
}
/*
* We can now destroy the instance
*/
DEBUG_CLEAR_MAGIC(ii);
kfree(ii);
/*
* Decrease global interface count
*/
spin_lock_bh(&ecm_db_lock);
ecm_db_iface_count--;
DEBUG_ASSERT(ecm_db_iface_count >= 0, "%p: iface count wrap\n", ii);
spin_unlock_bh(&ecm_db_lock);
return 0;
}
EXPORT_SYMBOL(ecm_db_iface_deref);
/*
* ecm_db_listener_deref()
* Release reference to listener.
*
* On final reference release listener shall be removed from the database.
*/
int ecm_db_listener_deref(struct ecm_db_listener_instance *li)
{
struct ecm_db_listener_instance *cli;
struct ecm_db_listener_instance **cli_prev;
DEBUG_CHECK_MAGIC(li, ECM_DB_LISTENER_INSTANCE_MAGIC, "%p: magic failed", li);
spin_lock_bh(&ecm_db_lock);
li->refs--;
DEBUG_ASSERT(li->refs >= 0, "%p: ref wrap\n", li);
if (li->refs > 0) {
int refs;
refs = li->refs;
spin_unlock_bh(&ecm_db_lock);
return refs;
}
/*
* Instance is to be removed and destroyed.
* Link the listener out of the listener list.
*/
cli = ecm_db_listeners;
cli_prev = &ecm_db_listeners;
while (cli) {
if (cli == li) {
*cli_prev = cli->next;
break;
}
cli_prev = &cli->next;
cli = cli->next;
}
DEBUG_ASSERT(cli, "%p: not found\n", li);
spin_unlock_bh(&ecm_db_lock);
/*
* Invoke final callback
*/
if (li->final) {
li->final(li->arg);
}
DEBUG_CLEAR_MAGIC(li);
kfree(li);
/*
* Decrease global listener count
*/
spin_lock_bh(&ecm_db_lock);
ecm_db_listeners_count--;
DEBUG_ASSERT(ecm_db_listeners_count >= 0, "%p: listener count wrap\n", li);
spin_unlock_bh(&ecm_db_lock);
return 0;
}
EXPORT_SYMBOL(ecm_db_listener_deref);
/*
* ecm_db_connection_defunct_all()
* Make defunct ALL connections.
*
* This API is typically used in shutdown situations commanded by the user.
* NOTE: Ensure all front ends are stopped to avoid further connections being created while this is running.
*/
void ecm_db_connection_defunct_all(void)
{
struct ecm_db_connection_instance *ci;
DEBUG_INFO("Defuncting all\n");
/*
* Iterate all connections
*/
ci = ecm_db_connections_get_and_ref_first();
while (ci) {
struct ecm_db_connection_instance *cin;
DEBUG_TRACE("%p: defunct\n", ci);
ecm_db_connection_make_defunct(ci);
cin = ecm_db_connection_get_and_ref_next(ci);
ecm_db_connection_deref(ci);
ci = cin;
}
DEBUG_INFO("Defuncting complete\n");
}
EXPORT_SYMBOL(ecm_db_connection_defunct_all);
/*
* ecm_db_connection_generate_hash_index()
* Calculate the hash index.
*
* Note: The hash we produce is symmetric - i.e. we can swap the "from" and "to"
* details without generating a different hash index!
*/
static inline ecm_db_connection_hash_t ecm_db_connection_generate_hash_index(ip_addr_t host1_addr, uint32_t host1_port, ip_addr_t host2_addr, uint32_t host2_port, int protocol)
{
uint32_t temp;
uint32_t hash_val;
/*
* The hash function only uses both host 1 address/port, host 2 address/port
* and protocol fields.
*/
temp = (u32)host1_addr[0] + host1_port + (u32)host2_addr[0] + host2_port + (uint32_t)protocol;
hash_val = (temp >> 24) ^ (temp >> 16) ^ (temp >> 8) ^ temp;
return (ecm_db_connection_hash_t)(hash_val & (ECM_DB_CONNECTION_HASH_SLOTS - 1));
}
/*
* ecm_db_connection_generate_serial_hash_index()
* Calculate the serial hash index.
*/
static inline ecm_db_connection_serial_hash_t ecm_db_connection_generate_serial_hash_index(uint32_t serial)
{
return (ecm_db_connection_serial_hash_t)(serial & (ECM_DB_CONNECTION_SERIAL_HASH_SLOTS - 1));
}
/*
* ecm_db_mapping_generate_hash_index()
* Calculate the hash index.
*/
static inline ecm_db_mapping_hash_t ecm_db_mapping_generate_hash_index(ip_addr_t address, uint32_t port)
{
uint32_t temp;
uint32_t hash_val;
temp = (u32)address[0] + port;
hash_val = (temp >> 24) ^ (temp >> 16) ^ (temp >> 8) ^ temp;
return (ecm_db_mapping_hash_t)(hash_val & (ECM_DB_MAPPING_HASH_SLOTS - 1));
}
/*
* ecm_db_host_generate_hash_index()
* Calculate the hash index.
*/
static inline ecm_db_host_hash_t ecm_db_host_generate_hash_index(ip_addr_t address)
{
uint32_t temp;
uint32_t hash_val;
temp = (uint32_t)address[0];
hash_val = (temp >> 24) ^ (temp >> 16) ^ (temp >> 8) ^ temp;
return (ecm_db_host_hash_t)(hash_val & (ECM_DB_HOST_HASH_SLOTS - 1));
}
/*
* ecm_db_node_generate_hash_index()
* Calculate the hash index.
*/
static inline ecm_db_node_hash_t ecm_db_node_generate_hash_index(uint8_t *address)
{
uint32_t hash_val;
hash_val = (((uint32_t)(address[2] ^ address[4])) << 8) | (address[3] ^ address[5]);
hash_val &= (ECM_DB_NODE_HASH_SLOTS - 1);
return (ecm_db_node_hash_t)hash_val;
}
/*
* ecm_db_iface_generate_hash_index_sit()
* Calculate the hash index.
*/
static inline ecm_db_iface_hash_t ecm_db_iface_generate_hash_index_sit(ip_addr_t saddr, ip_addr_t daddr)
{
uint32_t temp;
uint32_t hash_val;
temp = (uint32_t )(saddr[0] ^ daddr[0]);
hash_val = (temp >> 24) ^ (temp >> 16) ^ (temp >> 8) ^ temp;
return (ecm_db_iface_hash_t)(hash_val & (ECM_DB_IFACE_HASH_SLOTS - 1));
}
/*
* ecm_db_iface_generate_hash_index_tunipip6()
* Calculate the hash index.
*/
static inline ecm_db_iface_hash_t ecm_db_iface_generate_hash_index_tunipip6(ip_addr_t saddr, ip_addr_t daddr)
{
uint32_t temp;
uint32_t hash_val;
temp = (uint32_t )(saddr[0] ^ daddr[0]);
hash_val = (temp >> 24) ^ (temp >> 16) ^ (temp >> 8) ^ temp;
return (ecm_db_iface_hash_t)(hash_val & (ECM_DB_IFACE_HASH_SLOTS - 1));
}
/*
* ecm_db_iface_generate_hash_index_ethernet()
* Calculate the hash index.
*/
static inline ecm_db_iface_hash_t ecm_db_iface_generate_hash_index_ethernet(uint8_t *address)
{
return (ecm_db_iface_hash_t)(address[5] & (ECM_DB_IFACE_HASH_SLOTS - 1));
}
/*
* ecm_db_iface_generate_hash_index_pppoe()
* Calculate the hash index.
*/
static inline ecm_db_iface_hash_t ecm_db_iface_generate_hash_index_pppoe(uint16_t pppoe_session_id)
{
return (ecm_db_iface_hash_t)(pppoe_session_id & (ECM_DB_IFACE_HASH_SLOTS - 1));
}
/*
* ecm_db_iface_generate_hash_index_unknown()
* Calculate the hash index.
*/
static inline ecm_db_iface_hash_t ecm_db_iface_generate_hash_index_unknown(uint32_t os_specific_ident)
{
return (ecm_db_iface_hash_t)(os_specific_ident & (ECM_DB_IFACE_HASH_SLOTS - 1));
}
/*
* ecm_db_iface_generate_hash_index_loopback()
* Calculate the hash index.
*/
static inline ecm_db_iface_hash_t ecm_db_iface_generate_hash_index_loopback(uint32_t os_specific_ident)
{
return (ecm_db_iface_hash_t)(os_specific_ident & (ECM_DB_IFACE_HASH_SLOTS - 1));
}
/*
* ecm_db_iface_generate_hash_index_ipsec_tunnel()
* Calculate the hash index.
* GGG TODO Flesh this out using actual tunnel endpoint keys
*/
static inline ecm_db_iface_hash_t ecm_db_iface_generate_hash_index_ipsec_tunnel(uint32_t os_specific_ident)
{
return (ecm_db_iface_hash_t)(os_specific_ident & (ECM_DB_IFACE_HASH_SLOTS - 1));
}
/*
* ecm_db_host_find_and_ref()
* Lookup and return a host reference if any
*/
struct ecm_db_host_instance *ecm_db_host_find_and_ref(ip_addr_t address)
{
ecm_db_host_hash_t hash_index;
struct ecm_db_host_instance *hi;
DEBUG_TRACE("Lookup host with addr " ECM_IP_ADDR_OCTAL_FMT "\n", ECM_IP_ADDR_TO_OCTAL(address));
/*
* Compute the hash chain index and prepare to walk the chain
*/
hash_index = ecm_db_host_generate_hash_index(address);
/*
* Iterate the chain looking for a host with matching details
*/
spin_lock_bh(&ecm_db_lock);
hi = ecm_db_host_table[hash_index];
while (hi) {
if (!ECM_IP_ADDR_MATCH(hi->address, address)) {
hi = hi->hash_next;
continue;
}
_ecm_db_host_ref(hi);
spin_unlock_bh(&ecm_db_lock);
DEBUG_TRACE("host found %p\n", hi);
return hi;
}
spin_unlock_bh(&ecm_db_lock);
DEBUG_TRACE("Host not found\n");
return NULL;
}
EXPORT_SYMBOL(ecm_db_host_find_and_ref);
/*
* ecm_db_node_find_and_ref()
* Lookup and return a node reference if any
*/
struct ecm_db_node_instance *ecm_db_node_find_and_ref(uint8_t *address)
{
ecm_db_node_hash_t hash_index;
struct ecm_db_node_instance *ni;
DEBUG_TRACE("Lookup node with addr %pM\n", address);
/*
* Compute the hash chain index and prepare to walk the chain
*/
hash_index = ecm_db_node_generate_hash_index(address);
/*
* Iterate the chain looking for a host with matching details
*/
spin_lock_bh(&ecm_db_lock);
ni = ecm_db_node_table[hash_index];
while (ni) {
if (memcmp(ni->address, address, ETH_ALEN)) {
ni = ni->hash_next;
continue;
}
_ecm_db_node_ref(ni);
spin_unlock_bh(&ecm_db_lock);
DEBUG_TRACE("node found %p\n", ni);
return ni;
}
spin_unlock_bh(&ecm_db_lock);
DEBUG_TRACE("Node not found\n");
return NULL;
}
EXPORT_SYMBOL(ecm_db_node_find_and_ref);
/*
* ecm_db_iface_ethernet_address_get()
* Obtain the ethernet address for an ethernet interface
*/
void ecm_db_iface_ethernet_address_get(struct ecm_db_iface_instance *ii, uint8_t *address)
{
DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed", ii);
DEBUG_ASSERT(ii->type == ECM_DB_IFACE_TYPE_ETHERNET, "%p: Bad type, expected ethernet, actual: %d\n", ii, ii->type);
spin_lock_bh(&ecm_db_lock);
memcpy(address, ii->type_info.ethernet.address, sizeof(ii->type_info.ethernet.address));
spin_unlock_bh(&ecm_db_lock);
}
EXPORT_SYMBOL(ecm_db_iface_ethernet_address_get);
/*
* ecm_db_iface_bridge_address_get()
* Obtain the ethernet address for a bridge interface
*/
void ecm_db_iface_bridge_address_get(struct ecm_db_iface_instance *ii, uint8_t *address)
{
DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed", ii);
DEBUG_ASSERT(ii->type == ECM_DB_IFACE_TYPE_BRIDGE, "%p: Bad type, expected bridge, actual: %d\n", ii, ii->type);
spin_lock_bh(&ecm_db_lock);
memcpy(address, ii->type_info.bridge.address, sizeof(ii->type_info.bridge.address));
spin_unlock_bh(&ecm_db_lock);
}
EXPORT_SYMBOL(ecm_db_iface_bridge_address_get);
struct ecm_db_iface_instance *ecm_db_iface_ifidx_find_and_ref_ethernet(uint8_t *address, int32_t ifidx)
{
ecm_db_iface_hash_t hash_index;
struct ecm_db_iface_instance *ii;
DEBUG_TRACE("Lookup ethernet iface with addr %pM\n", address);
/*
* Compute the hash chain index and prepare to walk the chain
*/
hash_index = ecm_db_iface_generate_hash_index_ethernet(address);
/*
* Iterate the chain looking for a host with matching details
*/
spin_lock_bh(&ecm_db_lock);
ii = ecm_db_iface_table[hash_index];
while (ii) {
if ((ii->type != ECM_DB_IFACE_TYPE_ETHERNET)
|| memcmp(ii->type_info.ethernet.address, address, ETH_ALEN)
|| ii->interface_identifier != ifidx) {
ii = ii->hash_next;
continue;
}
_ecm_db_iface_ref(ii);
spin_unlock_bh(&ecm_db_lock);
DEBUG_TRACE("iface found %p\n", ii);
return ii;
}
spin_unlock_bh(&ecm_db_lock);
DEBUG_TRACE("Iface not found\n");
return NULL;
}
EXPORT_SYMBOL(ecm_db_iface_ifidx_find_and_ref_ethernet);
/*
* ecm_db_iface_vlan_info_get()
* Get vlan interface specific information
*/
void ecm_db_iface_vlan_info_get(struct ecm_db_iface_instance *ii, struct ecm_db_interface_info_vlan *vlan_info)
{
DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed", ii);
DEBUG_ASSERT(ii->type == ECM_DB_IFACE_TYPE_VLAN, "%p: Bad type, expected vlan, actual: %d\n", ii, ii->type);
spin_lock_bh(&ecm_db_lock);
memcpy(vlan_info->address, ii->type_info.vlan.address, sizeof(ii->type_info.vlan.address));
vlan_info->vlan_tag = ii->type_info.vlan.vlan_tag;
vlan_info->vlan_tpid = ii->type_info.vlan.vlan_tpid;
spin_unlock_bh(&ecm_db_lock);
}
EXPORT_SYMBOL(ecm_db_iface_vlan_info_get);
/*
* ecm_db_iface_find_and_ref_vlan()
* Lookup and return a iface reference if any
*/
struct ecm_db_iface_instance *ecm_db_iface_find_and_ref_vlan(uint8_t *address, uint16_t vlan_tag, uint16_t vlan_tpid)
{
ecm_db_iface_hash_t hash_index;
struct ecm_db_iface_instance *ii;
DEBUG_TRACE("Lookup vlan iface with addr %pM, vlan tag: %x vlan tpid: %x\n", address, vlan_tag, vlan_tpid);
/*
* Compute the hash chain index and prepare to walk the chain
*/
hash_index = ecm_db_iface_generate_hash_index_ethernet(address);
/*
* Iterate the chain looking for a host with matching details
*/
spin_lock_bh(&ecm_db_lock);
ii = ecm_db_iface_table[hash_index];
while (ii) {
if ((ii->type != ECM_DB_IFACE_TYPE_VLAN) || (ii->type_info.vlan.vlan_tag != vlan_tag)
|| (ii->type_info.vlan.vlan_tpid != vlan_tpid)
|| memcmp(ii->type_info.vlan.address, address, ETH_ALEN)) {
ii = ii->hash_next;
continue;
}
_ecm_db_iface_ref(ii);
spin_unlock_bh(&ecm_db_lock);
DEBUG_TRACE("iface found %p\n", ii);
return ii;
}
spin_unlock_bh(&ecm_db_lock);
DEBUG_TRACE("Iface not found\n");
return NULL;
}
EXPORT_SYMBOL(ecm_db_iface_find_and_ref_vlan);
/*
* ecm_db_iface_find_and_ref_bridge()
* Lookup and return a iface reference if any
*/
struct ecm_db_iface_instance *ecm_db_iface_find_and_ref_bridge(uint8_t *address)
{
ecm_db_iface_hash_t hash_index;
struct ecm_db_iface_instance *ii;
DEBUG_TRACE("Lookup bridge iface with addr %pM\n", address);
/*
* Compute the hash chain index and prepare to walk the chain
*/
hash_index = ecm_db_iface_generate_hash_index_ethernet(address);
/*
* Iterate the chain looking for a host with matching details
*/
spin_lock_bh(&ecm_db_lock);
ii = ecm_db_iface_table[hash_index];
while (ii) {
if ((ii->type != ECM_DB_IFACE_TYPE_BRIDGE) || memcmp(ii->type_info.bridge.address, address, ETH_ALEN)) {
ii = ii->hash_next;
continue;
}
_ecm_db_iface_ref(ii);
spin_unlock_bh(&ecm_db_lock);
DEBUG_TRACE("iface found %p\n", ii);
return ii;
}
spin_unlock_bh(&ecm_db_lock);
DEBUG_TRACE("Iface not found\n");
return NULL;
}
EXPORT_SYMBOL(ecm_db_iface_find_and_ref_bridge);
/*
* ecm_db_iface_find_and_ref_lag()
* Lookup and return a iface reference if any
*/
struct ecm_db_iface_instance *ecm_db_iface_find_and_ref_lag(uint8_t *address)
{
ecm_db_iface_hash_t hash_index;
struct ecm_db_iface_instance *ii;
DEBUG_TRACE("Lookup lag iface with addr %pM\n", address);
/*
* Compute the hash chain index and prepare to walk the chain
*/
hash_index = ecm_db_iface_generate_hash_index_ethernet(address);
/*
* Iterate the chain looking for a host with matching details
*/
spin_lock_bh(&ecm_db_lock);
ii = ecm_db_iface_table[hash_index];
while (ii) {
if ((ii->type != ECM_DB_IFACE_TYPE_LAG) || memcmp(ii->type_info.lag.address, address, ETH_ALEN)) {
ii = ii->hash_next;
continue;
}
_ecm_db_iface_ref(ii);
spin_unlock_bh(&ecm_db_lock);
DEBUG_TRACE("iface found %p\n", ii);
return ii;
}
spin_unlock_bh(&ecm_db_lock);
DEBUG_TRACE("Iface not found\n");
return NULL;
}
EXPORT_SYMBOL(ecm_db_iface_find_and_ref_lag);
/*
* ecm_db_iface_pppoe_session_info_get()
* Get vlan interface specific information
*/
void ecm_db_iface_pppoe_session_info_get(struct ecm_db_iface_instance *ii, struct ecm_db_interface_info_pppoe *pppoe_info)
{
DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed", ii);
DEBUG_ASSERT(ii->type == ECM_DB_IFACE_TYPE_PPPOE, "%p: Bad type, expected pppoe, actual: %d\n", ii, ii->type);
spin_lock_bh(&ecm_db_lock);
memcpy(pppoe_info->remote_mac, ii->type_info.pppoe.remote_mac, sizeof(ii->type_info.pppoe.remote_mac));
pppoe_info->pppoe_session_id = ii->type_info.pppoe.pppoe_session_id;
spin_unlock_bh(&ecm_db_lock);
}
EXPORT_SYMBOL(ecm_db_iface_pppoe_session_info_get);
/*
* ecm_db_iface_find_and_ref_pppoe()
* Lookup and return a iface reference if any
*/
struct ecm_db_iface_instance *ecm_db_iface_find_and_ref_pppoe(uint16_t pppoe_session_id, uint8_t *remote_mac)
{
ecm_db_iface_hash_t hash_index;
struct ecm_db_iface_instance *ii;
DEBUG_TRACE("Lookup pppoe iface with addr %x\n", pppoe_session_id);
/*
* Compute the hash chain index and prepare to walk the chain
*/
hash_index = ecm_db_iface_generate_hash_index_pppoe(pppoe_session_id);
/*
* Iterate the chain looking for a host with matching details
*/
spin_lock_bh(&ecm_db_lock);
ii = ecm_db_iface_table[hash_index];
while (ii) {
if ((ii->type != ECM_DB_IFACE_TYPE_PPPOE)
|| (ii->type_info.pppoe.pppoe_session_id != pppoe_session_id)
|| memcmp(ii->type_info.pppoe.remote_mac, remote_mac, ETH_ALEN)) {
ii = ii->hash_next;
continue;
}
_ecm_db_iface_ref(ii);
spin_unlock_bh(&ecm_db_lock);
DEBUG_TRACE("iface found %p\n", ii);
return ii;
}
spin_unlock_bh(&ecm_db_lock);
DEBUG_TRACE("Iface not found\n");
return NULL;
}
EXPORT_SYMBOL(ecm_db_iface_find_and_ref_pppoe);
/*
* ecm_db_iface_find_and_ref_unknown()
* Lookup and return a iface reference if any
*/
struct ecm_db_iface_instance *ecm_db_iface_find_and_ref_unknown(uint32_t os_specific_ident)
{
ecm_db_iface_hash_t hash_index;
struct ecm_db_iface_instance *ii;
DEBUG_TRACE("Lookup unknown iface with addr %x (%u)\n", os_specific_ident, os_specific_ident);
/*
* Compute the hash chain index and prepare to walk the chain
*/
hash_index = ecm_db_iface_generate_hash_index_unknown(os_specific_ident);
/*
* Iterate the chain looking for a host with matching details
*/
spin_lock_bh(&ecm_db_lock);
ii = ecm_db_iface_table[hash_index];
while (ii) {
if ((ii->type != ECM_DB_IFACE_TYPE_UNKNOWN) || (ii->type_info.unknown.os_specific_ident != os_specific_ident)) {
ii = ii->hash_next;
continue;
}
_ecm_db_iface_ref(ii);
spin_unlock_bh(&ecm_db_lock);
DEBUG_TRACE("iface found %p\n", ii);
return ii;
}
spin_unlock_bh(&ecm_db_lock);
DEBUG_TRACE("Iface not found\n");
return NULL;
}
EXPORT_SYMBOL(ecm_db_iface_find_and_ref_unknown);
/*
* ecm_db_iface_find_and_ref_loopback()
* Lookup and return a iface reference if any
*/
struct ecm_db_iface_instance *ecm_db_iface_find_and_ref_loopback(uint32_t os_specific_ident)
{
ecm_db_iface_hash_t hash_index;
struct ecm_db_iface_instance *ii;
DEBUG_TRACE("Lookup loopback iface with addr %x (%u)\n", os_specific_ident, os_specific_ident);
/*
* Compute the hash chain index and prepare to walk the chain
*/
hash_index = ecm_db_iface_generate_hash_index_loopback(os_specific_ident);
/*
* Iterate the chain looking for a host with matching details
*/
spin_lock_bh(&ecm_db_lock);
ii = ecm_db_iface_table[hash_index];
while (ii) {
if ((ii->type != ECM_DB_IFACE_TYPE_LOOPBACK) || (ii->type_info.loopback.os_specific_ident != os_specific_ident)) {
ii = ii->hash_next;
continue;
}
_ecm_db_iface_ref(ii);
spin_unlock_bh(&ecm_db_lock);
DEBUG_TRACE("iface found %p\n", ii);
return ii;
}
spin_unlock_bh(&ecm_db_lock);
DEBUG_TRACE("Iface not found\n");
return NULL;
}
EXPORT_SYMBOL(ecm_db_iface_find_and_ref_loopback);
/*
* ecm_db_iface_find_and_ref_ipsec_tunnel()
* Lookup and return a iface reference if any.
* GGG TODO Flesh this out using tunnel endpoint keys
*/
struct ecm_db_iface_instance *ecm_db_iface_find_and_ref_ipsec_tunnel(uint32_t os_specific_ident)
{
ecm_db_iface_hash_t hash_index;
struct ecm_db_iface_instance *ii;
DEBUG_TRACE("Lookup ipsec_tunnel iface with addr %x (%u)\n", os_specific_ident, os_specific_ident);
/*
* Compute the hash chain index and prepare to walk the chain
*/
hash_index = ecm_db_iface_generate_hash_index_ipsec_tunnel(os_specific_ident);
/*
* Iterate the chain looking for a host with matching details
*/
spin_lock_bh(&ecm_db_lock);
ii = ecm_db_iface_table[hash_index];
while (ii) {
if ((ii->type != ECM_DB_IFACE_TYPE_IPSEC_TUNNEL) || (ii->type_info.ipsec_tunnel.os_specific_ident != os_specific_ident)) {
ii = ii->hash_next;
continue;
}
_ecm_db_iface_ref(ii);
spin_unlock_bh(&ecm_db_lock);
DEBUG_TRACE("iface found %p\n", ii);
return ii;
}
spin_unlock_bh(&ecm_db_lock);
DEBUG_TRACE("Iface not found\n");
return NULL;
}
EXPORT_SYMBOL(ecm_db_iface_find_and_ref_ipsec_tunnel);
/*
* ecm_db_iface_find_and_ref_sit()
* Lookup and return a iface reference if any
*/
struct ecm_db_iface_instance *ecm_db_iface_find_and_ref_sit(ip_addr_t saddr, ip_addr_t daddr)
{
ecm_db_iface_hash_t hash_index;
struct ecm_db_iface_instance *ii;
DEBUG_TRACE("Lookup sit (6-in-4) iface with saddr: " ECM_IP_ADDR_OCTAL_FMT ", daddr: " ECM_IP_ADDR_OCTAL_FMT "\n",
ECM_IP_ADDR_TO_OCTAL(saddr), ECM_IP_ADDR_TO_OCTAL(daddr));
/*
* Compute the hash chain index and prepare to walk the chain
*/
hash_index = ecm_db_iface_generate_hash_index_sit(saddr, daddr);
/*
* Iterate the chain looking for a host with matching details
*/
spin_lock_bh(&ecm_db_lock);
ii = ecm_db_iface_table[hash_index];
while (ii) {
if ((ii->type != ECM_DB_IFACE_TYPE_SIT)
|| !ECM_IP_ADDR_MATCH(ii->type_info.sit.saddr, saddr)
|| !ECM_IP_ADDR_MATCH(ii->type_info.sit.daddr, daddr)) {
ii = ii->hash_next;
continue;
}
_ecm_db_iface_ref(ii);
spin_unlock_bh(&ecm_db_lock);
DEBUG_TRACE("iface found %p\n", ii);
return ii;
}
spin_unlock_bh(&ecm_db_lock);
DEBUG_TRACE("Iface not found\n");
return NULL;
}
EXPORT_SYMBOL(ecm_db_iface_find_and_ref_sit);
/*
* ecm_db_iface_find_and_ref_tunipip6()
* Lookup and return a iface reference if any
*/
struct ecm_db_iface_instance *ecm_db_iface_find_and_ref_tunipip6(ip_addr_t saddr, ip_addr_t daddr)
{
ecm_db_iface_hash_t hash_index;
struct ecm_db_iface_instance *ii;
DEBUG_TRACE("Lookup TUNIPIP6 iface with saddr: " ECM_IP_ADDR_OCTAL_FMT ", daddr: " ECM_IP_ADDR_OCTAL_FMT "\n",
ECM_IP_ADDR_TO_OCTAL(saddr), ECM_IP_ADDR_TO_OCTAL(daddr));
/*
* Compute the hash chain index and prepare to walk the chain
*/
hash_index = ecm_db_iface_generate_hash_index_tunipip6(saddr, daddr);
/*
* Iterate the chain looking for a host with matching details
*/
spin_lock_bh(&ecm_db_lock);
ii = ecm_db_iface_table[hash_index];
while (ii) {
if ((ii->type != ECM_DB_IFACE_TYPE_TUNIPIP6)
|| !ECM_IP_ADDR_MATCH(ii->type_info.tunipip6.saddr, saddr)
|| !ECM_IP_ADDR_MATCH(ii->type_info.tunipip6.daddr, daddr)) {
ii = ii->hash_next;
continue;
}
_ecm_db_iface_ref(ii);
spin_unlock_bh(&ecm_db_lock);
DEBUG_TRACE("iface found %p\n", ii);
return ii;
}
spin_unlock_bh(&ecm_db_lock);
DEBUG_TRACE("Iface not found\n");
return NULL;
}
EXPORT_SYMBOL(ecm_db_iface_find_and_ref_tunipip6);
/*
* ecm_db_mapping_find_and_ref()
* Lookup and return a mapping reference if any.
*
* NOTE: For non-port based protocols the ports are expected to be -(protocol)
*/
struct ecm_db_mapping_instance *ecm_db_mapping_find_and_ref(ip_addr_t address, int port)
{
ecm_db_mapping_hash_t hash_index;
struct ecm_db_mapping_instance *mi;
DEBUG_TRACE("Lookup mapping with addr " ECM_IP_ADDR_OCTAL_FMT " and port %d\n", ECM_IP_ADDR_TO_OCTAL(address), port);
/*
* Compute the hash chain index and prepare to walk the chain
*/
hash_index = ecm_db_mapping_generate_hash_index(address, port);
/*
* Iterate the chain looking for a mapping with matching details
*/
spin_lock_bh(&ecm_db_lock);
mi = ecm_db_mapping_table[hash_index];
while (mi) {
if (mi->port != port) {
mi = mi->hash_next;
continue;
}
if (!ECM_IP_ADDR_MATCH(mi->host->address, address)) {
mi = mi->hash_next;
continue;
}
_ecm_db_mapping_ref(mi);
spin_unlock_bh(&ecm_db_lock);
DEBUG_TRACE("Mapping found %p\n", mi);
return mi;
}
spin_unlock_bh(&ecm_db_lock);
DEBUG_TRACE("Mapping not found\n");
return NULL;
}
EXPORT_SYMBOL(ecm_db_mapping_find_and_ref);
/*
* ecm_db_connection_find_and_ref()
* Locate a connection instance based on addressing, protocol and optional port information.
*
* NOTE: For non-port based protocols then ports are expected to be -(protocol).
*/
struct ecm_db_connection_instance *ecm_db_connection_find_and_ref(ip_addr_t host1_addr, ip_addr_t host2_addr, int protocol, int host1_port, int host2_port)
{
ecm_db_connection_hash_t hash_index;
struct ecm_db_connection_instance *ci;
DEBUG_TRACE("Lookup connection " ECM_IP_ADDR_OCTAL_FMT ":%d <> " ECM_IP_ADDR_OCTAL_FMT ":%d protocol %d\n", ECM_IP_ADDR_TO_OCTAL(host1_addr), host1_port, ECM_IP_ADDR_TO_OCTAL(host2_addr), host2_port, protocol);
/*
* Compute the hash chain index and prepare to walk the chain
*/
hash_index = ecm_db_connection_generate_hash_index(host1_addr, host1_port, host2_addr, host2_port, protocol);
/*
* Iterate the chain looking for a connection with matching details
*/
spin_lock_bh(&ecm_db_lock);
ci = ecm_db_connection_table[hash_index];
if (ci) {
_ecm_db_connection_ref(ci);
}
spin_unlock_bh(&ecm_db_lock);
while (ci) {
struct ecm_db_connection_instance *cin;
/*
* The use of unlikely() is liberally used because under fast-hit scenarios the connection would always be at the start of a chain
*/
if (unlikely(ci->protocol != protocol)) {
goto try_next;
}
if (unlikely(!ECM_IP_ADDR_MATCH(host1_addr, ci->mapping_from->host->address))) {
goto try_reverse;
}
if (unlikely(host1_port != ci->mapping_from->port)) {
goto try_reverse;
}
if (unlikely(!ECM_IP_ADDR_MATCH(host2_addr, ci->mapping_to->host->address))) {
goto try_reverse;
}
if (unlikely(host2_port != ci->mapping_to->port)) {
goto try_reverse;
}
goto connection_found;
try_reverse:
if (unlikely(!ECM_IP_ADDR_MATCH(host1_addr, ci->mapping_to->host->address))) {
goto try_next;
}
if (unlikely(host1_port != ci->mapping_to->port)) {
goto try_next;
}
if (unlikely(!ECM_IP_ADDR_MATCH(host2_addr, ci->mapping_from->host->address))) {
goto try_next;
}
if (unlikely(host2_port != ci->mapping_from->port)) {
goto try_next;
}
goto connection_found;
try_next:
spin_lock_bh(&ecm_db_lock);
cin = ci->hash_next;
if (cin) {
_ecm_db_connection_ref(cin);
}
spin_unlock_bh(&ecm_db_lock);
ecm_db_connection_deref(ci);
ci = cin;
}
DEBUG_TRACE("Connection not found\n");
return NULL;
connection_found:
DEBUG_TRACE("Connection found %p\n", ci);
/*
* Move this connection to the head of the hash chain.
* This will win for us with heavy hit connections - we bubble MRU to the front of the list to
* avoid too much chain walking.
*/
spin_lock_bh(&ecm_db_lock);
if (!ci->hash_prev) {
/*
* No prev pointer - ci is at the head of the list already
*/
DEBUG_ASSERT(ecm_db_connection_table[hash_index] == ci, "%p: hash table bad\n", ci);
spin_unlock_bh(&ecm_db_lock);
return ci;
}
/*
* Link out
*/
ci->hash_prev->hash_next = ci->hash_next;
if (ci->hash_next) {
ci->hash_next->hash_prev = ci->hash_prev;
}
/*
* Re-insert at the head.
* NOTE: We know that there is a head already that is different to ci.
*/
ci->hash_next = ecm_db_connection_table[hash_index];
ecm_db_connection_table[hash_index]->hash_prev = ci;
ecm_db_connection_table[hash_index] = ci;
ci->hash_prev = NULL;
spin_unlock_bh(&ecm_db_lock);
return ci;
}
EXPORT_SYMBOL(ecm_db_connection_find_and_ref);
/*
* ecm_db_connection_serial_find_and_ref()
* Locate a connection instance based on serial if it still exists
*/
struct ecm_db_connection_instance *ecm_db_connection_serial_find_and_ref(uint32_t serial)
{
ecm_db_connection_serial_hash_t serial_hash_index;
struct ecm_db_connection_instance *ci;
DEBUG_TRACE("Lookup connection serial: %u\n", serial);
/*
* Compute the hash chain index and prepare to walk the chain
*/
serial_hash_index = ecm_db_connection_generate_serial_hash_index(serial);
/*
* Iterate the chain looking for a connection with matching serial
*/
spin_lock_bh(&ecm_db_lock);
ci = ecm_db_connection_serial_table[serial_hash_index];
if (ci) {
_ecm_db_connection_ref(ci);
}
spin_unlock_bh(&ecm_db_lock);
while (ci) {
struct ecm_db_connection_instance *cin;
/*
* The use of likely() is used because under fast-hit scenarios the connection would always be at the start of a chain
*/
if (likely(ci->serial == serial)) {
goto connection_found;
}
/*
* Try next
*/
spin_lock_bh(&ecm_db_lock);
cin = ci->serial_hash_next;
if (cin) {
_ecm_db_connection_ref(cin);
}
spin_unlock_bh(&ecm_db_lock);
ecm_db_connection_deref(ci);
ci = cin;
}
DEBUG_TRACE("Connection not found\n");
return NULL;
connection_found:
DEBUG_TRACE("Connection found %p\n", ci);
/*
* Move this connection to the head of the hash chain.
* This will win for us with heavy hit connections - we bubble MRU to the front of the list to
* avoid too much chain walking.
*/
spin_lock_bh(&ecm_db_lock);
if (!ci->serial_hash_prev) {
/*
* No prev pointer - ci is at the head of the list already
*/
DEBUG_ASSERT(ecm_db_connection_serial_table[serial_hash_index] == ci, "%p: hash table bad\n", ci);
spin_unlock_bh(&ecm_db_lock);
return ci;
}
/*
* Link out
*/
ci->serial_hash_prev->serial_hash_next = ci->serial_hash_next;
if (ci->serial_hash_next) {
ci->serial_hash_next->serial_hash_prev = ci->serial_hash_prev;
}
/*
* Re-insert at the head.
* NOTE: We know that there is a head already that is different to ci.
*/
ci->serial_hash_next = ecm_db_connection_serial_table[serial_hash_index];
ecm_db_connection_serial_table[serial_hash_index]->serial_hash_prev = ci;
ecm_db_connection_serial_table[serial_hash_index] = ci;
ci->serial_hash_prev = NULL;
spin_unlock_bh(&ecm_db_lock);
return ci;
}
EXPORT_SYMBOL(ecm_db_connection_serial_find_and_ref);
/*
* ecm_db_mapping_connections_from_get_and_ref_first()
* Return a reference to the first connection made from this mapping
*/
struct ecm_db_connection_instance *ecm_db_mapping_connections_from_get_and_ref_first(struct ecm_db_mapping_instance *mi)
{
struct ecm_db_connection_instance *ci;
DEBUG_CHECK_MAGIC(mi, ECM_DB_MAPPING_INSTANCE_MAGIC, "%p: magic failed", mi);
spin_lock_bh(&ecm_db_lock);
ci = mi->from_connections;
if (ci) {
_ecm_db_connection_ref(ci);
}
spin_unlock_bh(&ecm_db_lock);
return ci;
}
EXPORT_SYMBOL(ecm_db_mapping_connections_from_get_and_ref_first);
/*
* ecm_db_mapping_connections_to_get_and_ref_first()
* Return a reference to the first connection made to this mapping
*/
struct ecm_db_connection_instance *ecm_db_mapping_connections_to_get_and_ref_first(struct ecm_db_mapping_instance *mi)
{
struct ecm_db_connection_instance *ci;
DEBUG_CHECK_MAGIC(mi, ECM_DB_MAPPING_INSTANCE_MAGIC, "%p: magic failed", mi);
spin_lock_bh(&ecm_db_lock);
ci = mi->to_connections;
if (ci) {
_ecm_db_connection_ref(ci);
}
spin_unlock_bh(&ecm_db_lock);
return ci;
}
EXPORT_SYMBOL(ecm_db_mapping_connections_to_get_and_ref_first);
/*
* ecm_db_mapping_connections_nat_from_get_and_ref_first()
* Return a reference to the first NAT connection made from this mapping
*/
struct ecm_db_connection_instance *ecm_db_mapping_connections_nat_from_get_and_ref_first(struct ecm_db_mapping_instance *mi)
{
struct ecm_db_connection_instance *ci;
DEBUG_CHECK_MAGIC(mi, ECM_DB_MAPPING_INSTANCE_MAGIC, "%p: magic failed", mi);
spin_lock_bh(&ecm_db_lock);
ci = mi->from_nat_connections;
if (ci) {
_ecm_db_connection_ref(ci);
}
spin_unlock_bh(&ecm_db_lock);
return ci;
}
EXPORT_SYMBOL(ecm_db_mapping_connections_nat_from_get_and_ref_first);
/*
* ecm_db_mapping_connections_nat_to_get_and_ref_first()
* Return a reference to the first NAT connection made to this mapping
*/
struct ecm_db_connection_instance *ecm_db_mapping_connections_nat_to_get_and_ref_first(struct ecm_db_mapping_instance *mi)
{
struct ecm_db_connection_instance *ci;
DEBUG_CHECK_MAGIC(mi, ECM_DB_MAPPING_INSTANCE_MAGIC, "%p: magic failed", mi);
spin_lock_bh(&ecm_db_lock);
ci = mi->to_nat_connections;
if (ci) {
_ecm_db_connection_ref(ci);
}
spin_unlock_bh(&ecm_db_lock);
return ci;
}
EXPORT_SYMBOL(ecm_db_mapping_connections_nat_to_get_and_ref_first);
/*
* ecm_db_connection_node_from_get_and_ref()
* Return node reference
*/
struct ecm_db_node_instance *ecm_db_connection_node_to_get_and_ref(struct ecm_db_connection_instance *ci)
{
struct ecm_db_node_instance *ni;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
spin_lock_bh(&ecm_db_lock);
ni = ci->mapping_to->host->node;
DEBUG_CHECK_MAGIC(ni, ECM_DB_NODE_INSTANCE_MAGIC, "%p: magic failed\n", ni);
_ecm_db_node_ref(ni);
spin_unlock_bh(&ecm_db_lock);
return ni;
}
EXPORT_SYMBOL(ecm_db_connection_node_to_get_and_ref);
/*
* ecm_db_connection_mapping_from_get_and_ref_next()
* Return reference to next connection in from mapping chain
*/
struct ecm_db_connection_instance *ecm_db_connection_mapping_from_get_and_ref_next(struct ecm_db_connection_instance *ci)
{
struct ecm_db_connection_instance *nci;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
spin_lock_bh(&ecm_db_lock);
nci = ci->from_next;
if (nci) {
_ecm_db_connection_ref(nci);
}
spin_unlock_bh(&ecm_db_lock);
return nci;
}
EXPORT_SYMBOL(ecm_db_connection_mapping_from_get_and_ref_next);
/*
* ecm_db_connection_mapping_to_get_and_ref_next()
* Return reference to next connection in to mapping chain
*/
struct ecm_db_connection_instance *ecm_db_connection_mapping_to_get_and_ref_next(struct ecm_db_connection_instance *ci)
{
struct ecm_db_connection_instance *nci;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
spin_lock_bh(&ecm_db_lock);
nci = ci->to_next;
if (nci) {
_ecm_db_connection_ref(nci);
}
spin_unlock_bh(&ecm_db_lock);
return nci;
}
EXPORT_SYMBOL(ecm_db_connection_mapping_to_get_and_ref_next);
/*
* ecm_db_connection_mapping_nat_from_get_and_ref_next()
* Return reference to next connection in from NAT mapping chain
*/
struct ecm_db_connection_instance *ecm_db_connection_mapping_nat_from_get_and_ref_next(struct ecm_db_connection_instance *ci)
{
struct ecm_db_connection_instance *nci;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
spin_lock_bh(&ecm_db_lock);
nci = ci->from_nat_next;
if (nci) {
_ecm_db_connection_ref(nci);
}
spin_unlock_bh(&ecm_db_lock);
return nci;
}
EXPORT_SYMBOL(ecm_db_connection_mapping_nat_from_get_and_ref_next);
/*
* ecm_db_connection_mapping_nat_to_get_and_ref_next()
* Return reference to next connection in to NAT mapping chain
*/
struct ecm_db_connection_instance *ecm_db_connection_mapping_nat_to_get_and_ref_next(struct ecm_db_connection_instance *ci)
{
struct ecm_db_connection_instance *nci;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
spin_lock_bh(&ecm_db_lock);
nci = ci->to_nat_next;
if (nci) {
_ecm_db_connection_ref(nci);
}
spin_unlock_bh(&ecm_db_lock);
return nci;
}
EXPORT_SYMBOL(ecm_db_connection_mapping_nat_to_get_and_ref_next);
/*
* ecm_db_iface_connections_from_get_and_ref_first()
* Return a reference to the first connection made from this iface
*/
struct ecm_db_connection_instance *ecm_db_iface_connections_from_get_and_ref_first(struct ecm_db_iface_instance *ii)
{
struct ecm_db_connection_instance *ci;
DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed", ii);
spin_lock_bh(&ecm_db_lock);
ci = ii->from_connections;
if (ci) {
_ecm_db_connection_ref(ci);
}
spin_unlock_bh(&ecm_db_lock);
return ci;
}
EXPORT_SYMBOL(ecm_db_iface_connections_from_get_and_ref_first);
/*
* ecm_db_iface_connections_to_get_and_ref_first()
* Return a reference to the first connection made to this iface
*/
struct ecm_db_connection_instance *ecm_db_iface_connections_to_get_and_ref_first(struct ecm_db_iface_instance *ii)
{
struct ecm_db_connection_instance *ci;
DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed", ii);
spin_lock_bh(&ecm_db_lock);
ci = ii->to_connections;
if (ci) {
_ecm_db_connection_ref(ci);
}
spin_unlock_bh(&ecm_db_lock);
return ci;
}
EXPORT_SYMBOL(ecm_db_iface_connections_to_get_and_ref_first);
/*
* ecm_db_iface_connections_nat_from_get_and_ref_first()
* Return a reference to the first NAT connection made from this iface
*/
struct ecm_db_connection_instance *ecm_db_iface_connections_nat_from_get_and_ref_first(struct ecm_db_iface_instance *ii)
{
struct ecm_db_connection_instance *ci;
DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed", ii);
spin_lock_bh(&ecm_db_lock);
ci = ii->from_nat_connections;
if (ci) {
_ecm_db_connection_ref(ci);
}
spin_unlock_bh(&ecm_db_lock);
return ci;
}
EXPORT_SYMBOL(ecm_db_iface_connections_nat_from_get_and_ref_first);
/*
* ecm_db_iface_connections_nat_to_get_and_ref_first()
* Return a reference to the first NAT connection made to this iface
*/
struct ecm_db_connection_instance *ecm_db_iface_connections_nat_to_get_and_ref_first(struct ecm_db_iface_instance *ii)
{
struct ecm_db_connection_instance *ci;
DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed", ii);
spin_lock_bh(&ecm_db_lock);
ci = ii->to_nat_connections;
if (ci) {
_ecm_db_connection_ref(ci);
}
spin_unlock_bh(&ecm_db_lock);
return ci;
}
EXPORT_SYMBOL(ecm_db_iface_connections_nat_to_get_and_ref_first);
/*
* ecm_db_connection_iface_from_get_and_ref_next()
* Return reference to next connection in from iface chain
*/
struct ecm_db_connection_instance *ecm_db_connection_iface_from_get_and_ref_next(struct ecm_db_connection_instance *ci)
{
struct ecm_db_connection_instance *nci;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
spin_lock_bh(&ecm_db_lock);
nci = ci->iface_from_next;
if (nci) {
_ecm_db_connection_ref(nci);
}
spin_unlock_bh(&ecm_db_lock);
return nci;
}
EXPORT_SYMBOL(ecm_db_connection_iface_from_get_and_ref_next);
/*
* ecm_db_connection_iface_to_get_and_ref_next()
* Return reference to next connection in to iface chain
*/
struct ecm_db_connection_instance *ecm_db_connection_iface_to_get_and_ref_next(struct ecm_db_connection_instance *ci)
{
struct ecm_db_connection_instance *nci;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
spin_lock_bh(&ecm_db_lock);
nci = ci->iface_to_next;
if (nci) {
_ecm_db_connection_ref(nci);
}
spin_unlock_bh(&ecm_db_lock);
return nci;
}
EXPORT_SYMBOL(ecm_db_connection_iface_to_get_and_ref_next);
/*
* ecm_db_connection_iface_nat_from_get_and_ref_next()
* Return reference to next connection in from NAT iface chain
*/
struct ecm_db_connection_instance *ecm_db_connection_iface_nat_from_get_and_ref_next(struct ecm_db_connection_instance *ci)
{
struct ecm_db_connection_instance *nci;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
spin_lock_bh(&ecm_db_lock);
nci = ci->iface_from_nat_next;
if (nci) {
_ecm_db_connection_ref(nci);
}
spin_unlock_bh(&ecm_db_lock);
return nci;
}
EXPORT_SYMBOL(ecm_db_connection_iface_nat_from_get_and_ref_next);
/*
* ecm_db_connection_iface_nat_to_get_and_ref_next()
* Return reference to next connection in to NAT iface chain
*/
struct ecm_db_connection_instance *ecm_db_connection_iface_nat_to_get_and_ref_next(struct ecm_db_connection_instance *ci)
{
struct ecm_db_connection_instance *nci;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
spin_lock_bh(&ecm_db_lock);
nci = ci->iface_to_nat_next;
if (nci) {
_ecm_db_connection_ref(nci);
}
spin_unlock_bh(&ecm_db_lock);
return nci;
}
EXPORT_SYMBOL(ecm_db_connection_iface_nat_to_get_and_ref_next);
/*
* ecm_db_node_hosts_get_and_ref_first()
* Return a reference to the first host associated with this node
*/
struct ecm_db_host_instance *ecm_db_node_hosts_get_and_ref_first(struct ecm_db_node_instance *ni)
{
struct ecm_db_host_instance *hi;
DEBUG_CHECK_MAGIC(ni, ECM_DB_NODE_INSTANCE_MAGIC, "%p: magic failed", ni);
spin_lock_bh(&ecm_db_lock);
hi = ni->hosts;
if (hi) {
_ecm_db_host_ref(hi);
}
spin_unlock_bh(&ecm_db_lock);
return hi;
}
EXPORT_SYMBOL(ecm_db_node_hosts_get_and_ref_first);
/*
* ecm_db_iface_nodes_get_and_ref_first()
* Return a reference to the first node made from this iface
*/
struct ecm_db_node_instance *ecm_db_iface_nodes_get_and_ref_first(struct ecm_db_iface_instance *ii)
{
struct ecm_db_node_instance *ni;
DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed", ii);
spin_lock_bh(&ecm_db_lock);
ni = ii->nodes;
if (ni) {
_ecm_db_node_ref(ni);
}
spin_unlock_bh(&ecm_db_lock);
return ni;
}
EXPORT_SYMBOL(ecm_db_iface_nodes_get_and_ref_first);
/*
* ecm_db_mapping_node_get_and_ref()
*/
struct ecm_db_node_instance *ecm_db_mapping_node_get_and_ref(struct ecm_db_mapping_instance *mi)
{
struct ecm_db_node_instance *ni;
DEBUG_CHECK_MAGIC(mi, ECM_DB_MAPPING_INSTANCE_MAGIC, "%p: magic failed\n", mi);
spin_lock_bh(&ecm_db_lock);
ni = mi->host->node;
DEBUG_CHECK_MAGIC(ni, ECM_DB_NODE_INSTANCE_MAGIC, "%p: magic failed\n", ni);
_ecm_db_node_ref(ni);
spin_unlock_bh(&ecm_db_lock);
return ni;
}
EXPORT_SYMBOL(ecm_db_mapping_node_get_and_ref);
/*
* ecm_db_mapping_iface_get_and_ref()
* Return a reference to the interface on which this mapping resides
*/
struct ecm_db_iface_instance *ecm_db_mapping_iface_get_and_ref(struct ecm_db_mapping_instance *mi)
{
struct ecm_db_iface_instance *ii;
DEBUG_CHECK_MAGIC(mi, ECM_DB_MAPPING_INSTANCE_MAGIC, "%p: magic failed\n", mi);
spin_lock_bh(&ecm_db_lock);
ii = mi->host->node->iface;
DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed\n", ii);
_ecm_db_iface_ref(ii);
spin_unlock_bh(&ecm_db_lock);
return ii;
}
EXPORT_SYMBOL(ecm_db_mapping_iface_get_and_ref);
/*
* ecm_db_mapping_host_get_and_ref()
*/
struct ecm_db_host_instance *ecm_db_mapping_host_get_and_ref(struct ecm_db_mapping_instance *mi)
{
DEBUG_CHECK_MAGIC(mi, ECM_DB_MAPPING_INSTANCE_MAGIC, "%p: magic failed\n", mi);
spin_lock_bh(&ecm_db_lock);
_ecm_db_host_ref(mi->host);
spin_unlock_bh(&ecm_db_lock);
return mi->host;
}
EXPORT_SYMBOL(ecm_db_mapping_host_get_and_ref);
/*
* ecm_db_host_node_get_and_ref()
*/
struct ecm_db_node_instance *ecm_db_host_node_get_and_ref(struct ecm_db_host_instance *hi)
{
DEBUG_CHECK_MAGIC(hi, ECM_DB_HOST_INSTANCE_MAGIC, "%p: magic failed\n", hi);
spin_lock_bh(&ecm_db_lock);
_ecm_db_node_ref(hi->node);
spin_unlock_bh(&ecm_db_lock);
return hi->node;
}
EXPORT_SYMBOL(ecm_db_host_node_get_and_ref);
/*
* ecm_db_node_iface_get_and_ref()
*/
struct ecm_db_iface_instance *ecm_db_node_iface_get_and_ref(struct ecm_db_node_instance *ni)
{
DEBUG_CHECK_MAGIC(ni, ECM_DB_NODE_INSTANCE_MAGIC, "%p: magic failed\n", ni);
spin_lock_bh(&ecm_db_lock);
_ecm_db_iface_ref(ni->iface);
spin_unlock_bh(&ecm_db_lock);
return ni->iface;
}
EXPORT_SYMBOL(ecm_db_node_iface_get_and_ref);
/*
* ecm_db_node_host_count_get()
* Return the number of hosts to this node
*/
int ecm_db_node_host_count_get(struct ecm_db_node_instance *ni)
{
int count;
DEBUG_CHECK_MAGIC(ni, ECM_DB_NODE_INSTANCE_MAGIC, "%p: magic failed\n", ni);
spin_lock_bh(&ecm_db_lock);
count = ni->host_count;
spin_unlock_bh(&ecm_db_lock);
return count;
}
EXPORT_SYMBOL(ecm_db_node_host_count_get);
/*
* ecm_db_iface_node_count_get()
* Return the number of nodes to this iface
*/
int ecm_db_iface_node_count_get(struct ecm_db_iface_instance *ii)
{
int count;
DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed\n", ii);
spin_lock_bh(&ecm_db_lock);
count = ii->node_count;
spin_unlock_bh(&ecm_db_lock);
return count;
}
EXPORT_SYMBOL(ecm_db_iface_node_count_get);
/*
* ecm_db_host_mapping_count_get()
* Return the number of mappings to this host
*/
int ecm_db_host_mapping_count_get(struct ecm_db_host_instance *hi)
{
int count;
DEBUG_CHECK_MAGIC(hi, ECM_DB_HOST_INSTANCE_MAGIC, "%p: magic failed\n", hi);
spin_lock_bh(&ecm_db_lock);
count = hi->mapping_count;
spin_unlock_bh(&ecm_db_lock);
return count;
}
EXPORT_SYMBOL(ecm_db_host_mapping_count_get);
/*
* ecm_db_mapping_connections_total_count_get()
* Return the total number of connections (NAT and non-NAT) this mapping has
*/
int ecm_db_mapping_connections_total_count_get(struct ecm_db_mapping_instance *mi)
{
int count;
DEBUG_CHECK_MAGIC(mi, ECM_DB_MAPPING_INSTANCE_MAGIC, "%p: magic failed\n", mi);
spin_lock_bh(&ecm_db_lock);
count = mi->from + mi->to + mi->nat_from + mi->nat_to;
DEBUG_ASSERT(count >= 0, "%p: Count overflow from: %d, to: %d, nat_from: %d, nat_to: %d\n", mi, mi->from, mi->to, mi->nat_from, mi->nat_to);
spin_unlock_bh(&ecm_db_lock);
return count;
}
EXPORT_SYMBOL(ecm_db_mapping_connections_total_count_get);
/*
* ecm_db_connection_mapping_from_get_and_ref()
* Return a reference to the from mapping of the connection
*/
struct ecm_db_mapping_instance *ecm_db_connection_mapping_from_get_and_ref(struct ecm_db_connection_instance *ci)
{
struct ecm_db_mapping_instance *mi;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
spin_lock_bh(&ecm_db_lock);
mi = ci->mapping_from;
_ecm_db_mapping_ref(mi);
spin_unlock_bh(&ecm_db_lock);
return mi;
}
EXPORT_SYMBOL(ecm_db_connection_mapping_from_get_and_ref);
/*
* ecm_db_connection_mapping_nat_from_get_and_ref()
* Return a reference to the from NAT mapping of the connection
*/
struct ecm_db_mapping_instance *ecm_db_connection_mapping_nat_from_get_and_ref(struct ecm_db_connection_instance *ci)
{
struct ecm_db_mapping_instance *mi;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
spin_lock_bh(&ecm_db_lock);
mi = ci->mapping_nat_from;
_ecm_db_mapping_ref(mi);
spin_unlock_bh(&ecm_db_lock);
return mi;
}
EXPORT_SYMBOL(ecm_db_connection_mapping_nat_from_get_and_ref);
/*
* ecm_db_connection_mapping_to_get_and_ref()
* Return a reference to the from mapping of the connection
*/
struct ecm_db_mapping_instance *ecm_db_connection_mapping_to_get_and_ref(struct ecm_db_connection_instance *ci)
{
struct ecm_db_mapping_instance *mi;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
spin_lock_bh(&ecm_db_lock);
mi = ci->mapping_to;
_ecm_db_mapping_ref(mi);
spin_unlock_bh(&ecm_db_lock);
return mi;
}
EXPORT_SYMBOL(ecm_db_connection_mapping_to_get_and_ref);
/*
* ecm_db_connection_mapping_to_nat_get_and_ref()
* Return a reference to the from NAT mapping of the connection
*/
struct ecm_db_mapping_instance *ecm_db_connection_mapping_nat_to_get_and_ref(struct ecm_db_connection_instance *ci)
{
struct ecm_db_mapping_instance *mi;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
spin_lock_bh(&ecm_db_lock);
mi = ci->mapping_nat_to;
_ecm_db_mapping_ref(mi);
spin_unlock_bh(&ecm_db_lock);
return mi;
}
EXPORT_SYMBOL(ecm_db_connection_mapping_nat_to_get_and_ref);
/*
* ecm_db_connection_node_from_get_and_ref()
* Return node reference
*/
struct ecm_db_node_instance *ecm_db_connection_node_from_get_and_ref(struct ecm_db_connection_instance *ci)
{
struct ecm_db_node_instance *ni;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
spin_lock_bh(&ecm_db_lock);
ni = ci->mapping_from->host->node;
_ecm_db_node_ref(ni);
spin_unlock_bh(&ecm_db_lock);
return ni;
}
EXPORT_SYMBOL(ecm_db_connection_node_from_get_and_ref);
/*
* ecm_db_timer_groups_check()
* Check for expired group entries, returns the number that have expired
*/
static uint32_t ecm_db_timer_groups_check(uint32_t time_now)
{
ecm_db_timer_group_t i;
uint32_t expired = 0;
DEBUG_TRACE("Timer groups check start %u\n", time_now);
/*
* Examine all timer groups for expired entries.
*/
for (i = 0; i < ECM_DB_TIMER_GROUPS_MAX; ++i) {
struct ecm_db_timer_group *timer_group;
/*
* The group tail tracks the oldest entry so that is what we examine.
*/
timer_group = &ecm_db_timer_groups[i];
spin_lock_bh(&ecm_db_lock);
while (timer_group->tail) {
struct ecm_db_timer_group_entry *tge;
tge = timer_group->tail;
if (tge->timeout > time_now) {
/*
* Not expired - and no further will be as they are in order
*/
break;
}
/*
* Has expired - remove the entry from the list and invoke the callback
* NOTE: We know the entry is at the tail of the group
*/
if (tge->prev) {
tge->prev->next = NULL;
} else {
/*
* First in the group
*/
DEBUG_ASSERT(timer_group->head == tge, "%p: bad head, expecting %p got %p\n", timer_group, tge, timer_group->head);
timer_group->head = NULL;
}
timer_group->tail = tge->prev;
tge->group = ECM_DB_TIMER_GROUPS_MAX;
spin_unlock_bh(&ecm_db_lock);
expired++;
DEBUG_TRACE("%p: Expired\n", tge);
tge->fn(tge->arg);
spin_lock_bh(&ecm_db_lock);
}
spin_unlock_bh(&ecm_db_lock);
}
spin_lock_bh(&ecm_db_lock);
time_now = ecm_db_time;
spin_unlock_bh(&ecm_db_lock);
DEBUG_TRACE("Timer groups check end %u, expired count %u\n", time_now, expired);
return expired;
}
/*
* ecm_db_connection_classifier_assign()
* Assign a classifier to the connection assigned classifier list.
*
* This adds the classifier in the ci->assignments list in priority order according to the classifier type.
* Only assigned classifiers are in this list, allowing fast retrival of in-order current assignments, avoiding the need to skip over unassigned classifiers.
* Because there is only one of each type of classifier the classifier is also recorded in an array, the position in which is its type value.
* This allows fast lookup based on type too.
*/
void ecm_db_connection_classifier_assign(struct ecm_db_connection_instance *ci, struct ecm_classifier_instance *new_ca)
{
struct ecm_classifier_instance *ca;
struct ecm_classifier_instance *ca_prev;
ecm_classifier_type_t new_ca_type;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
/*
* Get the type (which is also used as the priority)
*/
new_ca_type = new_ca->type_get(new_ca);
/*
* Connection holds ref to the classifier
*/
new_ca->ref(new_ca);
/*
* Find place to insert the classifier
*/
spin_lock_bh(&ecm_db_lock);
ca = ci->assignments;
ca_prev = NULL;
while (ca) {
ecm_classifier_type_t ca_type;
ca_type = ca->type_get(ca);
/*
* If new ca is less important that the current assigned classifier insert here
*/
if (new_ca_type < ca_type) {
break;
}
ca_prev = ca;
ca = ca->ca_next;
}
/*
* Insert new_ca before ca and after ca_prev.
*/
new_ca->ca_prev = ca_prev;
if (ca_prev) {
ca_prev->ca_next = new_ca;
} else {
DEBUG_ASSERT(ci->assignments == ca, "%p: Bad assigmnment list, expecting: %p, got: %p\n", ci, ca, ci->assignments);
ci->assignments = new_ca;
}
new_ca->ca_next = ca;
if (ca) {
ca->ca_prev = new_ca;
}
/*
* Insert based on type too
*/
DEBUG_ASSERT(ci->assignments_by_type[new_ca_type] == NULL, "%p: Only one of each type: %d may be registered, new: %p, existing, %p\n",
ci, new_ca_type, new_ca, ci->assignments_by_type[new_ca_type]);
ci->assignments_by_type[new_ca_type] = new_ca;
spin_unlock_bh(&ecm_db_lock);
}
EXPORT_SYMBOL(ecm_db_connection_classifier_assign);
/*
* ecm_db_connection_classifier_assignments_get_and_ref()
* Populate the given array with references to the currently assigned classifiers.
*
* This function returns the number of assignments starting from [0].
* [0] is the lowest priority classifier, [return_val - 1] is the highest priority.
* Release each classifier when you are done, for convenience use ecm_db_connection_assignments_release().
*
* NOTE: The array also contains the default classifier too which of course will always be at [0]
*
* WARNING: The array MUST be of size ECM_CLASSIFIER_TYPES.
*/
int ecm_db_connection_classifier_assignments_get_and_ref(struct ecm_db_connection_instance *ci, struct ecm_classifier_instance *assignments[])
{
int aci_count;
struct ecm_classifier_instance *aci;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
aci_count = 1;
assignments[0] = (struct ecm_classifier_instance *)ci->dci;
ci->dci->base.ref((struct ecm_classifier_instance *)ci->dci);
spin_lock_bh(&ecm_db_lock);
aci = ci->assignments;
while (aci) {
aci->ref(aci);
assignments[aci_count++] = aci;
aci = aci->ca_next;
}
spin_unlock_bh(&ecm_db_lock);
return aci_count;
}
EXPORT_SYMBOL(ecm_db_connection_classifier_assignments_get_and_ref);
/*
* ecm_db_connection_assignments_release()
* Release references to classifiers in the assignments array
*/
void ecm_db_connection_assignments_release(int assignment_count, struct ecm_classifier_instance *assignments[])
{
int i;
for (i = 0; i < assignment_count; ++i) {
struct ecm_classifier_instance *aci = assignments[i];
if (aci) {
aci->deref(aci);
}
}
}
EXPORT_SYMBOL(ecm_db_connection_assignments_release);
/*
* ecm_db_connection_assigned_classifier_find_and_ref()
* Return a ref to classifier of the requested type, if found
*/
struct ecm_classifier_instance *ecm_db_connection_assigned_classifier_find_and_ref(struct ecm_db_connection_instance *ci, ecm_classifier_type_t type)
{
struct ecm_classifier_instance *ca;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
spin_lock_bh(&ecm_db_lock);
ca = ci->assignments_by_type[type];
if (ca) {
ca->ref(ca);
}
spin_unlock_bh(&ecm_db_lock);
return ca;
}
EXPORT_SYMBOL(ecm_db_connection_assigned_classifier_find_and_ref);
/*
* ecm_db_connection_classifier_unassign()
* Unassign a classifier
*/
void ecm_db_connection_classifier_unassign(struct ecm_db_connection_instance *ci, struct ecm_classifier_instance *cci)
{
ecm_classifier_type_t ca_type;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
DEBUG_ASSERT(cci->type_get(cci) != ECM_CLASSIFIER_TYPE_DEFAULT, "%p: Cannot unassign default", ci);
/*
* Get the type
*/
ca_type = cci->type_get(cci);
DEBUG_TRACE("%p: Unassign type: %d, classifier: %p\n", ci, ca_type, cci);
spin_lock_bh(&ecm_db_lock);
/*
* Remove from assignments_by_type
* NOTEL It is possible that in SMP this classifier has already been unassigned.
*/
if (ci->assignments_by_type[ca_type] == NULL) {
spin_unlock_bh(&ecm_db_lock);
DEBUG_TRACE("%p: Classifier type: %d already unassigned\n", ci, ca_type);
return;
}
ci->assignments_by_type[ca_type] = NULL;
/*
* Link out of assignments list
*/
if (cci->ca_prev) {
cci->ca_prev->ca_next = cci->ca_next;
} else {
DEBUG_ASSERT(ci->assignments == cci, "%p: Bad assigmnment list, expecting: %p, got: %p", ci, cci, ci->assignments);
ci->assignments = cci->ca_next;
}
if (cci->ca_next) {
cci->ca_next->ca_prev = cci->ca_prev;
}
spin_unlock_bh(&ecm_db_lock);
cci->deref(cci);
}
EXPORT_SYMBOL(ecm_db_connection_classifier_unassign);
/*
* ecm_db_connection_classifier_default_get_and_ref()
* Get a reference to default classifier associated with this connection
*/
struct ecm_classifier_default_instance *ecm_db_connection_classifier_default_get_and_ref(struct ecm_db_connection_instance *ci)
{
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
/*
* No need to lock this object - it cannot change
*/
ci->dci->base.ref((struct ecm_classifier_instance *)ci->dci);
return ci->dci;
}
EXPORT_SYMBOL(ecm_db_connection_classifier_default_get_and_ref);
/*
* ecm_db_connection_from_interfaces_get_and_ref()
* Return the interface heirarchy from which this connection is established.
*
* 'interfaces' MUST be an array as large as ECM_DB_IFACE_HEIRARCHY_MAX.
* Returns either ECM_DB_IFACE_HEIRARCHY_MAX if there are no interfaces / error.
* Returns the index into the interfaces[] of the first interface (so "for (i = <ret val>, i < ECM_DB_IFACE_HEIRARCHY_MAX; ++i)" works)
*
* Each interface is referenced on return, be sure to release them individually or use ecm_db_connection_interfaces_deref() instead.
*/
int32_t ecm_db_connection_from_interfaces_get_and_ref(struct ecm_db_connection_instance *ci, struct ecm_db_iface_instance *interfaces[])
{
int32_t n;
int32_t i;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
spin_lock_bh(&ecm_db_lock);
n = ci->from_interface_first;
for (i = n; i < ECM_DB_IFACE_HEIRARCHY_MAX; ++i) {
interfaces[i] = ci->from_interfaces[i];
_ecm_db_iface_ref(interfaces[i]);
}
spin_unlock_bh(&ecm_db_lock);
return n;
}
EXPORT_SYMBOL(ecm_db_connection_from_interfaces_get_and_ref);
/*
* ecm_db_connection_to_interfaces_get_and_ref()
* Return the interface heirarchy to which this connection is established.
*
* 'interfaces' MUST be an array as large as ECM_DB_IFACE_HEIRARCHY_MAX.
* Returns either ECM_DB_IFACE_HEIRARCHY_MAX if there are no interfaces / error.
* Returns the index into the interfaces[] of the first interface (so "for (i = <ret val>, i < ECM_DB_IFACE_HEIRARCHY_MAX; ++i)" works)
*
* Each interface is referenced on return, be sure to release them individually or use ecm_db_connection_interfaces_deref() instead.
*/
int32_t ecm_db_connection_to_interfaces_get_and_ref(struct ecm_db_connection_instance *ci, struct ecm_db_iface_instance *interfaces[])
{
int32_t n;
int32_t i;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
spin_lock_bh(&ecm_db_lock);
n = ci->to_interface_first;
for (i = n; i < ECM_DB_IFACE_HEIRARCHY_MAX; ++i) {
interfaces[i] = ci->to_interfaces[i];
_ecm_db_iface_ref(interfaces[i]);
}
spin_unlock_bh(&ecm_db_lock);
return n;
}
EXPORT_SYMBOL(ecm_db_connection_to_interfaces_get_and_ref);
/*
* ecm_db_connection_from_nat_interfaces_get_and_ref()
* Return the interface heirarchy from (nat) which this connection is established.
*
* 'interfaces' MUST be an array as large as ECM_DB_IFACE_HEIRARCHY_MAX.
* Returns either ECM_DB_IFACE_HEIRARCHY_MAX if there are no interfaces / error.
* Returns the index into the interfaces[] of the first interface (so "for (i = <ret val>, i < ECM_DB_IFACE_HEIRARCHY_MAX; ++i)" works)
*
* Each interface is referenced on return, be sure to release them individually or use ecm_db_connection_interfaces_deref() instead.
*/
int32_t ecm_db_connection_from_nat_interfaces_get_and_ref(struct ecm_db_connection_instance *ci, struct ecm_db_iface_instance *interfaces[])
{
int32_t n;
int32_t i;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
spin_lock_bh(&ecm_db_lock);
n = ci->from_nat_interface_first;
for (i = n; i < ECM_DB_IFACE_HEIRARCHY_MAX; ++i) {
interfaces[i] = ci->from_nat_interfaces[i];
_ecm_db_iface_ref(interfaces[i]);
}
spin_unlock_bh(&ecm_db_lock);
return n;
}
EXPORT_SYMBOL(ecm_db_connection_from_nat_interfaces_get_and_ref);
/*
* ecm_db_connection_to_nat_interfaces_get_and_ref()
* Return the interface heirarchy to (nat) which this connection is established.
*
* 'interfaces' MUST be an array as large as ECM_DB_IFACE_HEIRARCHY_MAX.
* Returns either ECM_DB_IFACE_HEIRARCHY_MAX if there are no interfaces / error.
* Returns the index into the interfaces[] of the first interface (so "for (i = <ret val>, i < ECM_DB_IFACE_HEIRARCHY_MAX; ++i)" works)
*
* Each interface is referenced on return, be sure to release them individually or use ecm_db_connection_interfaces_deref() instead.
*/
int32_t ecm_db_connection_to_nat_interfaces_get_and_ref(struct ecm_db_connection_instance *ci, struct ecm_db_iface_instance *interfaces[])
{
int32_t n;
int32_t i;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
spin_lock_bh(&ecm_db_lock);
n = ci->to_nat_interface_first;
for (i = n; i < ECM_DB_IFACE_HEIRARCHY_MAX; ++i) {
interfaces[i] = ci->to_nat_interfaces[i];
_ecm_db_iface_ref(interfaces[i]);
}
spin_unlock_bh(&ecm_db_lock);
return n;
}
EXPORT_SYMBOL(ecm_db_connection_to_nat_interfaces_get_and_ref);
/*
* ecm_db_connection_interfaces_deref()
* Release all interfaces in the given interfaces heirarchy array.
*
* 'first' is the number returned by one of the ecm_db_connection_xx_interfaces_get_and_ref().
* You should NOT have released any references to any of the interfaces in the array youself, this releases them all.
*/
void ecm_db_connection_interfaces_deref(struct ecm_db_iface_instance *interfaces[], int32_t first)
{
int32_t i;
DEBUG_ASSERT((first >= 0) && (first <= ECM_DB_IFACE_HEIRARCHY_MAX), "Bad first: %d\n", first);
for (i = first; i < ECM_DB_IFACE_HEIRARCHY_MAX; ++i) {
ecm_db_iface_deref(interfaces[i]);
}
}
EXPORT_SYMBOL(ecm_db_connection_interfaces_deref);
/*
* ecm_db_connection_from_interfaces_reset()
* Reset the from interfaces heirarchy with a new set of interfaces
*
* NOTE: This will mark the list as set even if you specify no list as a replacement.
* This is deliberate - it's stating that there is no list :-)
*/
void ecm_db_connection_from_interfaces_reset(struct ecm_db_connection_instance *ci, struct ecm_db_iface_instance *interfaces[], int32_t new_first)
{
struct ecm_db_iface_instance *old[ECM_DB_IFACE_HEIRARCHY_MAX];
int32_t old_first;
int32_t i;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
/*
* Iterate the from interface list, removing the old and adding in the new
*/
spin_lock_bh(&ecm_db_lock);
for (i = 0; i < ECM_DB_IFACE_HEIRARCHY_MAX; ++i) {
/*
* Put any previous interface into the old list
*/
old[i] = ci->from_interfaces[i];
ci->from_interfaces[i] = NULL;
if (i < new_first) {
continue;
}
ci->from_interfaces[i] = interfaces[i];
_ecm_db_iface_ref(ci->from_interfaces[i]);
}
/*
* Get old first and update to new first
*/
old_first = ci->from_interface_first;
ci->from_interface_first = new_first;
ci->from_interface_set = true;
spin_unlock_bh(&ecm_db_lock);
/*
* Release old
*/
ecm_db_connection_interfaces_deref(old, old_first);
}
EXPORT_SYMBOL(ecm_db_connection_from_interfaces_reset);
/*
* ecm_db_connection_to_interfaces_reset()
* Reset the to interfaces heirarchy with a new set of interfaces
*
* NOTE: This will mark the list as set even if you specify no list as a replacement.
* This is deliberate - it's stating that there is no list :-)
*/
void ecm_db_connection_to_interfaces_reset(struct ecm_db_connection_instance *ci, struct ecm_db_iface_instance *interfaces[], int32_t new_first)
{
struct ecm_db_iface_instance *old[ECM_DB_IFACE_HEIRARCHY_MAX];
int32_t old_first;
int32_t i;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
/*
* Iterate the to interface list, removing the old and adding in the new
*/
spin_lock_bh(&ecm_db_lock);
for (i = 0; i < ECM_DB_IFACE_HEIRARCHY_MAX; ++i) {
/*
* Put any previous interface into the old list
*/
old[i] = ci->to_interfaces[i];
ci->to_interfaces[i] = NULL;
if (i < new_first) {
continue;
}
ci->to_interfaces[i] = interfaces[i];
_ecm_db_iface_ref(ci->to_interfaces[i]);
}
/*
* Get old first and update to new first
*/
old_first = ci->to_interface_first;
ci->to_interface_first = new_first;
ci->to_interface_set = true;
spin_unlock_bh(&ecm_db_lock);
/*
* Release old
*/
ecm_db_connection_interfaces_deref(old, old_first);
}
EXPORT_SYMBOL(ecm_db_connection_to_interfaces_reset);
/*
* ecm_db_connection_from_nat_interfaces_reset()
* Reset the from NAT interfaces heirarchy with a new set of interfaces
*
* NOTE: This will mark the list as set even if you specify no list as a replacement.
* This is deliberate - it's stating that there is no list :-)
*/
void ecm_db_connection_from_nat_interfaces_reset(struct ecm_db_connection_instance *ci, struct ecm_db_iface_instance *interfaces[], int32_t new_first)
{
struct ecm_db_iface_instance *old[ECM_DB_IFACE_HEIRARCHY_MAX];
int32_t old_first;
int32_t i;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
/*
* Iterate the from nat interface list, removing the old and adding in the new
*/
spin_lock_bh(&ecm_db_lock);
for (i = 0; i < ECM_DB_IFACE_HEIRARCHY_MAX; ++i) {
/*
* Put any previous interface into the old list
*/
old[i] = ci->from_nat_interfaces[i];
ci->from_nat_interfaces[i] = NULL;
if (i < new_first) {
continue;
}
ci->from_nat_interfaces[i] = interfaces[i];
_ecm_db_iface_ref(ci->from_nat_interfaces[i]);
}
/*
* Get old first and update to new first
*/
old_first = ci->from_nat_interface_first;
ci->from_nat_interface_first = new_first;
ci->from_nat_interface_set = true;
spin_unlock_bh(&ecm_db_lock);
/*
* Release old
*/
ecm_db_connection_interfaces_deref(old, old_first);
}
EXPORT_SYMBOL(ecm_db_connection_from_nat_interfaces_reset);
/*
* ecm_db_connection_to_nat_interfaces_reset()
* Reset the to NAT interfaces heirarchy with a new set of interfaces.
*
* NOTE: This will mark the list as set even if you specify no list as a replacement.
* This is deliberate - it's stating that there is no list :-)
*/
void ecm_db_connection_to_nat_interfaces_reset(struct ecm_db_connection_instance *ci, struct ecm_db_iface_instance *interfaces[], int32_t new_first)
{
struct ecm_db_iface_instance *old[ECM_DB_IFACE_HEIRARCHY_MAX];
int32_t old_first;
int32_t i;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
/*
* Iterate the to nat interface list, removing the old and adding in the new
*/
spin_lock_bh(&ecm_db_lock);
for (i = 0; i < ECM_DB_IFACE_HEIRARCHY_MAX; ++i) {
/*
* Put any previous interface into the old list
*/
old[i] = ci->to_nat_interfaces[i];
ci->to_nat_interfaces[i] = NULL;
if (i < new_first) {
continue;
}
ci->to_nat_interfaces[i] = interfaces[i];
_ecm_db_iface_ref(ci->to_nat_interfaces[i]);
}
/*
* Get old first and update to new first
*/
old_first = ci->to_nat_interface_first;
ci->to_nat_interface_first = new_first;
ci->to_nat_interface_set = true;
spin_unlock_bh(&ecm_db_lock);
/*
* Release old
*/
ecm_db_connection_interfaces_deref(old, old_first);
}
EXPORT_SYMBOL(ecm_db_connection_to_nat_interfaces_reset);
/*
* ecm_db_connection_to_nat_interfaces_get_count()
* Return the number of interfaces in the list
*/
int32_t ecm_db_connection_to_nat_interfaces_get_count(struct ecm_db_connection_instance *ci)
{
int32_t first;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
spin_lock_bh(&ecm_db_lock);
first = ci->to_nat_interface_first;
spin_unlock_bh(&ecm_db_lock);
return ECM_DB_IFACE_HEIRARCHY_MAX - first;
}
EXPORT_SYMBOL(ecm_db_connection_to_nat_interfaces_get_count);
/*
* ecm_db_connection_from_nat_interfaces_get_count()
* Return the number of interfaces in the list
*/
int32_t ecm_db_connection_from_nat_interfaces_get_count(struct ecm_db_connection_instance *ci)
{
int32_t first;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
spin_lock_bh(&ecm_db_lock);
first = ci->from_nat_interface_first;
spin_unlock_bh(&ecm_db_lock);
return ECM_DB_IFACE_HEIRARCHY_MAX - first;
}
EXPORT_SYMBOL(ecm_db_connection_from_nat_interfaces_get_count);
/*
* ecm_db_connection_to_interfaces_get_count()
* Return the number of interfaces in the list
*/
int32_t ecm_db_connection_to_interfaces_get_count(struct ecm_db_connection_instance *ci)
{
int32_t first;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
spin_lock_bh(&ecm_db_lock);
first = ci->to_interface_first;
spin_unlock_bh(&ecm_db_lock);
return ECM_DB_IFACE_HEIRARCHY_MAX - first;
}
EXPORT_SYMBOL(ecm_db_connection_to_interfaces_get_count);
/*
* ecm_db_connection_from_interfaces_get_count()
* Return the number of interfaces in the list
*/
int32_t ecm_db_connection_from_interfaces_get_count(struct ecm_db_connection_instance *ci)
{
int32_t first;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
spin_lock_bh(&ecm_db_lock);
first = ci->from_interface_first;
spin_unlock_bh(&ecm_db_lock);
return ECM_DB_IFACE_HEIRARCHY_MAX - first;
}
EXPORT_SYMBOL(ecm_db_connection_from_interfaces_get_count);
/*
* ecm_db_connection_to_interfaces_set_check()
* Returns true if the interface list has been set - even if set to an empty list!
*/
bool ecm_db_connection_to_interfaces_set_check(struct ecm_db_connection_instance *ci)
{
bool set;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
spin_lock_bh(&ecm_db_lock);
set = ci->to_interface_set;
spin_unlock_bh(&ecm_db_lock);
return set;
}
EXPORT_SYMBOL(ecm_db_connection_to_interfaces_set_check);
/*
* ecm_db_connection_from_interfaces_set_check()
* Returns true if the interface list has been set - even if set to an empty list!
*/
bool ecm_db_connection_from_interfaces_set_check(struct ecm_db_connection_instance *ci)
{
bool set;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
spin_lock_bh(&ecm_db_lock);
set = ci->from_interface_set;
spin_unlock_bh(&ecm_db_lock);
return set;
}
EXPORT_SYMBOL(ecm_db_connection_from_interfaces_set_check);
/*
* ecm_db_connection_to_nat_interfaces_set_check()
* Returns true if the interface list has been set - even if set to an empty list!
*/
bool ecm_db_connection_to_nat_interfaces_set_check(struct ecm_db_connection_instance *ci)
{
bool set;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
spin_lock_bh(&ecm_db_lock);
set = ci->to_nat_interface_set;
spin_unlock_bh(&ecm_db_lock);
return set;
}
EXPORT_SYMBOL(ecm_db_connection_to_nat_interfaces_set_check);
/*
* ecm_db_connection_from_nat_interfaces_set_check()
* Returns true if the interface list has been set - even if set to an empty list!
*/
bool ecm_db_connection_from_nat_interfaces_set_check(struct ecm_db_connection_instance *ci)
{
bool set;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
spin_lock_bh(&ecm_db_lock);
set = ci->from_nat_interface_set;
spin_unlock_bh(&ecm_db_lock);
return set;
}
EXPORT_SYMBOL(ecm_db_connection_from_nat_interfaces_set_check);
/*
* ecm_db_connection_from_interfaces_clear()
* Clear down the interfaces list, marking the list as not set
*/
void ecm_db_connection_from_interfaces_clear(struct ecm_db_connection_instance *ci)
{
struct ecm_db_iface_instance *discard[ECM_DB_IFACE_HEIRARCHY_MAX];
int32_t discard_first;
int32_t i;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
spin_lock_bh(&ecm_db_lock);
for (i = ci->from_interface_first; i < ECM_DB_IFACE_HEIRARCHY_MAX; ++i) {
discard[i] = ci->from_interfaces[i];
}
discard_first = ci->from_interface_first;
ci->from_interface_set = false;
ci->from_interface_first = ECM_DB_IFACE_HEIRARCHY_MAX;
spin_unlock_bh(&ecm_db_lock);
/*
* Release previous
*/
ecm_db_connection_interfaces_deref(discard, discard_first);
}
EXPORT_SYMBOL(ecm_db_connection_from_interfaces_clear);
/*
* ecm_db_connection_from_nat_interfaces_clear()
* Clear down the interfaces list, marking the list as not set
*/
void ecm_db_connection_from_nat_interfaces_clear(struct ecm_db_connection_instance *ci)
{
struct ecm_db_iface_instance *discard[ECM_DB_IFACE_HEIRARCHY_MAX];
int32_t discard_first;
int32_t i;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
spin_lock_bh(&ecm_db_lock);
for (i = ci->from_nat_interface_first; i < ECM_DB_IFACE_HEIRARCHY_MAX; ++i) {
discard[i] = ci->from_nat_interfaces[i];
}
discard_first = ci->from_nat_interface_first;
ci->from_nat_interface_set = false;
ci->from_nat_interface_first = ECM_DB_IFACE_HEIRARCHY_MAX;
spin_unlock_bh(&ecm_db_lock);
/*
* Release previous
*/
ecm_db_connection_interfaces_deref(discard, discard_first);
}
EXPORT_SYMBOL(ecm_db_connection_from_nat_interfaces_clear);
/*
* ecm_db_connection_to_interfaces_clear()
* Clear down the interfaces list, marking the list as not set
*/
void ecm_db_connection_to_interfaces_clear(struct ecm_db_connection_instance *ci)
{
struct ecm_db_iface_instance *discard[ECM_DB_IFACE_HEIRARCHY_MAX];
int32_t discard_first;
int32_t i;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
spin_lock_bh(&ecm_db_lock);
for (i = ci->to_interface_first; i < ECM_DB_IFACE_HEIRARCHY_MAX; ++i) {
discard[i] = ci->to_interfaces[i];
}
discard_first = ci->to_interface_first;
ci->to_interface_set = false;
ci->to_interface_first = ECM_DB_IFACE_HEIRARCHY_MAX;
spin_unlock_bh(&ecm_db_lock);
/*
* Release previous
*/
ecm_db_connection_interfaces_deref(discard, discard_first);
}
EXPORT_SYMBOL(ecm_db_connection_to_interfaces_clear);
/*
* ecm_db_connection_to_nat_interfaces_clear()
* Clear down the interfaces list, marking the list as not set
*/
void ecm_db_connection_to_nat_interfaces_clear(struct ecm_db_connection_instance *ci)
{
struct ecm_db_iface_instance *discard[ECM_DB_IFACE_HEIRARCHY_MAX];
int32_t discard_first;
int32_t i;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
spin_lock_bh(&ecm_db_lock);
for (i = ci->to_nat_interface_first; i < ECM_DB_IFACE_HEIRARCHY_MAX; ++i) {
discard[i] = ci->to_nat_interfaces[i];
}
discard_first = ci->to_nat_interface_first;
ci->to_nat_interface_set = false;
ci->to_nat_interface_first = ECM_DB_IFACE_HEIRARCHY_MAX;
spin_unlock_bh(&ecm_db_lock);
/*
* Release previous
*/
ecm_db_connection_interfaces_deref(discard, discard_first);
}
EXPORT_SYMBOL(ecm_db_connection_to_nat_interfaces_clear);
/*
* ecm_db_connection_add()
* Add the connection into the database.
*
* NOTE: The parameters are DIRECTIONAL in terms of which mapping established the connection.
* NOTE: Dir confirms if this is an egressing or ingressing connection. This applies to firewalling front ends mostly. If INGRESS then mapping_from is the WAN side. If EGRESS then mapping_to is the WAN side.
*/
void ecm_db_connection_add(struct ecm_db_connection_instance *ci,
struct ecm_front_end_connection_instance *feci,
struct ecm_classifier_default_instance *dci,
struct ecm_db_mapping_instance *mapping_from, struct ecm_db_mapping_instance *mapping_to,
struct ecm_db_mapping_instance *mapping_nat_from, struct ecm_db_mapping_instance *mapping_nat_to,
int protocol, ecm_db_direction_t dir,
ecm_db_connection_final_callback_t final,
ecm_db_timer_group_t tg, bool is_routed,
void *arg)
{
ecm_db_connection_hash_t hash_index;
ecm_db_connection_serial_hash_t serial_hash_index;
struct ecm_db_listener_instance *li;
struct ecm_db_iface_instance *iface_from;
struct ecm_db_iface_instance *iface_to;
struct ecm_db_iface_instance *iface_nat_from;
struct ecm_db_iface_instance *iface_nat_to;
DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed\n", ci);
DEBUG_CHECK_MAGIC(mapping_from, ECM_DB_MAPPING_INSTANCE_MAGIC, "%p: magic failed\n", mapping_from);
DEBUG_CHECK_MAGIC(mapping_to, ECM_DB_MAPPING_INSTANCE_MAGIC, "%p: magic failed\n", mapping_to);
DEBUG_CHECK_MAGIC(mapping_nat_from, ECM_DB_MAPPING_INSTANCE_MAGIC, "%p: magic failed\n", mapping_nat_from);
DEBUG_CHECK_MAGIC(mapping_nat_to, ECM_DB_MAPPING_INSTANCE_MAGIC, "%p: magic failed\n", mapping_nat_to);
DEBUG_ASSERT((protocol >= 0) && (protocol <= 255), "%p: invalid protocol number %d\n", ci, protocol);
spin_lock_bh(&ecm_db_lock);
DEBUG_ASSERT(!(ci->flags & ECM_DB_CONNECTION_FLAGS_INSERTED), "%p: inserted\n", ci);
spin_unlock_bh(&ecm_db_lock);
/*
* Record owner arg and callbacks
*/
ci->final = final;
ci->arg = arg;
/*
* Take reference to the front end
*/
feci->ref(feci);
ci->feci = feci;
/*
* Take reference to the default classifier
*/
dci->base.ref((struct ecm_classifier_instance *)dci);
ci->dci = dci;
ci->assignments_by_type[ECM_CLASSIFIER_TYPE_DEFAULT] = (struct ecm_classifier_instance *)dci;
/*
* Connection takes references to the mappings
*/
ecm_db_mapping_ref(mapping_from);
ecm_db_mapping_ref(mapping_to);
ci->mapping_from = mapping_from;
ci->mapping_to = mapping_to;
ecm_db_mapping_ref(mapping_nat_from);
ecm_db_mapping_ref(mapping_nat_to);
ci->mapping_nat_from = mapping_nat_from;
ci->mapping_nat_to = mapping_nat_to;
/*
* Set the protocol and routed flag
*/
ci->protocol = protocol;
ci->is_routed = is_routed;
/*
* Set direction of connection
*/
ci->direction = dir;
/*
* Identify which hash chain this connection will go into
*/
hash_index = ecm_db_connection_generate_hash_index(mapping_from->host->address, mapping_from->port, mapping_to->host->address, mapping_to->port, protocol);
ci->hash_index = hash_index;
/*
* Identify which serial hash chain this connection will go into
*/
serial_hash_index = ecm_db_connection_generate_serial_hash_index(ci->serial);
ci->serial_hash_index = serial_hash_index;
/*
* Now we need to lock
*/
spin_lock_bh(&ecm_db_lock);
/*
* Increment protocol counter stats
*/
ecm_db_connection_count_by_protocol[protocol]++;
DEBUG_ASSERT(ecm_db_connection_count_by_protocol[protocol] > 0, "%p: Invalid protocol count %d\n", ci, ecm_db_connection_count_by_protocol[protocol]);
DEBUG_TRACE("c\n");
/*
* Set time
*/
ci->time_added = ecm_db_time;
/*
* Add connection into the global list
*/
ci->prev = NULL;
ci->next = ecm_db_connections;
if (ecm_db_connections) {
ecm_db_connections->prev = ci;
}
ecm_db_connections = ci;
/*
* Add this connection into the connections hash table
*/
ci->flags |= ECM_DB_CONNECTION_FLAGS_INSERTED;
/*
* Insert mapping into the connections hash table
*/
ci->hash_next = ecm_db_connection_table[hash_index];
if (ecm_db_connection_table[hash_index]) {
ecm_db_connection_table[hash_index]->hash_prev = ci;
}
ecm_db_connection_table[hash_index] = ci;
ecm_db_connection_table_lengths[hash_index]++;
DEBUG_ASSERT(ecm_db_connection_table_lengths[hash_index] > 0, "%p: invalid table len %d\n", ci, ecm_db_connection_table_lengths[hash_index]);
/*
* Insert connection into the connections serial hash table
*/
ci->serial_hash_next = ecm_db_connection_serial_table[serial_hash_index];
if (ecm_db_connection_serial_table[serial_hash_index]) {
ecm_db_connection_serial_table[serial_hash_index]->serial_hash_prev = ci;
}
ecm_db_connection_serial_table[serial_hash_index] = ci;
ecm_db_connection_serial_table_lengths[serial_hash_index]++;
DEBUG_ASSERT(ecm_db_connection_serial_table_lengths[serial_hash_index] > 0, "%p: invalid table len %d\n", ci, ecm_db_connection_serial_table_lengths[serial_hash_index]);
/*
* Add this connection into the FROM mapping
*/
ci->from_prev = NULL;
ci->from_next = mapping_from->from_connections;
if (mapping_from->from_connections) {
mapping_from->from_connections->from_prev = ci;
}
mapping_from->from_connections = ci;
/*
* Add this connection into the TO mapping
*/
ci->to_prev = NULL;
ci->to_next = mapping_to->to_connections;
if (mapping_to->to_connections) {
mapping_to->to_connections->to_prev = ci;
}
mapping_to->to_connections = ci;
/*
* Add this connection into the FROM NAT mapping
*/
ci->from_nat_prev = NULL;
ci->from_nat_next = mapping_nat_from->from_nat_connections;
if (mapping_nat_from->from_nat_connections) {
mapping_nat_from->from_nat_connections->from_nat_prev = ci;
}
mapping_nat_from->from_nat_connections = ci;
/*
* Add this connection into the TO NAT mapping
*/
ci->to_nat_prev = NULL;
ci->to_nat_next = mapping_nat_to->to_nat_connections;
if (mapping_nat_to->to_nat_connections) {
mapping_nat_to->to_nat_connections->to_nat_prev = ci;
}
mapping_nat_to->to_nat_connections = ci;
/*
* Add this connection into the FROM iface list of connections
* NOTE: There is no need to ref the iface because it will exist for as long as this connection exists
* due to the heirarchy of dependencies being kept by the database.
*/
iface_from = mapping_from->host->node->iface;
ci->iface_from_prev = NULL;
ci->iface_from_next = iface_from->from_connections;
if (iface_from->from_connections) {
iface_from->from_connections->iface_from_prev = ci;
}
iface_from->from_connections = ci;
/*
* Add this connection into the TO iface list of connections
* NOTE: There is no need to ref the iface because it will exist for as long as this connection exists
* due to the heirarchy of dependencies being kept by the database.
*/
iface_to = mapping_to->host->node->iface;
ci->iface_to_prev = NULL;
ci->iface_to_next = iface_to->to_connections;
if (iface_to->to_connections) {
iface_to->to_connections->iface_to_prev = ci;
}
iface_to->to_connections = ci;
/*
* Add this connection into the FROM NAT iface list of connections
* NOTE: There is no need to ref the iface because it will exist for as long as this connection exists
* due to the heirarchy of dependencies being kept by the database.
*/
iface_nat_from = mapping_nat_from->host->node->iface;
ci->iface_from_nat_prev = NULL;
ci->iface_from_nat_next = iface_nat_from->from_nat_connections;
if (iface_nat_from->from_nat_connections) {
iface_nat_from->from_nat_connections->iface_from_nat_prev = ci;
}
iface_nat_from->from_nat_connections = ci;
/*
* Add this connection into the TO NAT iface list of connections
* NOTE: There is no need to ref the iface because it will exist for as long as this connection exists
* due to the heirarchy of dependencies being kept by the database.
*/
iface_nat_to = mapping_nat_to->host->node->iface;
ci->iface_to_nat_prev = NULL;
ci->iface_to_nat_next = iface_nat_to->to_nat_connections;
if (iface_nat_to->to_nat_connections) {
iface_nat_to->to_nat_connections->iface_to_nat_prev = ci;
}
iface_nat_to->to_nat_connections = ci;
/*
* NOTE: The interface heirarchy lists are deliberately left empty - these are completed
* by the front end if it is appropriate to do so.
*/
/*
* Update the counters in the mapping
*/
if (protocol == IPPROTO_UDP) {
mapping_from->udp_from++;
mapping_to->udp_to++;
mapping_nat_from->udp_nat_from++;
mapping_nat_to->udp_nat_to++;
} else if (protocol == IPPROTO_TCP) {
mapping_from->tcp_from++;
mapping_to->tcp_to++;
mapping_nat_from->tcp_nat_from++;
mapping_nat_to->tcp_nat_to++;
}
mapping_from->from++;
mapping_to->to++;
mapping_nat_from->nat_from++;
mapping_nat_to->nat_to++;
/*
* Set the generation number
*/
ci->classifier_generation = ecm_db_classifier_generation;
spin_unlock_bh(&ecm_db_lock);
/*
* Throw add event to the listeners
*/
DEBUG_TRACE("%p: Throw connection added event\n", ci);
li = ecm_db_listeners_get_and_ref_first();
while (li) {
struct ecm_db_listener_instance *lin;
if (li->connection_added) {
li->connection_added(li->arg, ci);
}
/*
* Get next listener
*/
lin = ecm_db_listener_get_and_ref_next(li);
ecm_db_listener_deref(li);
li = lin;
}
/*
* Set timer group. 'ref' the connection to ensure it persists for the timer.
*/
ecm_db_connection_ref(ci);
ecm_db_timer_group_entry_set(&ci->defunct_timer, tg);
}
EXPORT_SYMBOL(ecm_db_connection_add);
/*
* ecm_db_mapping_add()
* Add a mapping instance into the database
*
* NOTE: The mapping will take a reference to the host instance.
*/
void ecm_db_mapping_add(struct ecm_db_mapping_instance *mi, struct ecm_db_host_instance *hi, int port,
ecm_db_mapping_final_callback_t final, void *arg)
{
ecm_db_mapping_hash_t hash_index;
struct ecm_db_listener_instance *li;
spin_lock_bh(&ecm_db_lock);
DEBUG_CHECK_MAGIC(mi, ECM_DB_MAPPING_INSTANCE_MAGIC, "%p: magic failed\n", mi);
DEBUG_CHECK_MAGIC(hi, ECM_DB_HOST_INSTANCE_MAGIC, "%p: magic failed\n", hi);
DEBUG_ASSERT(mi->from_connections == NULL, "%p: connections not null\n", mi);
DEBUG_ASSERT(mi->to_connections == NULL, "%p: connections not null\n", mi);
DEBUG_ASSERT(!(mi->flags & ECM_DB_MAPPING_FLAGS_INSERTED), "%p: inserted\n", mi);
DEBUG_ASSERT((hi->flags & ECM_DB_HOST_FLAGS_INSERTED), "%p: not inserted\n", hi);
DEBUG_ASSERT(!mi->from && !mi->to && !mi->tcp_from && !mi->tcp_to && !mi->udp_from && !mi->udp_to, "%p: count errors\n", mi);
spin_unlock_bh(&ecm_db_lock);
mi->arg = arg;
mi->final = final;
/*
* Compute hash table position for insertion
*/
hash_index = ecm_db_mapping_generate_hash_index(hi->address, port);
mi->hash_index = hash_index;
/*
* Record port
*/
mi->port = port;
/*
* Mapping takes a ref to the host
*/
ecm_db_host_ref(hi);
mi->host = hi;
/*
* Set time
*/
spin_lock_bh(&ecm_db_lock);
mi->time_added = ecm_db_time;
/*
* Record the mapping is inserted
*/
mi->flags |= ECM_DB_MAPPING_FLAGS_INSERTED;
/*
* Add into the global list
*/
mi->prev = NULL;
mi->next = ecm_db_mappings;
if (ecm_db_mappings) {
ecm_db_mappings->prev = mi;
}
ecm_db_mappings = mi;
/*
* Insert mapping into the mappings hash table
*/
mi->hash_next = ecm_db_mapping_table[hash_index];
if (ecm_db_mapping_table[hash_index]) {
ecm_db_mapping_table[hash_index]->hash_prev = mi;
}
ecm_db_mapping_table[hash_index] = mi;
ecm_db_mapping_table_lengths[hash_index]++;
DEBUG_ASSERT(ecm_db_mapping_table_lengths[hash_index] > 0, "%p: invalid table len %d\n", hi, ecm_db_mapping_table_lengths[hash_index]);
/*
* Insert mapping into the host mapping list
*/
mi->mapping_prev = NULL;
mi->mapping_next = hi->mappings;
if (hi->mappings) {
hi->mappings->mapping_prev = mi;
}
hi->mappings = mi;
hi->mapping_count++;
spin_unlock_bh(&ecm_db_lock);
/*
* Throw add event to the listeners
*/
DEBUG_TRACE("%p: Throw mapping added event\n", mi);
li = ecm_db_listeners_get_and_ref_first();
while (li) {
struct ecm_db_listener_instance *lin;
if (li->mapping_added) {
li->mapping_added(li->arg, mi);
}
/*
* Get next listener
*/
lin = ecm_db_listener_get_and_ref_next(li);
ecm_db_listener_deref(li);
li = lin;
}
}
EXPORT_SYMBOL(ecm_db_mapping_add);
/*
* ecm_db_host_add()
* Add a host instance into the database
*/
void ecm_db_host_add(struct ecm_db_host_instance *hi, struct ecm_db_node_instance *ni, ip_addr_t address, bool on_link,
ecm_db_host_final_callback_t final, void *arg)
{
ecm_db_host_hash_t hash_index;
struct ecm_db_listener_instance *li;
spin_lock_bh(&ecm_db_lock);
DEBUG_CHECK_MAGIC(hi, ECM_DB_HOST_INSTANCE_MAGIC, "%p: magic failed\n", hi);
DEBUG_CHECK_MAGIC(ni, ECM_DB_NODE_INSTANCE_MAGIC, "%p: magic failed\n", ni);
DEBUG_ASSERT((hi->mappings == NULL) && (hi->mapping_count == 0), "%p: mappings not null\n", hi);
DEBUG_ASSERT((hi->node == NULL), "%p: node not null\n", hi);
DEBUG_ASSERT(!(hi->flags & ECM_DB_HOST_FLAGS_INSERTED), "%p: inserted\n", hi);
spin_unlock_bh(&ecm_db_lock);
hi->arg = arg;
hi->final = final;
ECM_IP_ADDR_COPY(hi->address, address);
hi->on_link = on_link;
/*
* Compute hash index into which host will be added
*/
hash_index = ecm_db_host_generate_hash_index(address);
hi->hash_index = hash_index;
/*
* Host takes a ref to the node
*/
ecm_db_node_ref(ni);
hi->node = ni;
/*
* Add into the global list
*/
spin_lock_bh(&ecm_db_lock);
hi->flags |= ECM_DB_HOST_FLAGS_INSERTED;
hi->prev = NULL;
hi->next = ecm_db_hosts;
if (ecm_db_hosts) {
ecm_db_hosts->prev = hi;
}
ecm_db_hosts = hi;
/*
* Add host into the hash table
*/
hi->hash_next = ecm_db_host_table[hash_index];
if (ecm_db_host_table[hash_index]) {
ecm_db_host_table[hash_index]->hash_prev = hi;
}
ecm_db_host_table[hash_index] = hi;
ecm_db_host_table_lengths[hash_index]++;
DEBUG_ASSERT(ecm_db_host_table_lengths[hash_index] > 0, "%p: invalid table len %d\n", hi, ecm_db_host_table_lengths[hash_index]);
/*
* Set time of add
*/
hi->time_added = ecm_db_time;
/*
* Insert host into the node hosts list
*/
hi->host_prev = NULL;
hi->host_next = ni->hosts;
if (ni->hosts) {
ni->hosts->host_prev = hi;
}
ni->hosts = hi;
ni->host_count++;
spin_unlock_bh(&ecm_db_lock);
/*
* Throw add event to the listeners
*/
DEBUG_TRACE("%p: Throw host added event\n", hi);
li = ecm_db_listeners_get_and_ref_first();
while (li) {
struct ecm_db_listener_instance *lin;
if (li->host_added) {
li->host_added(li->arg, hi);
}
/*
* Get next listener
*/
lin = ecm_db_listener_get_and_ref_next(li);
ecm_db_listener_deref(li);
li = lin;
}
}
EXPORT_SYMBOL(ecm_db_host_add);
/*
* ecm_db_node_add()
* Add a node instance into the database
*/
void ecm_db_node_add(struct ecm_db_node_instance *ni, struct ecm_db_iface_instance *ii, uint8_t *address,
ecm_db_node_final_callback_t final, void *arg)
{
ecm_db_node_hash_t hash_index;
struct ecm_db_listener_instance *li;
spin_lock_bh(&ecm_db_lock);
DEBUG_CHECK_MAGIC(ni, ECM_DB_NODE_INSTANCE_MAGIC, "%p: magic failed\n", ni);
DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed\n", ii);
DEBUG_ASSERT(address, "%p: address null\n", ni);
DEBUG_ASSERT((ni->hosts == NULL) && (ni->host_count == 0), "%p: hosts not null\n", ni);
DEBUG_ASSERT((ni->iface == NULL), "%p: iface not null\n", ni);
DEBUG_ASSERT(!(ni->flags & ECM_DB_NODE_FLAGS_INSERTED), "%p: inserted\n", ni);
spin_unlock_bh(&ecm_db_lock);
memcpy(ni->address, address, ETH_ALEN);
ni->arg = arg;
ni->final = final;
/*
* Compute hash chain for insertion
*/
hash_index = ecm_db_node_generate_hash_index(address);
ni->hash_index = hash_index;
/*
* Node takes a ref to the iface
*/
ecm_db_iface_ref(ii);
ni->iface = ii;
/*
* Add into the global list
*/
spin_lock_bh(&ecm_db_lock);
ni->flags |= ECM_DB_NODE_FLAGS_INSERTED;
ni->prev = NULL;
ni->next = ecm_db_nodes;
if (ecm_db_nodes) {
ecm_db_nodes->prev = ni;
}
ecm_db_nodes = ni;
/*
* Insert into the hash chain
*/
ni->hash_next = ecm_db_node_table[hash_index];
if (ecm_db_node_table[hash_index]) {
ecm_db_node_table[hash_index]->hash_prev = ni;
}
ecm_db_node_table[hash_index] = ni;
ecm_db_node_table_lengths[hash_index]++;
DEBUG_ASSERT(ecm_db_node_table_lengths[hash_index] > 0, "%p: invalid table len %d\n", ni, ecm_db_node_table_lengths[hash_index]);
/*
* Set time of add
*/
ni->time_added = ecm_db_time;
/*
* Insert node into the iface nodes list
*/
ni->node_prev = NULL;
ni->node_next = ii->nodes;
if (ii->nodes) {
ii->nodes->node_prev = ni;
}
ii->nodes = ni;
ii->node_count++;
spin_unlock_bh(&ecm_db_lock);
/*
* Throw add event to the listeners
*/
DEBUG_TRACE("%p: Throw node added event\n", ni);
li = ecm_db_listeners_get_and_ref_first();
while (li) {
struct ecm_db_listener_instance *lin;
if (li->node_added) {
li->node_added(li->arg, ni);
}
/*
* Get next listener
*/
lin = ecm_db_listener_get_and_ref_next(li);
ecm_db_listener_deref(li);
li = lin;
}
}
EXPORT_SYMBOL(ecm_db_node_add);
/*
* ecm_db_iface_xml_state_get_open()
* Get the start of XML state for an interface object
*/
static int ecm_db_iface_xml_state_get_open(struct ecm_db_iface_instance *ii, char *buf, int buf_sz)
{
int count;
int node_count;
uint32_t time_added;
uint64_t from_data_total;
uint64_t to_data_total;
uint64_t from_packet_total;
uint64_t to_packet_total;
uint64_t from_data_total_dropped;
uint64_t to_data_total_dropped;
uint64_t from_packet_total_dropped;
uint64_t to_packet_total_dropped;
int32_t interface_identifier;
int32_t nss_interface_identifier;
char name[IFNAMSIZ];
int32_t mtu;
ecm_db_iface_type_t type;
DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed\n", ii);
DEBUG_TRACE("%p: Open iface msg\n", ii);
/*
* Create a small xml stats block, like:
* <iface blah="" ... >
* Extract general information from the iface for inclusion into the message
*/
node_count = ecm_db_iface_node_count_get(ii);
time_added = ii->time_added;
ecm_db_iface_data_stats_get(ii, &from_data_total, &to_data_total,
&from_packet_total, &to_packet_total,
&from_data_total_dropped, &to_data_total_dropped,
&from_packet_total_dropped, &to_packet_total_dropped);
type = ii->type;
spin_lock_bh(&ecm_db_lock);
strcpy(name, ii->name);
mtu = ii->mtu;
interface_identifier = ii->interface_identifier;
nss_interface_identifier = ii->nss_interface_identifier;
spin_unlock_bh(&ecm_db_lock);
/*
* Prep the message
*/
count = snprintf(buf, buf_sz,
"<iface type=\"%d\" name=\"%s\" nodes=\"%d\" time_added=\"%u\""
" mtu=\"%d\" interface_identifier=\"%d\" nss_interface_identifier=\"%d\""
" from_data_total=\"%llu\" to_data_total=\"%llu\" from_packet_total=\"%llu\" to_packet_total=\"%llu\""
" from_data_total_dropped=\"%llu\" to_data_total_dropped=\"%llu\" from_packet_total_dropped=\"%llu\" to_packet_total_dropped=\"%llu\">\n",
type,
name,
node_count,
time_added,
mtu,
interface_identifier,
nss_interface_identifier,
from_data_total,
to_data_total,
from_packet_total,
to_packet_total,
from_data_total_dropped,
to_data_total_dropped,
from_packet_total_dropped,
to_packet_total_dropped);
if ((count <= 0) || (count >= buf_sz)) {
return -1;
}
return count;
}
/*
* ecm_db_iface_xml_state_get_close()
* Get the end of XML state for an interface object
*/
static int ecm_db_iface_xml_state_get_close(struct ecm_db_iface_instance *ii, char *buf, int buf_sz)
{
int count;
DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed\n", ii);
DEBUG_TRACE("%p: Close iface msg\n", ii);
/*
* Create a small xml stats block, like:
* </iface>
*/
/*
* Prep the message
*/
count = snprintf(buf, buf_sz, "</iface>\n");
if ((count <= 0) || (count >= buf_sz)) {
return -1;
}
return count;
}
/*
* ecm_db_iface_ethernet_xml_state_get()
* Return interface type specific state
*/
static int ecm_db_iface_ethernet_xml_state_get(struct ecm_db_iface_instance *ii, char *buf, int buf_sz)
{
int count;
int total;
uint8_t address[ETH_ALEN];
DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed\n", ii);
spin_lock_bh(&ecm_db_lock);
memcpy(address, ii->type_info.ethernet.address, ETH_ALEN);
spin_unlock_bh(&ecm_db_lock);
/*
* Write out opening element
*/
total = 0;
count = ecm_db_iface_xml_state_get_open(ii, buf + total, buf_sz - total);
if ((count <= 0) || (count >= (buf_sz - total))) {
return -1;
}
total += count;
/*
* Write out type specific data
*/
count = snprintf(buf + total, buf_sz - total, "<ethernet address=\"%pM\"/>\n", address);
if ((count <= 0) || (count >= (buf_sz - total))) {
return -1;
}
total += count;
/*
* Write out closing element
*/
count = ecm_db_iface_xml_state_get_close(ii, buf + total, buf_sz - total);
if ((count <= 0) || (count >= (buf_sz - total))) {
return -1;
}
total += count;
return total;
}
/*
* ecm_db_iface_add_ethernet()
* Add a iface instance into the database
*/
void ecm_db_iface_add_ethernet(struct ecm_db_iface_instance *ii, uint8_t *address, char *name, int32_t mtu,
int32_t interface_identifier, int32_t nss_interface_identifier,
ecm_db_iface_final_callback_t final, void *arg)
{
ecm_db_iface_hash_t hash_index;
struct ecm_db_listener_instance *li;
struct ecm_db_interface_info_ethernet *type_info;
spin_lock_bh(&ecm_db_lock);
DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed\n", ii);
DEBUG_ASSERT(address, "%p: address null\n", ii);
DEBUG_ASSERT((ii->nodes == NULL) && (ii->node_count == 0), "%p: nodes not null\n", ii);
DEBUG_ASSERT(!(ii->flags & ECM_DB_IFACE_FLAGS_INSERTED), "%p: inserted\n", ii);
DEBUG_ASSERT(name, "%p: no name given\n", ii);
spin_unlock_bh(&ecm_db_lock);
/*
* Record general info
*/
ii->type = ECM_DB_IFACE_TYPE_ETHERNET;
ii->xml_state_get = ecm_db_iface_ethernet_xml_state_get;
ii->arg = arg;
ii->final = final;
strcpy(ii->name, name);
ii->mtu = mtu;
ii->interface_identifier = interface_identifier;
ii->nss_interface_identifier = nss_interface_identifier;
/*
* Type specific info
*/
type_info = &ii->type_info.ethernet;
memcpy(type_info->address, address, ETH_ALEN);
/*
* Compute hash chain for insertion
*/
hash_index = ecm_db_iface_generate_hash_index_ethernet(address);
ii->hash_index = hash_index;
/*
* Add into the global list
*/
spin_lock_bh(&ecm_db_lock);
ii->flags |= ECM_DB_IFACE_FLAGS_INSERTED;
ii->prev = NULL;
ii->next = ecm_db_interfaces;
if (ecm_db_interfaces) {
ecm_db_interfaces->prev = ii;
}
ecm_db_interfaces = ii;
/*
* Insert into chain
*/
ii->hash_next = ecm_db_iface_table[hash_index];
if (ecm_db_iface_table[hash_index]) {
ecm_db_iface_table[hash_index]->hash_prev = ii;
}
ecm_db_iface_table[hash_index] = ii;
ecm_db_iface_table_lengths[hash_index]++;
DEBUG_ASSERT(ecm_db_iface_table_lengths[hash_index] > 0, "%p: invalid table len %d\n", ii, ecm_db_iface_table_lengths[hash_index]);
DEBUG_INFO("%p: interface inserted at hash index %u, hash prev is %p, type: %d\n", ii, ii->hash_index, ii->hash_prev, ii->type);
/*
* Set time of addition
*/
ii->time_added = ecm_db_time;
spin_unlock_bh(&ecm_db_lock);
/*
* Throw add event to the listeners
*/
DEBUG_TRACE("%p: Throw iface added event\n", ii);
li = ecm_db_listeners_get_and_ref_first();
while (li) {
struct ecm_db_listener_instance *lin;
if (li->iface_added) {
li->iface_added(li->arg, ii);
}
/*
* Get next listener
*/
lin = ecm_db_listener_get_and_ref_next(li);
ecm_db_listener_deref(li);
li = lin;
}
}
EXPORT_SYMBOL(ecm_db_iface_add_ethernet);
/*
* ecm_db_iface_lag_xml_state_get()
* Return interface type specific state
*/
static int ecm_db_iface_lag_xml_state_get(struct ecm_db_iface_instance *ii, char *buf, int buf_sz)
{
int count;
int total;
uint8_t address[ETH_ALEN];
DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed\n", ii);
spin_lock_bh(&ecm_db_lock);
memcpy(address, ii->type_info.lag.address, ETH_ALEN);
spin_unlock_bh(&ecm_db_lock);
/*
* Write out opening element
*/
total = 0;
count = ecm_db_iface_xml_state_get_open(ii, buf + total, buf_sz - total);
if ((count <= 0) || (count >= (buf_sz - total))) {
return -1;
}
total += count;
/*
* Write out type specific data
*/
count = snprintf(buf + total, buf_sz - total, "<lag address=\"%pM\"/>\n", address);
if ((count <= 0) || (count >= (buf_sz - total))) {
return -1;
}
total += count;
/*
* Write out closing element
*/
count = ecm_db_iface_xml_state_get_close(ii, buf + total, buf_sz - total);
if ((count <= 0) || (count == (buf_sz - total))) {
return -1;
}
total += count;
return total;
}
/*
* ecm_db_iface_add_lag()
* Add a iface instance into the database
*/
void ecm_db_iface_add_lag(struct ecm_db_iface_instance *ii, uint8_t *address, char *name, int32_t mtu,
int32_t interface_identifier, int32_t nss_interface_identifier,
ecm_db_iface_final_callback_t final, void *arg)
{
ecm_db_iface_hash_t hash_index;
struct ecm_db_listener_instance *li;
struct ecm_db_interface_info_lag *type_info;
spin_lock_bh(&ecm_db_lock);
DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed\n", ii);
DEBUG_ASSERT(address, "%p: address null\n", ii);
DEBUG_ASSERT((ii->nodes == NULL) && (ii->node_count == 0), "%p: nodes not null\n", ii);
DEBUG_ASSERT(!(ii->flags & ECM_DB_IFACE_FLAGS_INSERTED), "%p: inserted\n", ii);
DEBUG_ASSERT(name, "%p: no name given\n", ii);
spin_unlock_bh(&ecm_db_lock);
/*
* Record general info
*/
ii->type = ECM_DB_IFACE_TYPE_LAG;
ii->xml_state_get = ecm_db_iface_lag_xml_state_get;
ii->arg = arg;
ii->final = final;
strcpy(ii->name, name);
ii->mtu = mtu;
ii->interface_identifier = interface_identifier;
ii->nss_interface_identifier = nss_interface_identifier;
/*
* Type specific info
*/
type_info = &ii->type_info.lag;
memcpy(type_info->address, address, ETH_ALEN);
/*
* Compute hash chain for insertion
*/
hash_index = ecm_db_iface_generate_hash_index_ethernet(address);
ii->hash_index = hash_index;
/*
* Add into the global list
*/
spin_lock_bh(&ecm_db_lock);
ii->flags |= ECM_DB_IFACE_FLAGS_INSERTED;
ii->prev = NULL;
ii->next = ecm_db_interfaces;
if (ecm_db_interfaces) {
ecm_db_interfaces->prev = ii;
}
ecm_db_interfaces = ii;
/*
* Insert into chain
*/
ii->hash_next = ecm_db_iface_table[hash_index];
if (ecm_db_iface_table[hash_index]) {
ecm_db_iface_table[hash_index]->hash_prev = ii;
}
ecm_db_iface_table[hash_index] = ii;
ecm_db_iface_table_lengths[hash_index]++;
DEBUG_ASSERT(ecm_db_iface_table_lengths[hash_index] > 0, "%p: invalid table len %d\n", ii, ecm_db_iface_table_lengths[hash_index]);
DEBUG_INFO("%p: interface inserted at hash index %u, hash prev is %p, type: %d\n", ii, ii->hash_index, ii->hash_prev, ii->type);
/*
* Set time of addition
*/
ii->time_added = ecm_db_time;
spin_unlock_bh(&ecm_db_lock);
/*
* Throw add event to the listeners
*/
DEBUG_TRACE("%p: Throw iface added event\n", ii);
li = ecm_db_listeners_get_and_ref_first();
while (li) {
struct ecm_db_listener_instance *lin;
if (li->iface_added) {
li->iface_added(li->arg, ii);
}
/*
* Get next listener
*/
lin = ecm_db_listener_get_and_ref_next(li);
ecm_db_listener_deref(li);
li = lin;
}
}
EXPORT_SYMBOL(ecm_db_iface_add_lag);
/*
* ecm_db_iface_bridge_xml_state_get()
* Return interface type specific state
*/
static int ecm_db_iface_bridge_xml_state_get(struct ecm_db_iface_instance *ii, char *buf, int buf_sz)
{
int count;
int total;
uint8_t address[ETH_ALEN];
DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed\n", ii);
spin_lock_bh(&ecm_db_lock);
memcpy(address, ii->type_info.bridge.address, ETH_ALEN);
spin_unlock_bh(&ecm_db_lock);
/*
* Write out opening element
*/
total = 0;
count = ecm_db_iface_xml_state_get_open(ii, buf + total, buf_sz - total);
if ((count <= 0) || (count >= (buf_sz - total))) {
return -1;
}
total += count;
/*
* Write out type specific data
*/
count = snprintf(buf + total, buf_sz - total, "<bridge address=\"%pM\"/>\n", address);
if ((count <= 0) || (count >= (buf_sz - total))) {
return -1;
}
total += count;
/*
* Write out closing element
*/
count = ecm_db_iface_xml_state_get_close(ii, buf + total, buf_sz - total);
if ((count <= 0) || (count >= (buf_sz - total))) {
return -1;
}
total += count;
return total;
}
/*
* ecm_db_iface_add_bridge()
* Add a iface instance into the database
*/
void ecm_db_iface_add_bridge(struct ecm_db_iface_instance *ii, uint8_t *address, char *name, int32_t mtu,
int32_t interface_identifier, int32_t nss_interface_identifier,
ecm_db_iface_final_callback_t final, void *arg)
{
ecm_db_iface_hash_t hash_index;
struct ecm_db_listener_instance *li;
struct ecm_db_interface_info_bridge *type_info;
spin_lock_bh(&ecm_db_lock);
DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed\n", ii);
DEBUG_ASSERT(address, "%p: address null\n", ii);
DEBUG_ASSERT((ii->nodes == NULL) && (ii->node_count == 0), "%p: nodes not null\n", ii);
DEBUG_ASSERT(!(ii->flags & ECM_DB_IFACE_FLAGS_INSERTED), "%p: inserted\n", ii);
DEBUG_ASSERT(name, "%p: no name given\n", ii);
spin_unlock_bh(&ecm_db_lock);
/*
* Record general info
*/
ii->type = ECM_DB_IFACE_TYPE_BRIDGE;
ii->xml_state_get = ecm_db_iface_bridge_xml_state_get;
ii->arg = arg;
ii->final = final;
strcpy(ii->name, name);
ii->mtu = mtu;
ii->interface_identifier = interface_identifier;
ii->nss_interface_identifier = nss_interface_identifier;
/*
* Type specific info
*/
type_info = &ii->type_info.bridge;
memcpy(type_info->address, address, ETH_ALEN);
/*
* Compute hash chain for insertion
*/
hash_index = ecm_db_iface_generate_hash_index_ethernet(address);
ii->hash_index = hash_index;
/*
* Add into the global list
*/
spin_lock_bh(&ecm_db_lock);
ii->flags |= ECM_DB_IFACE_FLAGS_INSERTED;
ii->prev = NULL;
ii->next = ecm_db_interfaces;
if (ecm_db_interfaces) {
ecm_db_interfaces->prev = ii;
}
ecm_db_interfaces = ii;
/*
* Insert into chain
*/
ii->hash_next = ecm_db_iface_table[hash_index];
if (ecm_db_iface_table[hash_index]) {
ecm_db_iface_table[hash_index]->hash_prev = ii;
}
ecm_db_iface_table[hash_index] = ii;
ecm_db_iface_table_lengths[hash_index]++;
DEBUG_ASSERT(ecm_db_iface_table_lengths[hash_index] > 0, "%p: invalid table len %d\n", ii, ecm_db_iface_table_lengths[hash_index]);
DEBUG_INFO("%p: interface inserted at hash index %u, hash prev is %p, type: %d\n", ii, ii->hash_index, ii->hash_prev, ii->type);
/*
* Set time of addition
*/
ii->time_added = ecm_db_time;
spin_unlock_bh(&ecm_db_lock);
/*
* Throw add event to the listeners
*/
DEBUG_TRACE("%p: Throw iface added event\n", ii);
li = ecm_db_listeners_get_and_ref_first();
while (li) {
struct ecm_db_listener_instance *lin;
if (li->iface_added) {
li->iface_added(li->arg, ii);
}
/*
* Get next listener
*/
lin = ecm_db_listener_get_and_ref_next(li);
ecm_db_listener_deref(li);
li = lin;
}
}
EXPORT_SYMBOL(ecm_db_iface_add_bridge);
/*
* ecm_db_iface_vlan_xml_state_get()
* Return interface type specific state
*/
static int ecm_db_iface_vlan_xml_state_get(struct ecm_db_iface_instance *ii, char *buf, int buf_sz)
{
int count;
int total;
uint8_t address[ETH_ALEN];
uint16_t vlan_tag;
uint16_t vlan_tpid;
DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed\n", ii);
spin_lock_bh(&ecm_db_lock);
memcpy(address, ii->type_info.vlan.address, ETH_ALEN);
vlan_tag = ii->type_info.vlan.vlan_tag;
vlan_tpid = ii->type_info.vlan.vlan_tpid;
spin_unlock_bh(&ecm_db_lock);
/*
* Write out opening element
*/
total = 0;
count = ecm_db_iface_xml_state_get_open(ii, buf + total, buf_sz - total);
if ((count <= 0) || (count >= (buf_sz - total))) {
return -1;
}
total += count;
/*
* Write out type specific data
*/
count = snprintf(buf + total, buf_sz - total, "<vlan address=\"%pM\" vlan_tag=\"%x\" vlan_tpid=\"%x\"/>\n", address, vlan_tag, vlan_tpid);
if ((count <= 0) || (count >= (buf_sz - total))) {
return -1;
}
total += count;
/*
* Write out closing element
*/
count = ecm_db_iface_xml_state_get_close(ii, buf + total, buf_sz - total);
if ((count <= 0) || (count >= (buf_sz - total))) {
return -1;
}
total += count;
return total;
}
/*
* ecm_db_iface_add_vlan()
* Add a iface instance into the database
*/
void ecm_db_iface_add_vlan(struct ecm_db_iface_instance *ii, uint8_t *address, uint16_t vlan_tag, uint16_t vlan_tpid, char *name, int32_t mtu,
int32_t interface_identifier, int32_t nss_interface_identifier,
ecm_db_iface_final_callback_t final, void *arg)
{
ecm_db_iface_hash_t hash_index;
struct ecm_db_listener_instance *li;
struct ecm_db_interface_info_vlan *type_info;
spin_lock_bh(&ecm_db_lock);
DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed\n", ii);
DEBUG_ASSERT(address, "%p: address null\n", ii);
DEBUG_ASSERT((ii->nodes == NULL) && (ii->node_count == 0), "%p: nodes not null\n", ii);
DEBUG_ASSERT(!(ii->flags & ECM_DB_IFACE_FLAGS_INSERTED), "%p: inserted\n", ii);
DEBUG_ASSERT(name, "%p: no name given\n", ii);
spin_unlock_bh(&ecm_db_lock);
/*
* Record general info
*/
ii->type = ECM_DB_IFACE_TYPE_VLAN;
ii->xml_state_get = ecm_db_iface_vlan_xml_state_get;
ii->arg = arg;
ii->final = final;
strcpy(ii->name, name);
ii->mtu = mtu;
ii->interface_identifier = interface_identifier;
ii->nss_interface_identifier = nss_interface_identifier;
/*
* Type specific info
*/
type_info = &ii->type_info.vlan;
type_info->vlan_tag = vlan_tag;
type_info->vlan_tpid = vlan_tpid;
memcpy(type_info->address, address, ETH_ALEN);
/*
* Compute hash chain for insertion
*/
hash_index = ecm_db_iface_generate_hash_index_ethernet(address);
ii->hash_index = hash_index;
/*
* Add into the global list
*/
spin_lock_bh(&ecm_db_lock);
ii->flags |= ECM_DB_IFACE_FLAGS_INSERTED;
ii->prev = NULL;
ii->next = ecm_db_interfaces;
if (ecm_db_interfaces) {
ecm_db_interfaces->prev = ii;
}
ecm_db_interfaces = ii;
/*
* Insert into chain
*/
ii->hash_next = ecm_db_iface_table[hash_index];
if (ecm_db_iface_table[hash_index]) {
ecm_db_iface_table[hash_index]->hash_prev = ii;
}
ecm_db_iface_table[hash_index] = ii;
ecm_db_iface_table_lengths[hash_index]++;
DEBUG_ASSERT(ecm_db_iface_table_lengths[hash_index] > 0, "%p: invalid table len %d\n", ii, ecm_db_iface_table_lengths[hash_index]);
DEBUG_INFO("%p: interface inserted at hash index %u, hash prev is %p, type: %d\n", ii, ii->hash_index, ii->hash_prev, ii->type);
/*
* Set time of addition
*/
ii->time_added = ecm_db_time;
spin_unlock_bh(&ecm_db_lock);
/*
* Throw add event to the listeners
*/
DEBUG_TRACE("%p: Throw iface added event\n", ii);
li = ecm_db_listeners_get_and_ref_first();
while (li) {
struct ecm_db_listener_instance *lin;
if (li->iface_added) {
li->iface_added(li->arg, ii);
}
/*
* Get next listener
*/
lin = ecm_db_listener_get_and_ref_next(li);
ecm_db_listener_deref(li);
li = lin;
}
}
EXPORT_SYMBOL(ecm_db_iface_add_vlan);
/*
* ecm_db_iface_pppoe_xml_state_get()
* Return interface type specific state
*/
static int ecm_db_iface_pppoe_xml_state_get(struct ecm_db_iface_instance *ii, char *buf, int buf_sz)
{
int count;
int total;
uint16_t pppoe_session_id;
uint8_t remote_mac[ETH_ALEN];
DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed\n", ii);
spin_lock_bh(&ecm_db_lock);
pppoe_session_id = ii->type_info.pppoe.pppoe_session_id;
memcpy(remote_mac, ii->type_info.pppoe.remote_mac, ETH_ALEN);
spin_unlock_bh(&ecm_db_lock);
/*
* Write out opening element
*/
total = 0;
count = ecm_db_iface_xml_state_get_open(ii, buf + total, buf_sz - total);
if ((count <= 0) || (count >= (buf_sz - total))) {
return -1;
}
total += count;
/*
* Write out type specific data
*/
count = snprintf(buf + total, buf_sz - total, "<pppoe pppoe_session_id=\"%u\" remote_mac=\"%pM\"/>\n",
pppoe_session_id, remote_mac);
if ((count <= 0) || (count >= (buf_sz - total))) {
return -1;
}
total += count;
/*
* Write out closing element
*/
count = ecm_db_iface_xml_state_get_close(ii, buf + total, buf_sz - total);
if ((count <= 0) || (count >= (buf_sz - total))) {
return -1;
}
total += count;
return total;
}
/*
* ecm_db_iface_add_pppoe()
* Add a iface instance into the database
*/
void ecm_db_iface_add_pppoe(struct ecm_db_iface_instance *ii, uint16_t pppoe_session_id, uint8_t *remote_mac,
char *name, int32_t mtu, int32_t interface_identifier,
int32_t nss_interface_identifier, ecm_db_iface_final_callback_t final,
void *arg)
{
ecm_db_iface_hash_t hash_index;
struct ecm_db_listener_instance *li;
struct ecm_db_interface_info_pppoe *type_info;
spin_lock_bh(&ecm_db_lock);
DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed\n", ii);
DEBUG_ASSERT((ii->nodes == NULL) && (ii->node_count == 0), "%p: nodes not null\n", ii);
DEBUG_ASSERT(!(ii->flags & ECM_DB_IFACE_FLAGS_INSERTED), "%p: inserted\n", ii);
DEBUG_ASSERT(name, "%p: no name given\n", ii);
spin_unlock_bh(&ecm_db_lock);
/*
* Record general info
*/
ii->type = ECM_DB_IFACE_TYPE_PPPOE;
ii->xml_state_get = ecm_db_iface_pppoe_xml_state_get;
ii->arg = arg;
ii->final = final;
strcpy(ii->name, name);
ii->mtu = mtu;
ii->interface_identifier = interface_identifier;
ii->nss_interface_identifier = nss_interface_identifier;
/*
* Type specific info
*/
type_info = &ii->type_info.pppoe;
type_info->pppoe_session_id = pppoe_session_id;
memcpy(type_info->remote_mac, remote_mac, ETH_ALEN);
/*
* Compute hash chain for insertion
*/
hash_index = ecm_db_iface_generate_hash_index_pppoe(pppoe_session_id);
ii->hash_index = hash_index;
/*
* Add into the global list
*/
spin_lock_bh(&ecm_db_lock);
ii->flags |= ECM_DB_IFACE_FLAGS_INSERTED;
ii->prev = NULL;
ii->next = ecm_db_interfaces;
if (ecm_db_interfaces) {
ecm_db_interfaces->prev = ii;
}
ecm_db_interfaces = ii;
/*
* Insert into chain
*/
ii->hash_next = ecm_db_iface_table[hash_index];
if (ecm_db_iface_table[hash_index]) {
ecm_db_iface_table[hash_index]->hash_prev = ii;
}
ecm_db_iface_table[hash_index] = ii;
ecm_db_iface_table_lengths[hash_index]++;
DEBUG_ASSERT(ecm_db_iface_table_lengths[hash_index] > 0, "%p: invalid table len %d\n", ii, ecm_db_iface_table_lengths[hash_index]);
DEBUG_INFO("%p: interface inserted at hash index %u, hash prev is %p, type: %d\n", ii, ii->hash_index, ii->hash_prev, ii->type);
/*
* Set time of addition
*/
ii->time_added = ecm_db_time;
spin_unlock_bh(&ecm_db_lock);
/*
* Throw add event to the listeners
*/
DEBUG_TRACE("%p: Throw iface added event\n", ii);
li = ecm_db_listeners_get_and_ref_first();
while (li) {
struct ecm_db_listener_instance *lin;
if (li->iface_added) {
li->iface_added(li->arg, ii);
}
/*
* Get next listener
*/
lin = ecm_db_listener_get_and_ref_next(li);
ecm_db_listener_deref(li);
li = lin;
}
}
EXPORT_SYMBOL(ecm_db_iface_add_pppoe);
/*
* ecm_db_iface_unknown_xml_state_get()
* Return interface type specific state
*/
static int ecm_db_iface_unknown_xml_state_get(struct ecm_db_iface_instance *ii, char *buf, int buf_sz)
{
int count;
int total;
uint32_t os_specific_ident;
DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed\n", ii);
spin_lock_bh(&ecm_db_lock);
os_specific_ident = ii->type_info.unknown.os_specific_ident;
spin_unlock_bh(&ecm_db_lock);
/*
* Write out opening element
*/
total = 0;
count = ecm_db_iface_xml_state_get_open(ii, buf + total, buf_sz - total);
if ((count <= 0) || (count >= (buf_sz - total))) {
return -1;
}
total += count;
/*
* Write out type specific data
*/
count = snprintf(buf + total, buf_sz - total, "<unknown os_specific_ident=\"%u\"/>\n", os_specific_ident);
if ((count <= 0) || (count >= (buf_sz - total))) {
return -1;
}
total += count;
/*
* Write out closing element
*/
count = ecm_db_iface_xml_state_get_close(ii, buf + total, buf_sz - total);
if ((count <= 0) || (count >= (buf_sz - total))) {
return -1;
}
total += count;
return total;
}
/*
* ecm_db_iface_loopback_xml_state_get()
* Return interface type specific state
*/
static int ecm_db_iface_loopback_xml_state_get(struct ecm_db_iface_instance *ii, char *buf, int buf_sz)
{
int count;
int total;
uint32_t os_specific_ident;
DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed\n", ii);
spin_lock_bh(&ecm_db_lock);
os_specific_ident = ii->type_info.loopback.os_specific_ident;
spin_unlock_bh(&ecm_db_lock);
/*
* Write out opening element
*/
total = 0;
count = ecm_db_iface_xml_state_get_open(ii, buf + total, buf_sz - total);
if ((count <= 0) || (count >= (buf_sz - total))) {
return -1;
}
total += count;
/*
* Write out type specific data
*/
count = snprintf(buf + total, buf_sz - total, "<loopback os_specific_ident=\"%u\"/>\n", os_specific_ident);
if ((count <= 0) || (count >= (buf_sz - total))) {
return -1;
}
total += count;
/*
* Write out closing element
*/
count = ecm_db_iface_xml_state_get_close(ii, buf + total, buf_sz - total);
if ((count <= 0) || (count >= (buf_sz - total))) {
return -1;
}
total += count;
return total;
}
/*
* ecm_db_iface_ipsec_tunnel_xml_state_get()
* Return interface type specific state
*
* GGG TODO Output state on ipsec tunnel specific data
*/
static int ecm_db_iface_ipsec_tunnel_xml_state_get(struct ecm_db_iface_instance *ii, char *buf, int buf_sz)
{
int count;
int total;
uint32_t os_specific_ident;
DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed\n", ii);
spin_lock_bh(&ecm_db_lock);
os_specific_ident = ii->type_info.ipsec_tunnel.os_specific_ident;
spin_unlock_bh(&ecm_db_lock);
/*
* Write out opening element
*/
total = 0;
count = ecm_db_iface_xml_state_get_open(ii, buf + total, buf_sz - total);
if ((count <= 0) || (count >= (buf_sz - total))) {
return -1;
}
total += count;
/*
* Write out type specific data
*/
count = snprintf(buf + total, buf_sz - total, "<ipsec_tunnel os_specific_ident=\"%u\"/>\n", os_specific_ident);
if ((count <= 0) || (count >= (buf_sz - total))) {
return -1;
}
total += count;
/*
* Write out closing element
*/
count = ecm_db_iface_xml_state_get_close(ii, buf + total, buf_sz - total);
if ((count <= 0) || (count >= (buf_sz - total))) {
return -1;
}
total += count;
return total;
}
/*
* ecm_db_iface_add_unknown()
* Add a iface instance into the database
*/
void ecm_db_iface_add_unknown(struct ecm_db_iface_instance *ii, uint32_t os_specific_ident, char *name, int32_t mtu,
int32_t interface_identifier, int32_t nss_interface_identifier,
ecm_db_iface_final_callback_t final, void *arg)
{
ecm_db_iface_hash_t hash_index;
struct ecm_db_listener_instance *li;
struct ecm_db_interface_info_unknown *type_info;
spin_lock_bh(&ecm_db_lock);
DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed\n", ii);
DEBUG_ASSERT((ii->nodes == NULL) && (ii->node_count == 0), "%p: nodes not null\n", ii);
DEBUG_ASSERT(!(ii->flags & ECM_DB_IFACE_FLAGS_INSERTED), "%p: inserted\n", ii);
DEBUG_ASSERT(name, "%p: no name given\n", ii);
spin_unlock_bh(&ecm_db_lock);
/*
* Record general info
*/
ii->type = ECM_DB_IFACE_TYPE_UNKNOWN;
ii->xml_state_get = ecm_db_iface_unknown_xml_state_get;
ii->arg = arg;
ii->final = final;
strcpy(ii->name, name);
ii->mtu = mtu;
ii->interface_identifier = interface_identifier;
ii->nss_interface_identifier = nss_interface_identifier;
/*
* Type specific info
*/
type_info = &ii->type_info.unknown;
type_info->os_specific_ident = os_specific_ident;
/*
* Compute hash chain for insertion
*/
hash_index = ecm_db_iface_generate_hash_index_unknown(os_specific_ident);
ii->hash_index = hash_index;
/*
* Add into the global list
*/
spin_lock_bh(&ecm_db_lock);
ii->flags |= ECM_DB_IFACE_FLAGS_INSERTED;
ii->prev = NULL;
ii->next = ecm_db_interfaces;
if (ecm_db_interfaces) {
ecm_db_interfaces->prev = ii;
}
ecm_db_interfaces = ii;
/*
* Insert into chain
*/
ii->hash_next = ecm_db_iface_table[hash_index];
if (ecm_db_iface_table[hash_index]) {
ecm_db_iface_table[hash_index]->hash_prev = ii;
}
ecm_db_iface_table[hash_index] = ii;
ecm_db_iface_table_lengths[hash_index]++;
DEBUG_ASSERT(ecm_db_iface_table_lengths[hash_index] > 0, "%p: invalid table len %d\n", ii, ecm_db_iface_table_lengths[hash_index]);
DEBUG_INFO("%p: interface inserted at hash index %u, hash prev is %p, type: %d\n", ii, ii->hash_index, ii->hash_prev, ii->type);
/*
* Set time of addition
*/
ii->time_added = ecm_db_time;
spin_unlock_bh(&ecm_db_lock);
/*
* Throw add event to the listeners
*/
DEBUG_TRACE("%p: Throw iface added event\n", ii);
li = ecm_db_listeners_get_and_ref_first();
while (li) {
struct ecm_db_listener_instance *lin;
if (li->iface_added) {
li->iface_added(li->arg, ii);
}
/*
* Get next listener
*/
lin = ecm_db_listener_get_and_ref_next(li);
ecm_db_listener_deref(li);
li = lin;
}
}
EXPORT_SYMBOL(ecm_db_iface_add_unknown);
/*
* ecm_db_iface_add_loopback()
* Add a iface instance into the database
*/
void ecm_db_iface_add_loopback(struct ecm_db_iface_instance *ii, uint32_t os_specific_ident, char *name, int32_t mtu,
int32_t interface_identifier, int32_t nss_interface_identifier,
ecm_db_iface_final_callback_t final, void *arg)
{
ecm_db_iface_hash_t hash_index;
struct ecm_db_listener_instance *li;
struct ecm_db_interface_info_loopback *type_info;
spin_lock_bh(&ecm_db_lock);
DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed\n", ii);
DEBUG_ASSERT((ii->nodes == NULL) && (ii->node_count == 0), "%p: nodes not null\n", ii);
DEBUG_ASSERT(!(ii->flags & ECM_DB_IFACE_FLAGS_INSERTED), "%p: inserted\n", ii);
DEBUG_ASSERT(name, "%p: no name given\n", ii);
spin_unlock_bh(&ecm_db_lock);
/*
* Record general info
*/
ii->type = ECM_DB_IFACE_TYPE_LOOPBACK;
ii->xml_state_get = ecm_db_iface_loopback_xml_state_get;
ii->arg = arg;
ii->final = final;
strcpy(ii->name, name);
ii->mtu = mtu;
ii->interface_identifier = interface_identifier;
ii->nss_interface_identifier = nss_interface_identifier;
/*
* Type specific info
*/
type_info = &ii->type_info.loopback;
type_info->os_specific_ident = os_specific_ident;
/*
* Compute hash chain for insertion
*/
hash_index = ecm_db_iface_generate_hash_index_loopback(os_specific_ident);
ii->hash_index = hash_index;
/*
* Add into the global list
*/
spin_lock_bh(&ecm_db_lock);
ii->flags |= ECM_DB_IFACE_FLAGS_INSERTED;
ii->prev = NULL;
ii->next = ecm_db_interfaces;
if (ecm_db_interfaces) {
ecm_db_interfaces->prev = ii;
}
ecm_db_interfaces = ii;
/*
* Insert into chain
*/
ii->hash_next = ecm_db_iface_table[hash_index];
if (ecm_db_iface_table[hash_index]) {
ecm_db_iface_table[hash_index]->hash_prev = ii;
}
ecm_db_iface_table[hash_index] = ii;
ecm_db_iface_table_lengths[hash_index]++;
DEBUG_ASSERT(ecm_db_iface_table_lengths[hash_index] > 0, "%p: invalid table len %d\n", ii, ecm_db_iface_table_lengths[hash_index]);
DEBUG_INFO("%p: interface inserted at hash index %u, hash prev is %p, type: %d\n", ii, ii->hash_index, ii->hash_prev, ii->type);
/*
* Set time of addition
*/
ii->time_added = ecm_db_time;
spin_unlock_bh(&ecm_db_lock);
/*
* Throw add event to the listeners
*/
DEBUG_TRACE("%p: Throw iface added event\n", ii);
li = ecm_db_listeners_get_and_ref_first();
while (li) {
struct ecm_db_listener_instance *lin;
if (li->iface_added) {
li->iface_added(li->arg, ii);
}
/*
* Get next listener
*/
lin = ecm_db_listener_get_and_ref_next(li);
ecm_db_listener_deref(li);
li = lin;
}
}
EXPORT_SYMBOL(ecm_db_iface_add_loopback);
/*
* ecm_db_iface_add_sit()
* Add a iface instance into the database
*/
void ecm_db_iface_add_sit(struct ecm_db_iface_instance *ii, struct ecm_db_interface_info_sit *type_info, char *name, int32_t mtu,
int32_t interface_identifier, int32_t nss_interface_identifier,
ecm_db_iface_final_callback_t final, void *arg)
{
ecm_db_iface_hash_t hash_index;
struct ecm_db_listener_instance *li;
spin_lock_bh(&ecm_db_lock);
DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed\n", ii);
DEBUG_ASSERT((ii->nodes == NULL) && (ii->node_count == 0), "%p: nodes not null\n", ii);
DEBUG_ASSERT(!(ii->flags & ECM_DB_IFACE_FLAGS_INSERTED), "%p: inserted\n", ii);
DEBUG_ASSERT(name, "%p: no name given\n", ii);
spin_unlock_bh(&ecm_db_lock);
/*
* Record general info
*/
ii->type = ECM_DB_IFACE_TYPE_SIT;
ii->xml_state_get = ecm_db_iface_loopback_xml_state_get;
ii->arg = arg;
ii->final = final;
strcpy(ii->name, name);
ii->mtu = mtu;
ii->interface_identifier = interface_identifier;
ii->nss_interface_identifier = nss_interface_identifier;
/*
* Type specific info to be copied
*/
ii->type_info.sit = *type_info;
/*
* Compute hash chain for insertion
*/
hash_index = ecm_db_iface_generate_hash_index_sit(type_info->saddr, type_info->daddr);
ii->hash_index = hash_index;
/*
* Add into the global list
*/
spin_lock_bh(&ecm_db_lock);
ii->flags |= ECM_DB_IFACE_FLAGS_INSERTED;
ii->prev = NULL;
ii->next = ecm_db_interfaces;
if (ecm_db_interfaces) {
ecm_db_interfaces->prev = ii;
}
ecm_db_interfaces = ii;
/*
* Insert into chain
*/
ii->hash_next = ecm_db_iface_table[hash_index];
if (ecm_db_iface_table[hash_index]) {
ecm_db_iface_table[hash_index]->hash_prev = ii;
}
ecm_db_iface_table[hash_index] = ii;
ecm_db_iface_table_lengths[hash_index]++;
DEBUG_ASSERT(ecm_db_iface_table_lengths[hash_index] > 0, "%p: invalid table len %d\n", ii, ecm_db_iface_table_lengths[hash_index]);
DEBUG_INFO("%p: interface inserted at hash index %u, hash prev is %p, type: %d\n", ii, ii->hash_index, ii->hash_prev, ii->type);
/*
* Set time of addition
*/
ii->time_added = ecm_db_time;
spin_unlock_bh(&ecm_db_lock);
/*
* Throw add event to the listeners
*/
DEBUG_TRACE("%p: Throw iface added event\n", ii);
li = ecm_db_listeners_get_and_ref_first();
while (li) {
struct ecm_db_listener_instance *lin;
if (li->iface_added) {
li->iface_added(li->arg, ii);
}
/*
* Get next listener
*/
lin = ecm_db_listener_get_and_ref_next(li);
ecm_db_listener_deref(li);
li = lin;
}
}
EXPORT_SYMBOL(ecm_db_iface_add_sit);
/*
* ecm_db_iface_add_tunipip6()
* Add a iface instance into the database
*/
void ecm_db_iface_add_tunipip6(struct ecm_db_iface_instance *ii, struct ecm_db_interface_info_tunipip6 *type_info, char *name, int32_t mtu,
int32_t interface_identifier, int32_t nss_interface_identifier,
ecm_db_iface_final_callback_t final, void *arg)
{
ecm_db_iface_hash_t hash_index;
struct ecm_db_listener_instance *li;
spin_lock_bh(&ecm_db_lock);
DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed\n", ii);
DEBUG_ASSERT((ii->nodes == NULL) && (ii->node_count == 0), "%p: nodes not null\n", ii);
DEBUG_ASSERT(!(ii->flags & ECM_DB_IFACE_FLAGS_INSERTED), "%p: inserted\n", ii);
DEBUG_ASSERT(name, "%p: no name given\n", ii);
spin_unlock_bh(&ecm_db_lock);
/*
* Record general info
*/
ii->type = ECM_DB_IFACE_TYPE_TUNIPIP6;
ii->xml_state_get = ecm_db_iface_loopback_xml_state_get;
ii->arg = arg;
ii->final = final;
strcpy(ii->name, name);
ii->mtu = mtu;
ii->interface_identifier = interface_identifier;
ii->nss_interface_identifier = nss_interface_identifier;
/*
* Type specific info to be copied
*/
ii->type_info.tunipip6 = *type_info;
/*
* Compute hash chain for insertion
*/
hash_index = ecm_db_iface_generate_hash_index_tunipip6(type_info->saddr, type_info->daddr);
ii->hash_index = hash_index;
/*
* Add into the global list
*/
spin_lock_bh(&ecm_db_lock);
ii->flags |= ECM_DB_IFACE_FLAGS_INSERTED;
ii->prev = NULL;
ii->next = ecm_db_interfaces;
if (ecm_db_interfaces) {
ecm_db_interfaces->prev = ii;
}
ecm_db_interfaces = ii;
/*
* Insert into chain
*/
ii->hash_next = ecm_db_iface_table[hash_index];
if (ecm_db_iface_table[hash_index]) {
ecm_db_iface_table[hash_index]->hash_prev = ii;
}
ecm_db_iface_table[hash_index] = ii;
ecm_db_iface_table_lengths[hash_index]++;
DEBUG_ASSERT(ecm_db_iface_table_lengths[hash_index] > 0, "%p: invalid table len %d\n", ii, ecm_db_iface_table_lengths[hash_index]);
DEBUG_INFO("%p: interface inserted at hash index %u, hash prev is %p, type: %d\n", ii, ii->hash_index, ii->hash_prev, ii->type);
/*
* Set time of addition
*/
ii->time_added = ecm_db_time;
spin_unlock_bh(&ecm_db_lock);
/*
* Throw add event to the listeners
*/
DEBUG_TRACE("%p: Throw iface added event\n", ii);
li = ecm_db_listeners_get_and_ref_first();
while (li) {
struct ecm_db_listener_instance *lin;
if (li->iface_added) {
li->iface_added(li->arg, ii);
}
/*
* Get next listener
*/
lin = ecm_db_listener_get_and_ref_next(li);
ecm_db_listener_deref(li);
li = lin;
}
}
EXPORT_SYMBOL(ecm_db_iface_add_tunipip6);
/*
* ecm_db_iface_add_ipsec_tunnel()
* Add a iface instance into the database
*
* GGG TODO This needs to take ipsec tunnel endpoint information etc. something very appropriate for ipsec tunnels, anyhow.
*/
void ecm_db_iface_add_ipsec_tunnel(struct ecm_db_iface_instance *ii, uint32_t os_specific_ident, char *name, int32_t mtu,
int32_t interface_identifier, int32_t nss_interface_identifier,
ecm_db_iface_final_callback_t final, void *arg)
{
ecm_db_iface_hash_t hash_index;
struct ecm_db_listener_instance *li;
struct ecm_db_interface_info_ipsec_tunnel *type_info;
spin_lock_bh(&ecm_db_lock);
DEBUG_CHECK_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC, "%p: magic failed\n", ii);
DEBUG_ASSERT((ii->nodes == NULL) && (ii->node_count == 0), "%p: nodes not null\n", ii);
DEBUG_ASSERT(!(ii->flags & ECM_DB_IFACE_FLAGS_INSERTED), "%p: inserted\n", ii);
DEBUG_ASSERT(name, "%p: no name given\n", ii);
spin_unlock_bh(&ecm_db_lock);
/*
* Record general info
*/
ii->type = ECM_DB_IFACE_TYPE_IPSEC_TUNNEL;
ii->xml_state_get = ecm_db_iface_ipsec_tunnel_xml_state_get;
ii->arg = arg;
ii->final = final;
strcpy(ii->name, name);
ii->mtu = mtu;
ii->interface_identifier = interface_identifier;
ii->nss_interface_identifier = nss_interface_identifier;
/*
* Type specific info
*/
type_info = &ii->type_info.ipsec_tunnel;
type_info->os_specific_ident = os_specific_ident;
/*
* Compute hash chain for insertion
*/
hash_index = ecm_db_iface_generate_hash_index_ipsec_tunnel(os_specific_ident);
ii->hash_index = hash_index;
/*
* Add into the global list
*/
spin_lock_bh(&ecm_db_lock);
ii->flags |= ECM_DB_IFACE_FLAGS_INSERTED;
ii->prev = NULL;
ii->next = ecm_db_interfaces;
if (ecm_db_interfaces) {
ecm_db_interfaces->prev = ii;
}
ecm_db_interfaces = ii;
/*
* Insert into chain
*/
ii->hash_next = ecm_db_iface_table[hash_index];
if (ecm_db_iface_table[hash_index]) {
ecm_db_iface_table[hash_index]->hash_prev = ii;
}
ecm_db_iface_table[hash_index] = ii;
ecm_db_iface_table_lengths[hash_index]++;
DEBUG_ASSERT(ecm_db_iface_table_lengths[hash_index] > 0, "%p: invalid table len %d\n", ii, ecm_db_iface_table_lengths[hash_index]);
DEBUG_INFO("%p: interface inserted at hash index %u, hash prev is %p, type: %d\n", ii, ii->hash_index, ii->hash_prev, ii->type);
/*
* Set time of addition
*/
ii->time_added = ecm_db_time;
spin_unlock_bh(&ecm_db_lock);
/*
* Throw add event to the listeners
*/
DEBUG_TRACE("%p: Throw iface added event\n", ii);
li = ecm_db_listeners_get_and_ref_first();
while (li) {
struct ecm_db_listener_instance *lin;
if (li->iface_added) {
li->iface_added(li->arg, ii);
}
/*
* Get next listener
*/
lin = ecm_db_listener_get_and_ref_next(li);
ecm_db_listener_deref(li);
li = lin;
}
}
EXPORT_SYMBOL(ecm_db_iface_add_ipsec_tunnel);
/*
* ecm_db_listener_add()
* Add a listener instance into the database.
*/
void ecm_db_listener_add(struct ecm_db_listener_instance *li,
ecm_db_iface_listener_added_callback_t iface_added,
ecm_db_iface_listener_removed_callback_t iface_removed,
ecm_db_node_listener_added_callback_t node_added,
ecm_db_node_listener_removed_callback_t node_removed,
ecm_db_host_listener_added_callback_t host_added,
ecm_db_host_listener_removed_callback_t host_removed,
ecm_db_mapping_listener_added_callback_t mapping_added,
ecm_db_mapping_listener_removed_callback_t mapping_removed,
ecm_db_connection_listener_added_callback_t connection_added,
ecm_db_connection_listener_removed_callback_t connection_removed,
ecm_db_listener_final_callback_t final,
void *arg)
{
spin_lock_bh(&ecm_db_lock);
DEBUG_CHECK_MAGIC(li, ECM_DB_LISTENER_INSTANCE_MAGIC, "%p: magic failed\n", li);
DEBUG_ASSERT(!(li->flags & ECM_DB_LISTENER_FLAGS_INSERTED), "%p: inserted\n", li);
spin_unlock_bh(&ecm_db_lock);
li->arg = arg;
li->final = final;
li->iface_added = iface_added;
li->iface_removed = iface_removed;
li->node_added = node_added;
li->node_removed = node_removed;
li->host_added = host_added;
li->host_removed = host_removed;
li->mapping_added = mapping_added;
li->mapping_removed = mapping_removed;
li->connection_added = connection_added;
li->connection_removed = connection_removed;
/*
* Add instance into listener list
*/
spin_lock_bh(&ecm_db_lock);
li->flags |= ECM_DB_LISTENER_FLAGS_INSERTED;
li->next = ecm_db_listeners;
ecm_db_listeners = li;
spin_unlock_bh(&ecm_db_lock);
}
EXPORT_SYMBOL(ecm_db_listener_add);
/*
* ecm_db_connection_alloc()
* Allocate a connection instance
*/
struct ecm_db_connection_instance *ecm_db_connection_alloc(void)
{
struct ecm_db_connection_instance *ci;
/*
* Allocate the connection
*/
ci = (struct ecm_db_connection_instance *)kzalloc(sizeof(struct ecm_db_connection_instance), GFP_ATOMIC | __GFP_NOWARN);
if (!ci) {
DEBUG_WARN("Connection alloc failed\n");
return NULL;
}
/*
* Initialise the defunct timer entry
*/
ecm_db_timer_group_entry_init(&ci->defunct_timer, ecm_db_connection_defunct_callback, ci);
/*
* Refs is 1 for the creator of the connection
*/
ci->refs = 1;
DEBUG_SET_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC);
/*
* Initialise the interfaces from/to lists.
* Interfaces are added from end of array.
*/
ci->from_interface_first = ECM_DB_IFACE_HEIRARCHY_MAX;
ci->to_interface_first = ECM_DB_IFACE_HEIRARCHY_MAX;
ci->from_nat_interface_first = ECM_DB_IFACE_HEIRARCHY_MAX;
ci->to_nat_interface_first = ECM_DB_IFACE_HEIRARCHY_MAX;
/*
* If the master thread is terminating then we cannot create new instances
*/
spin_lock_bh(&ecm_db_lock);
if (ecm_db_terminate_pending) {
spin_unlock_bh(&ecm_db_lock);
DEBUG_WARN("Thread terminating\n");
kfree(ci);
return NULL;
}
/*
* Assign runtime unique serial
*/
ci->serial = ecm_db_connection_serial++;
ecm_db_connection_count++;
DEBUG_ASSERT(ecm_db_connection_count > 0, "%p: connection count wrap\n", ci);
spin_unlock_bh(&ecm_db_lock);
DEBUG_TRACE("Connection created %p\n", ci);
return ci;
}
EXPORT_SYMBOL(ecm_db_connection_alloc);
/*
* ecm_db_mapping_alloc()
* Allocate a mapping instance
*/
struct ecm_db_mapping_instance *ecm_db_mapping_alloc(void)
{
struct ecm_db_mapping_instance *mi;
mi = (struct ecm_db_mapping_instance *)kzalloc(sizeof(struct ecm_db_mapping_instance), GFP_ATOMIC | __GFP_NOWARN);
if (!mi) {
DEBUG_WARN("Alloc failed\n");
return NULL;
}
mi->refs = 1;
DEBUG_SET_MAGIC(mi, ECM_DB_MAPPING_INSTANCE_MAGIC);
/*
* Alloc operation must be atomic to ensure thread and module can be held
*/
spin_lock_bh(&ecm_db_lock);
/*
* If the event processing thread is terminating then we cannot create new instances
*/
if (ecm_db_terminate_pending) {
spin_unlock_bh(&ecm_db_lock);
DEBUG_WARN("Thread terminating\n");
kfree(mi);
return NULL;
}
ecm_db_mapping_count++;
spin_unlock_bh(&ecm_db_lock);
DEBUG_TRACE("Mapping created %p\n", mi);
return mi;
}
EXPORT_SYMBOL(ecm_db_mapping_alloc);
/*
* ecm_db_host_alloc()
* Allocate a host instance
*/
struct ecm_db_host_instance *ecm_db_host_alloc(void)
{
struct ecm_db_host_instance *hi;
hi = (struct ecm_db_host_instance *)kzalloc(sizeof(struct ecm_db_host_instance), GFP_ATOMIC | __GFP_NOWARN);
if (!hi) {
DEBUG_WARN("Alloc failed\n");
return NULL;
}
hi->refs = 1;
DEBUG_SET_MAGIC(hi, ECM_DB_HOST_INSTANCE_MAGIC);
/*
* Alloc operation must be atomic to ensure thread and module can be held
*/
spin_lock_bh(&ecm_db_lock);
/*
* If the event processing thread is terminating then we cannot create new instances
*/
if (ecm_db_terminate_pending) {
spin_unlock_bh(&ecm_db_lock);
DEBUG_WARN("Thread terminating\n");
kfree(hi);
return NULL;
}
ecm_db_host_count++;
spin_unlock_bh(&ecm_db_lock);
DEBUG_TRACE("Host created %p\n", hi);
return hi;
}
EXPORT_SYMBOL(ecm_db_host_alloc);
/*
* ecm_db_node_alloc()
* Allocate a node instance
*/
struct ecm_db_node_instance *ecm_db_node_alloc(void)
{
struct ecm_db_node_instance *ni;
ni = (struct ecm_db_node_instance *)kzalloc(sizeof(struct ecm_db_node_instance), GFP_ATOMIC | __GFP_NOWARN);
if (!ni) {
DEBUG_WARN("Alloc failed\n");
return NULL;
}
ni->refs = 1;
DEBUG_SET_MAGIC(ni, ECM_DB_NODE_INSTANCE_MAGIC);
/*
* Alloc operation must be atomic to ensure thread and module can be held
*/
spin_lock_bh(&ecm_db_lock);
/*
* If the event processing thread is terminating then we cannot create new instances
*/
if (ecm_db_terminate_pending) {
spin_unlock_bh(&ecm_db_lock);
DEBUG_WARN("Thread terminating\n");
kfree(ni);
return NULL;
}
ecm_db_node_count++;
spin_unlock_bh(&ecm_db_lock);
DEBUG_TRACE("Node created %p\n", ni);
return ni;
}
EXPORT_SYMBOL(ecm_db_node_alloc);
/*
* ecm_db_iface_alloc()
* Allocate a iface instance
*/
struct ecm_db_iface_instance *ecm_db_iface_alloc(void)
{
struct ecm_db_iface_instance *ii;
ii = (struct ecm_db_iface_instance *)kzalloc(sizeof(struct ecm_db_iface_instance), GFP_ATOMIC | __GFP_NOWARN);
if (!ii) {
DEBUG_WARN("Alloc failed\n");
return NULL;
}
ii->refs = 1;
DEBUG_SET_MAGIC(ii, ECM_DB_IFACE_INSTANCE_MAGIC);
/*
* Alloc operation must be atomic to ensure thread and module can be held
*/
spin_lock_bh(&ecm_db_lock);
/*
* If the event processing thread is terminating then we cannot create new instances
*/
if (ecm_db_terminate_pending) {
spin_unlock_bh(&ecm_db_lock);
DEBUG_WARN("Thread terminating\n");
kfree(ii);
return NULL;
}
ecm_db_iface_count++;
spin_unlock_bh(&ecm_db_lock);
DEBUG_TRACE("iface created %p\n", ii);
return ii;
}
EXPORT_SYMBOL(ecm_db_iface_alloc);
/*
* ecm_db_listener_alloc()
* Allocate a listener instance
*/
struct ecm_db_listener_instance *ecm_db_listener_alloc(void)
{
struct ecm_db_listener_instance *li;
li = (struct ecm_db_listener_instance *)kzalloc(sizeof(struct ecm_db_listener_instance), GFP_ATOMIC | __GFP_NOWARN);
if (!li) {
DEBUG_WARN("Alloc failed\n");
return NULL;
}
li->refs = 1;
DEBUG_SET_MAGIC(li, ECM_DB_LISTENER_INSTANCE_MAGIC);
/*
* Alloc operation must be atomic to ensure thread and module can be held
*/
spin_lock_bh(&ecm_db_lock);
/*
* If the event processing thread is terminating then we cannot create new instances
*/
if (ecm_db_terminate_pending) {
spin_unlock_bh(&ecm_db_lock);
DEBUG_WARN("Thread terminating\n");
kfree(li);
return NULL;
}
ecm_db_listeners_count++;
DEBUG_ASSERT(ecm_db_listeners_count > 0, "%p: listener count wrap\n", li);
spin_unlock_bh(&ecm_db_lock);
DEBUG_TRACE("Listener created %p\n", li);
return li;
}
EXPORT_SYMBOL(ecm_db_listener_alloc);
/*
* ecm_db_time_get()
* Return database time, in seconds since the database started.
*/
uint32_t ecm_db_time_get(void)
{
uint32_t time_now;
spin_lock_bh(&ecm_db_lock);
time_now = ecm_db_time;
spin_unlock_bh(&ecm_db_lock);
return time_now;
}
EXPORT_SYMBOL(ecm_db_time_get);
/*
* ecm_db_get_state_dev_major()
*/
static ssize_t ecm_db_get_state_dev_major(struct sys_device *dev,
struct sysdev_attribute *attr,
char *buf)
{
ssize_t count;
int major;
spin_lock_bh(&ecm_db_lock);
major = ecm_db_dev_major_id;
spin_unlock_bh(&ecm_db_lock);
count = snprintf(buf, (ssize_t)PAGE_SIZE, "%d\n", major);
return count;
}
/*
* ecm_db_get_connection_count()
*/
static ssize_t ecm_db_get_connection_count(struct sys_device *dev,
struct sysdev_attribute *attr,
char *buf)
{
ssize_t count;
int num;
/*
* Operate under our locks
*/
spin_lock_bh(&ecm_db_lock);
num = ecm_db_connection_count;
spin_unlock_bh(&ecm_db_lock);
count = snprintf(buf, (ssize_t)PAGE_SIZE, "%d\n", num);
return count;
}
/*
* ecm_db_get_host_count()
*/
static ssize_t ecm_db_get_host_count(struct sys_device *dev,
struct sysdev_attribute *attr,
char *buf)
{
ssize_t count;
int num;
/*
* Operate under our locks
*/
spin_lock_bh(&ecm_db_lock);
num = ecm_db_host_count;
spin_unlock_bh(&ecm_db_lock);
count = snprintf(buf, (ssize_t)PAGE_SIZE, "%d\n", num);
return count;
}
/*
* ecm_db_get_mapping_count()
*/
static ssize_t ecm_db_get_mapping_count(struct sys_device *dev,
struct sysdev_attribute *attr,
char *buf)
{
ssize_t count;
int num;
/*
* Operate under our locks
*/
spin_lock_bh(&ecm_db_lock);
num = ecm_db_mapping_count;
spin_unlock_bh(&ecm_db_lock);
count = snprintf(buf, (ssize_t)PAGE_SIZE, "%d\n", num);
return count;
}
/*
* ecm_db_get_node_count()
*/
static ssize_t ecm_db_get_node_count(struct sys_device *dev,
struct sysdev_attribute *attr,
char *buf)
{
ssize_t count;
int num;
/*
* Operate under our locks
*/
spin_lock_bh(&ecm_db_lock);
num = ecm_db_node_count;
spin_unlock_bh(&ecm_db_lock);
count = snprintf(buf, (ssize_t)PAGE_SIZE, "%d\n", num);
return count;
}
/*
* ecm_db_get_iface_count()
*/
static ssize_t ecm_db_get_iface_count(struct sys_device *dev,
struct sysdev_attribute *attr,
char *buf)
{
ssize_t count;
int num;
/*
* Operate under our locks
*/
spin_lock_bh(&ecm_db_lock);
num = ecm_db_iface_count;
spin_unlock_bh(&ecm_db_lock);
count = snprintf(buf, (ssize_t)PAGE_SIZE, "%d\n", num);
return count;
}
/*
* ecm_db_get_defunct_all()
* Reading this file returns the accumulated total of all objects
*/
static ssize_t ecm_db_get_defunct_all(struct sys_device *dev,
struct sysdev_attribute *attr,
char *buf)
{
ssize_t count;
int num;
/*
* Operate under our locks
*/
spin_lock_bh(&ecm_db_lock);
num = ecm_db_connection_count + ecm_db_mapping_count + ecm_db_host_count
+ ecm_db_node_count + ecm_db_iface_count;
spin_unlock_bh(&ecm_db_lock);
count = snprintf(buf, (ssize_t)PAGE_SIZE, "%d\n", num);
return count;
}
/*
* ecm_db_set_defunct_all()
*/
static ssize_t ecm_db_set_defunct_all(struct sys_device *dev,
struct sysdev_attribute *attr,
const char *buf, size_t count)
{
ecm_db_connection_defunct_all();
return count;
}
/*
* ecm_db_get_connection_counts_simple()
* Return total of connections for each simple protocol (tcp, udp, other). Primarily for use by the luci-bwc service.
*/
static ssize_t ecm_db_get_connection_counts_simple(struct sys_device *dev,
struct sysdev_attribute *attr,
char *buf)
{
int tcp_count;
int udp_count;
int other_count;
int total_count;
ssize_t count;
/*
* Get snapshot of the protocol counts
*/
spin_lock_bh(&ecm_db_lock);
tcp_count = ecm_db_connection_count_by_protocol[IPPROTO_TCP];
udp_count = ecm_db_connection_count_by_protocol[IPPROTO_UDP];
total_count = ecm_db_connection_count;
other_count = total_count - (tcp_count + udp_count);
spin_unlock_bh(&ecm_db_lock);
count = snprintf(buf, (ssize_t)PAGE_SIZE, "tcp %d udp %d other %d total %d\n", tcp_count, udp_count, other_count, total_count);
return count;
}
/*
* ecm_db_get_state_file_output_mask()
*/
static ssize_t ecm_db_get_state_file_output_mask(struct sys_device *dev,
struct sysdev_attribute *attr,
char *buf)
{
ssize_t count;
int num;
/*
* Operate under our locks
*/
spin_lock_bh(&ecm_db_lock);
num = ecm_db_state_file_output_mask;
spin_unlock_bh(&ecm_db_lock);
count = snprintf(buf, (ssize_t)PAGE_SIZE, "%d\n", num);
return count;
}
/*
* ecm_db_set_state_file_output_mask()
*/
static ssize_t ecm_db_set_state_file_output_mask(struct sys_device *dev,
struct sysdev_attribute *attr,
const char *buf, size_t count)
{
char num_buf[12];
int num;
/*
* Get the number from buf into a properly z-termed number buffer
*/
if (count > 11) return 0;
memcpy(num_buf, buf, count);
num_buf[count] = '\0';
sscanf(num_buf, "%d", &num);
DEBUG_TRACE("ecm_db_state_file_output_mask = %x\n", num);
/*
* Operate under our locks
*/
spin_lock_bh(&ecm_db_lock);
ecm_db_state_file_output_mask = num;
spin_unlock_bh(&ecm_db_lock);
return count;
}
/*
* SysFS attributes for the default classifier itself.
*/
static SYSDEV_ATTR(state_dev_major, 0444, ecm_db_get_state_dev_major, NULL);
static SYSDEV_ATTR(connection_count, 0444, ecm_db_get_connection_count, NULL);
static SYSDEV_ATTR(host_count, 0444, ecm_db_get_host_count, NULL);
static SYSDEV_ATTR(mapping_count, 0444, ecm_db_get_mapping_count, NULL);
static SYSDEV_ATTR(node_count, 0444, ecm_db_get_node_count, NULL);
static SYSDEV_ATTR(iface_count, 0444, ecm_db_get_iface_count, NULL);
static SYSDEV_ATTR(defunct_all, 0644, ecm_db_get_defunct_all, ecm_db_set_defunct_all);
static SYSDEV_ATTR(connection_counts_simple, 0444, ecm_db_get_connection_counts_simple, NULL);
static SYSDEV_ATTR(state_file_output_mask, 0644, ecm_db_get_state_file_output_mask, ecm_db_set_state_file_output_mask);
/*
* SysFS class of the ubicom default classifier
* SysFS control points can be found at /sys/devices/system/ecm_db/ecm_dbX/
*/
static struct sysdev_class ecm_db_sysclass = {
.name = "ecm_db",
};
/*
* ecm_db_connection_heirarchy_xml_state_get()
* Output XML state for an interface heirarchy list.
*
* Return value is comptible with snprintf()
*/
static int ecm_db_connection_heirarchy_xml_state_get(char *element, struct ecm_db_iface_instance *interfaces[], int32_t first_interface,
char *buf, int buf_sz)
{
int count;
int total;
int i;
/*
* Output the opening element
*/
total = 0;
count = snprintf(buf + total,
buf_sz - total,
"<%s count=\"%d\">\n",
element,
ECM_DB_IFACE_HEIRARCHY_MAX - first_interface);
if ((count <= 0) || (count >= (buf_sz - total))) {
return -1;
}
total += count;
/*
* Iterate the interface heirarchy list and output the information
*/
for (i = first_interface; i < ECM_DB_IFACE_HEIRARCHY_MAX; ++i) {
struct ecm_db_iface_instance *ii = interfaces[i];
DEBUG_TRACE("Element: %s, Output interface @ %d: %p\n", element, i, ii);
count = ii->xml_state_get(ii, buf + total, buf_sz - total);
if ((count <= 0) || (count >= (buf_sz - total))) {
return -1;
}
total += count;
}
/*
* Output closing element
*/
count = snprintf(buf + total,
buf_sz - total,
"</%s>\n",
element);
if ((count <= 0) || (count >= (buf_sz - total))) {
return -1;
}
total += count;
return total;
}
/*
* ecm_db_char_dev_conn_msg_prep()
* Prepare a connection message
*/
static bool ecm_db_char_dev_conn_msg_prep(struct ecm_db_state_file_instance *sfi)
{
int msg_len;
int extra_msg_len;
long int expires_in;
int sport;
int sport_nat;
char snode_address[25];
char snode_address_nat[25];
char sip_address[50];
char sip_address_nat[50];
char dnode_address[25];
char dnode_address_nat[25];
int dport;
int dport_nat;
char dip_address[50];
char dip_address_nat[50];
ecm_db_direction_t direction;
int protocol;
bool is_routed;
uint32_t time_added;
uint32_t serial;
uint64_t from_data_total;
uint64_t to_data_total;
uint64_t from_packet_total;
uint64_t to_packet_total;
uint64_t from_data_total_dropped;
uint64_t to_data_total_dropped;
uint64_t from_packet_total_dropped;
uint64_t to_packet_total_dropped;
struct ecm_db_host_instance *hi;
int aci_index;
int aci_count;
struct ecm_front_end_connection_instance *feci;
struct ecm_classifier_instance *assignments[ECM_CLASSIFIER_TYPES];
int32_t first_interface;
struct ecm_db_iface_instance *interfaces[ECM_DB_IFACE_HEIRARCHY_MAX];
DEBUG_TRACE("%p: Prep conn msg for %p\n", sfi, sfi->ci);
/*
* Identify expiration
*/
spin_lock_bh(&ecm_db_lock);
if (sfi->ci->defunct_timer.group == ECM_DB_TIMER_GROUPS_MAX) {
expires_in = -1;
} else {
expires_in = (long int)(sfi->ci->defunct_timer.timeout - ecm_db_time);
if (expires_in <= 0) {
expires_in = 0;
}
}
spin_unlock_bh(&ecm_db_lock);
/*
* Extract information from the connection for inclusion into the message
*/
sport = sfi->ci->mapping_from->port;
sport_nat = sfi->ci->mapping_nat_from->port;
dport = sfi->ci->mapping_to->port;
dport_nat = sfi->ci->mapping_nat_to->port;
hi = sfi->ci->mapping_to->host;
ecm_ip_addr_to_string(dip_address, hi->address);
sprintf(dnode_address, "%pM", hi->node->address);
hi = sfi->ci->mapping_nat_to->host;
ecm_ip_addr_to_string(dip_address_nat, hi->address);
hi = sfi->ci->mapping_from->host;
ecm_ip_addr_to_string(sip_address, hi->address);
sprintf(snode_address, "%pM", hi->node->address);
hi = sfi->ci->mapping_nat_from->host;
ecm_ip_addr_to_string(sip_address_nat, hi->address);
hi = sfi->ci->mapping_nat_to->host;
sprintf(dnode_address_nat, "%pM", hi->node->address);
hi = sfi->ci->mapping_nat_from->host;
sprintf(snode_address_nat, "%pM", hi->node->address);
direction = sfi->ci->direction;
protocol = sfi->ci->protocol;
is_routed = sfi->ci->is_routed;
time_added = sfi->ci->time_added;
serial = sfi->ci->serial;
ecm_db_connection_data_stats_get(sfi->ci, &from_data_total, &to_data_total,
&from_packet_total, &to_packet_total,
&from_data_total_dropped, &to_data_total_dropped,
&from_packet_total_dropped, &to_packet_total_dropped);
/*
* Use fresh buffer
*/
sfi->msgp = sfi->msg_buffer;
/*
* Prep the message
*/
msg_len = snprintf(sfi->msgp, ECM_DB_STATE_FILE_BUFFER_SIZE,
"<conn serial=\"%u\" sip_address=\"%s\" sip_address_nat=\"%s\" sport=\"%d\" sport_nat=\"%d\" snode_address=\"%s\" snode_address_nat=\"%s\""
" dip_address=\"%s\" dip_address_nat=\"%s\" dport=\"%d\" dport_nat=\"%d\" dnode_address=\"%s\" dnode_address_nat=\"%s\""
" protocol=\"%d\" is_routed=\"%d\" expires=\"%ld\" direction=\"%d\" time_added=\"%u\""
" from_data_total=\"%llu\" to_data_total=\"%llu\" from_packet_total=\"%llu\" to_packet_total=\"%llu\" from_data_total_dropped=\"%llu\" to_data_total_dropped=\"%llu\" from_packet_total_dropped=\"%llu\" to_packet_total_dropped=\"%llu\">\n",
serial,
sip_address,
sip_address_nat,
sport,
sport_nat,
snode_address,
snode_address_nat,
dip_address,
dip_address_nat,
dport,
dport_nat,
dnode_address,
dnode_address_nat,
protocol,
is_routed,
expires_in,
direction,
time_added,
from_data_total,
to_data_total,
from_packet_total,
to_packet_total,
from_data_total_dropped,
to_data_total_dropped,
from_packet_total_dropped,
to_packet_total_dropped);
if ((msg_len <= 0) || (msg_len >= ECM_DB_STATE_FILE_BUFFER_SIZE)) {
return false;
}
/*
* Output interface heirarchy information for this connection
*/
first_interface = ecm_db_connection_from_interfaces_get_and_ref(sfi->ci, interfaces);
extra_msg_len = ecm_db_connection_heirarchy_xml_state_get("from_interfaces", interfaces, first_interface, sfi->msgp + msg_len, ECM_DB_STATE_FILE_BUFFER_SIZE - msg_len);
ecm_db_connection_interfaces_deref(interfaces, first_interface);
if ((extra_msg_len <= 0) || (extra_msg_len >= (ECM_DB_STATE_FILE_BUFFER_SIZE - msg_len))) {
return false;
}
msg_len += extra_msg_len;
first_interface = ecm_db_connection_to_interfaces_get_and_ref(sfi->ci, interfaces);
extra_msg_len = ecm_db_connection_heirarchy_xml_state_get("to_interfaces", interfaces, first_interface, sfi->msgp + msg_len, ECM_DB_STATE_FILE_BUFFER_SIZE - msg_len);
ecm_db_connection_interfaces_deref(interfaces, first_interface);
if ((extra_msg_len <= 0) || (extra_msg_len >= (ECM_DB_STATE_FILE_BUFFER_SIZE - msg_len))) {
return false;
}
msg_len += extra_msg_len;
first_interface = ecm_db_connection_from_nat_interfaces_get_and_ref(sfi->ci, interfaces);
extra_msg_len = ecm_db_connection_heirarchy_xml_state_get("from_nat_interfaces", interfaces, first_interface, sfi->msgp + msg_len, ECM_DB_STATE_FILE_BUFFER_SIZE - msg_len);
ecm_db_connection_interfaces_deref(interfaces, first_interface);
if ((extra_msg_len <= 0) || (extra_msg_len >= (ECM_DB_STATE_FILE_BUFFER_SIZE - msg_len))) {
return false;
}
msg_len += extra_msg_len;
first_interface = ecm_db_connection_to_nat_interfaces_get_and_ref(sfi->ci, interfaces);
extra_msg_len = ecm_db_connection_heirarchy_xml_state_get("to_nat_interfaces", interfaces, first_interface, sfi->msgp + msg_len, ECM_DB_STATE_FILE_BUFFER_SIZE - msg_len);
ecm_db_connection_interfaces_deref(interfaces, first_interface);
if ((extra_msg_len <= 0) || (extra_msg_len >= (ECM_DB_STATE_FILE_BUFFER_SIZE - msg_len))) {
return false;
}
msg_len += extra_msg_len;
/*
* Output front end state
*/
feci = ecm_db_connection_front_end_get_and_ref(sfi->ci);
extra_msg_len = feci->xml_state_get(feci, sfi->msgp + msg_len, ECM_DB_STATE_FILE_BUFFER_SIZE - msg_len);
feci->deref(feci);
if ((extra_msg_len <= 0) || (extra_msg_len >= (ECM_DB_STATE_FILE_BUFFER_SIZE - msg_len))) {
return false;
}
msg_len += extra_msg_len;
/*
* Grab references to the assigned classifiers so we can produce state for them
*/
aci_count = ecm_db_connection_classifier_assignments_get_and_ref(sfi->ci, assignments);
/*
* Iterate the assigned classifiers and provide a state record for each
*/
for (aci_index = 0; aci_index < aci_count; ++aci_index) {
struct ecm_classifier_instance *aci;
aci = assignments[aci_index];
extra_msg_len = aci->xml_state_get(aci, sfi->msgp + msg_len, ECM_DB_STATE_FILE_BUFFER_SIZE - msg_len);
if ((extra_msg_len <= 0) || (extra_msg_len >= (ECM_DB_STATE_FILE_BUFFER_SIZE - msg_len))) {
ecm_db_connection_assignments_release(aci_count, assignments);
return false;
}
msg_len += extra_msg_len;
}
ecm_db_connection_assignments_release(aci_count, assignments);
/*
* Write out end element
*/
extra_msg_len = snprintf(sfi->msgp + msg_len, ECM_DB_STATE_FILE_BUFFER_SIZE - msg_len, "</conn>\n");
if ((extra_msg_len <= 0) || (extra_msg_len >= (ECM_DB_STATE_FILE_BUFFER_SIZE - msg_len))) {
return false;
}
msg_len += extra_msg_len;
/*
* Record the message length
*/
sfi->msg_len = msg_len;
DEBUG_TRACE("%p: Prepped msg %s\n", sfi, sfi->msgp);
return true;
}
/*
* ecm_db_char_dev_mapping_msg_prep()
* Prepare a mapping message
*/
static bool ecm_db_char_dev_mapping_msg_prep(struct ecm_db_state_file_instance *sfi)
{
int msg_len;
int port;
char address[25];
int tcp_from;
int tcp_to;
int udp_from;
int udp_to;
int from;
int to;
int tcp_nat_from;
int tcp_nat_to;
int udp_nat_from;
int udp_nat_to;
int nat_from;
int nat_to;
uint32_t time_added;
uint64_t from_data_total;
uint64_t to_data_total;
uint64_t from_packet_total;
uint64_t to_packet_total;
uint64_t from_data_total_dropped;
uint64_t to_data_total_dropped;
uint64_t from_packet_total_dropped;
uint64_t to_packet_total_dropped;
char node_address[25];
struct ecm_db_host_instance *hi;
DEBUG_TRACE("%p: Prep mapping msg for %p\n", sfi, sfi->mi);
/*
* Create a small xml stats element for our mapping.
* Extract information from the mapping for inclusion into the message
*/
ecm_db_mapping_port_count_get(sfi->mi, &tcp_from, &tcp_to, &udp_from, &udp_to, &from, &to,
&tcp_nat_from, &tcp_nat_to, &udp_nat_from, &udp_nat_to, &nat_from, &nat_to);
port = sfi->mi->port;
time_added = sfi->mi->time_added;
ecm_db_mapping_data_stats_get(sfi->mi, &from_data_total, &to_data_total,
&from_packet_total, &to_packet_total,
&from_data_total_dropped, &to_data_total_dropped,
&from_packet_total_dropped, &to_packet_total_dropped);
hi = sfi->mi->host;
ecm_ip_addr_to_string(address, hi->address);
sprintf(node_address, "%pM", hi->node->address);
/*
* Use fresh buffer
*/
sfi->msgp = sfi->msg_buffer;
/*
* Prep the message
*/
msg_len = snprintf(sfi->msgp, ECM_DB_STATE_FILE_BUFFER_SIZE,
"<mapping address=\"%s\" port=\"%d\" from=\"%d\" to=\"%d\" tcp_from=\"%d\" tcp_to=\"%d\" udp_from=\"%d\" udp_to=\"%d\""
" nat_from=\"%d\" nat_to=\"%d\" tcp_nat_from=\"%d\" tcp_nat_to=\"%d\" udp_nat_from=\"%d\" udp_nat_to=\"%d\""
" from_data_total=\"%llu\" to_data_total=\"%llu\" from_packet_total=\"%llu\" to_packet_total=\"%llu\""
" from_data_total_dropped=\"%llu\" to_data_total_dropped=\"%llu\" from_packet_total_dropped=\"%llu\" to_packet_total_dropped=\"%llu\""
" time_added=\"%u\" node_address=\"%s\"/>\n",
address,
port,
from,
to,
tcp_from,
tcp_to,
udp_from,
udp_to,
nat_from,
nat_to,
tcp_nat_from,
tcp_nat_to,
udp_nat_from,
udp_nat_to,
from_data_total,
to_data_total,
from_packet_total,
to_packet_total,
from_data_total_dropped,
to_data_total_dropped,
from_packet_total_dropped,
to_packet_total_dropped,
time_added,
node_address);
if ((msg_len <= 0) || (msg_len >= ECM_DB_STATE_FILE_BUFFER_SIZE)) {
return false;
}
sfi->msg_len = msg_len;
DEBUG_TRACE("%p: Prepped msg %s\n", sfi, sfi->msgp);
return true;
}
/*
* ecm_db_char_dev_host_msg_prep()
* Prepare a host message
*/
static bool ecm_db_char_dev_host_msg_prep(struct ecm_db_state_file_instance *sfi)
{
int msg_len;
char address[50];
int mapping_count;
uint32_t time_added;
uint64_t from_data_total;
uint64_t to_data_total;
uint64_t from_packet_total;
uint64_t to_packet_total;
uint64_t from_data_total_dropped;
uint64_t to_data_total_dropped;
uint64_t from_packet_total_dropped;
uint64_t to_packet_total_dropped;
char node_address[25];
bool on_link;
DEBUG_TRACE("%p: Prep host msg for %p\n", sfi, sfi->hi);
/*
* Create a small xml stats element for our host.
* Extract information from the host for inclusion into the message
*/
mapping_count = ecm_db_host_mapping_count_get(sfi->hi);
ecm_ip_addr_to_string(address, sfi->hi->address);
time_added = sfi->hi->time_added;
ecm_db_host_data_stats_get(sfi->hi, &from_data_total, &to_data_total,
&from_packet_total, &to_packet_total,
&from_data_total_dropped, &to_data_total_dropped,
&from_packet_total_dropped, &to_packet_total_dropped);
sprintf(node_address, "%pM", sfi->hi->node->address);
on_link = sfi->hi->on_link;
/*
* Use fresh buffer
*/
sfi->msgp = sfi->msg_buffer;
/*
* Prep the message
*/
msg_len = snprintf(sfi->msgp, ECM_DB_STATE_FILE_BUFFER_SIZE,
"<host address=\"%s\" mappings=\"%d\" time_added=\"%u\" node_address=\"%s\" on_link=\"%d\""
" from_data_total=\"%llu\" to_data_total=\"%llu\" from_packet_total=\"%llu\" to_packet_total=\"%llu\""
" from_data_total_dropped=\"%llu\" to_data_total_dropped=\"%llu\" from_packet_total_dropped=\"%llu\" to_packet_total_dropped=\"%llu\"/>\n",
address,
mapping_count,
time_added,
node_address,
on_link,
from_data_total,
to_data_total,
from_packet_total,
to_packet_total,
from_data_total_dropped,
to_data_total_dropped,
from_packet_total_dropped,
to_packet_total_dropped);
if ((msg_len <= 0) || (msg_len >= ECM_DB_STATE_FILE_BUFFER_SIZE)) {
return false;
}
sfi->msg_len = msg_len;
DEBUG_TRACE("%p: Prepped msg %s\n", sfi, sfi->msgp);
return true;
}
/*
* ecm_db_char_dev_nod__msg_prep()
* Prepare a node message
*/
static bool ecm_db_char_dev_node_msg_prep(struct ecm_db_state_file_instance *sfi)
{
int msg_len;
char address[25];
int host_count;
uint32_t time_added;
uint64_t from_data_total;
uint64_t to_data_total;
uint64_t from_packet_total;
uint64_t to_packet_total;
uint64_t from_data_total_dropped;
uint64_t to_data_total_dropped;
uint64_t from_packet_total_dropped;
uint64_t to_packet_total_dropped;
DEBUG_TRACE("%p: Prep node msg for %p\n", sfi, sfi->ni);
/*
* Create a small xml stats block for our managed node, like:
* <node address="" hosts="" time_added="" from_data_total="" to_data_total="" />
*
* Extract information from the node for inclusion into the message
*/
host_count = ecm_db_node_host_count_get(sfi->ni);
time_added = sfi->ni->time_added;
ecm_db_node_data_stats_get(sfi->ni, &from_data_total, &to_data_total,
&from_packet_total, &to_packet_total,
&from_data_total_dropped, &to_data_total_dropped,
&from_packet_total_dropped, &to_packet_total_dropped);
sprintf(address, "%pM", sfi->ni->address);
/*
* Use fresh buffer
*/
sfi->msgp = sfi->msg_buffer;
/*
* Prep the message
*/
msg_len = snprintf(sfi->msgp, ECM_DB_STATE_FILE_BUFFER_SIZE,
"<node address=\"%s\" mappings=\"%d\" time_added=\"%u\""
" from_data_total=\"%llu\" to_data_total=\"%llu\" from_packet_total=\"%llu\" to_packet_total=\"%llu\""
" from_data_total_dropped=\"%llu\" to_data_total_dropped=\"%llu\" from_packet_total_dropped=\"%llu\" to_packet_total_dropped=\"%llu\" />\n",
address,
host_count,
time_added,
from_data_total,
to_data_total,
from_packet_total,
to_packet_total,
from_data_total_dropped,
to_data_total_dropped,
from_packet_total_dropped,
to_packet_total_dropped);
if ((msg_len <= 0) || (msg_len >= ECM_DB_STATE_FILE_BUFFER_SIZE)) {
return false;
}
sfi->msg_len = msg_len;
DEBUG_TRACE("%p: Prepped msg %s\n", sfi, sfi->msgp);
return true;
}
/*
* ecm_db_char_dev_iface_msg_prep()
* Prepare an interface message
*/
static bool ecm_db_char_dev_iface_msg_prep(struct ecm_db_state_file_instance *sfi)
{
int msg_len;
DEBUG_TRACE("%p: Prep iface msg for %p\n", sfi, sfi->ii);
/*
* Use fresh buffer
*/
sfi->msgp = sfi->msg_buffer;
/*
* Prep the message
*/
msg_len = sfi->ii->xml_state_get(sfi->ii, sfi->msgp, ECM_DB_STATE_FILE_BUFFER_SIZE);
if ((msg_len <= 0) || (msg_len >= ECM_DB_STATE_FILE_BUFFER_SIZE)) {
return false;
}
/*
* Record the message length
*/
sfi->msg_len = msg_len;
DEBUG_TRACE("%p: Prepped msg %s\n", sfi, sfi->msgp);
return true;
}
/*
* ecm_db_char_dev_conn_chain_msg_prep()
* Generate an conn hash table chain message
*/
static bool ecm_db_char_dev_conn_chain_msg_prep(struct ecm_db_state_file_instance *sfi)
{
int chain_len;
int msg_len;
DEBUG_TRACE("%p: Prep conn chain msg\n", sfi);
/*
* Get hash table chain length
*/
spin_lock_bh(&ecm_db_lock);
chain_len = ecm_db_connection_table_lengths[sfi->connection_hash_index];
spin_unlock_bh(&ecm_db_lock);
/*
* Use fresh buffer
*/
sfi->msgp = sfi->msg_buffer;
/*
* Create a small xml stats block like:
* <conn_chain hash_index="" chain_length=""/>
*/
msg_len = snprintf(sfi->msgp, ECM_DB_STATE_FILE_BUFFER_SIZE,
"<conn_chain hash_index=\"%d\" chain_length=\"%d\"/>\n",
sfi->connection_hash_index,
chain_len);
if ((msg_len <= 0) || (msg_len >= ECM_DB_STATE_FILE_BUFFER_SIZE)) {
return false;
}
sfi->msg_len = msg_len;
DEBUG_TRACE("%p: Prepped msg %s\n", sfi, sfi->msgp);
return true;
}
/*
* ecm_db_char_dev_mapping_chain_msg_prep()
* Generate an mapping hash table chain message
*/
static bool ecm_db_char_dev_mapping_chain_msg_prep(struct ecm_db_state_file_instance *sfi)
{
int chain_len;
int msg_len;
DEBUG_TRACE("%p: Prep mapping chain msg\n", sfi);
/*
* Get hash table chain length
*/
spin_lock_bh(&ecm_db_lock);
chain_len = ecm_db_mapping_table_lengths[sfi->mapping_hash_index];
spin_unlock_bh(&ecm_db_lock);
/*
* Use fresh buffer
*/
sfi->msgp = sfi->msg_buffer;
/*
* Create a small xml stats block like:
* <mapping_chain hash_index="" chain_length=""/>
*/
msg_len = snprintf(sfi->msgp, ECM_DB_STATE_FILE_BUFFER_SIZE,
"<mapping_chain hash_index=\"%d\" chain_length=\"%d\"/>\n",
sfi->mapping_hash_index,
chain_len);
if ((msg_len <= 0) || (msg_len >= ECM_DB_STATE_FILE_BUFFER_SIZE)) {
return false;
}
sfi->msg_len = msg_len;
DEBUG_TRACE("%p: Prepped msg %s\n", sfi, sfi->msgp);
return true;
}
/*
* ecm_db_char_dev_host_chain_msg_prep()
* Generate an host hash table chain message
*/
static bool ecm_db_char_dev_host_chain_msg_prep(struct ecm_db_state_file_instance *sfi)
{
int chain_len;
int msg_len;
DEBUG_TRACE("%p: Prep host chain msg\n", sfi);
/*
* Get hash table chain length
*/
spin_lock_bh(&ecm_db_lock);
chain_len = ecm_db_host_table_lengths[sfi->host_hash_index];
spin_unlock_bh(&ecm_db_lock);
/*
* Use fresh buffer
*/
sfi->msgp = sfi->msg_buffer;
/*
* Create a small xml stats block like:
* <host_chain hash_index="" chain_length=""/>
*/
msg_len = snprintf(sfi->msgp, ECM_DB_STATE_FILE_BUFFER_SIZE,
"<host_chain hash_index=\"%d\" chain_length=\"%d\"/>\n",
sfi->host_hash_index,
chain_len);
if ((msg_len <= 0) || (msg_len >= ECM_DB_STATE_FILE_BUFFER_SIZE)) {
return false;
}
sfi->msg_len = msg_len;
DEBUG_TRACE("%p: Prepped msg %s\n", sfi, sfi->msgp);
return true;
}
/*
* ecm_db_char_dev_node_chain_msg_prep()
* Generate an node hash table chain message
*/
static bool ecm_db_char_dev_node_chain_msg_prep(struct ecm_db_state_file_instance *sfi)
{
int chain_len;
int msg_len;
DEBUG_TRACE("%p: Prep node chain msg\n", sfi);
/*
* Get hash table chain length
*/
spin_lock_bh(&ecm_db_lock);
chain_len = ecm_db_node_table_lengths[sfi->node_hash_index];
spin_unlock_bh(&ecm_db_lock);
/*
* Use fresh buffer
*/
sfi->msgp = sfi->msg_buffer;
/*
* Create a small xml stats block like:
* <node_chain hash_index="" chain_length=""/>
*/
msg_len = snprintf(sfi->msgp, ECM_DB_STATE_FILE_BUFFER_SIZE,
"<node_chain hash_index=\"%d\" chain_length=\"%d\"/>\n",
sfi->node_hash_index,
chain_len);
if ((msg_len <= 0) || (msg_len >= ECM_DB_STATE_FILE_BUFFER_SIZE)) {
return false;
}
sfi->msg_len = msg_len;
DEBUG_TRACE("%p: Prepped msg %s\n", sfi, sfi->msgp);
return true;
}
/*
* ecm_db_char_dev_iface_chain_msg_prep()
* Generate an interface hash table chain message
*/
static bool ecm_db_char_dev_iface_chain_msg_prep(struct ecm_db_state_file_instance *sfi)
{
int chain_len;
int msg_len;
DEBUG_TRACE("%p: Prep iface chain msg\n", sfi);
/*
* Get hash table chain length
*/
spin_lock_bh(&ecm_db_lock);
chain_len = ecm_db_iface_table_lengths[sfi->iface_hash_index];
spin_unlock_bh(&ecm_db_lock);
/*
* Use fresh buffer
*/
sfi->msgp = sfi->msg_buffer;
/*
* Create a small xml stats block like:
* <iface_chain hash_index="" chain_length=""/>
*/
msg_len = snprintf(sfi->msgp, ECM_DB_STATE_FILE_BUFFER_SIZE,
"<iface_chain hash_index=\"%d\" chain_length=\"%d\"/>\n",
sfi->iface_hash_index,
chain_len);
if ((msg_len <= 0) || (msg_len >= ECM_DB_STATE_FILE_BUFFER_SIZE)) {
return false;
}
sfi->msg_len = msg_len;
DEBUG_TRACE("%p: Prepped msg %s\n", sfi, sfi->msgp);
return true;
}
/*
* ecm_db_char_dev_protocol_count_msg_prep()
* Generate a protocol usage message
*/
static bool ecm_db_char_dev_protocol_count_msg_prep(struct ecm_db_state_file_instance *sfi)
{
int count;
int msg_len;
DEBUG_TRACE("%p: Prep protocol msg\n", sfi);
/*
* Get protocol connection total count
*/
spin_lock_bh(&ecm_db_lock);
count = ecm_db_connection_count_by_protocol[sfi->protocol];
spin_unlock_bh(&ecm_db_lock);
/*
* Use fresh buffer
*/
sfi->msgp = sfi->msg_buffer;
/*
* Create a small xml stats block like:
* <conn_proto_count protocol="" count=""/>
*/
msg_len = snprintf(sfi->msgp, ECM_DB_STATE_FILE_BUFFER_SIZE,
"<conn_proto_count protocol=\"%d\" count=\"%d\"/>\n",
sfi->protocol,
count);
if ((msg_len <= 0) || (msg_len >= ECM_DB_STATE_FILE_BUFFER_SIZE)) {
return false;
}
sfi->msg_len = msg_len;
DEBUG_TRACE("%p: Prepped msg %s\n", sfi, sfi->msgp);
return true;
}
/*
* ecm_db_char_device_open()
* Opens the special char device file which we use to dump our state.
*
*/
static int ecm_db_char_device_open(struct inode *inode, struct file *file)
{
struct ecm_db_state_file_instance *sfi;
DEBUG_INFO("State open\n");
/*
* Allocate state information for the reading
*/
DEBUG_ASSERT(file->private_data == NULL, "unexpected double open: %p?\n", file->private_data);
sfi = (struct ecm_db_state_file_instance *)kzalloc(sizeof(struct ecm_db_state_file_instance), GFP_ATOMIC | __GFP_NOWARN);
if (!sfi) {
return -ENOMEM;
}
DEBUG_SET_MAGIC(sfi, ECM_DB_STATE_FILE_INSTANCE_MAGIC);
file->private_data = sfi;
/*
* Snapshot output mask for this file
*/
spin_lock_bh(&ecm_db_lock);
sfi->output_mask = ecm_db_state_file_output_mask;
spin_unlock_bh(&ecm_db_lock);
/*
* Take references to each object list that we are going to generate state for.
*/
if (sfi->output_mask & ECM_DB_STATE_FILE_OUTPUT_CONNECTIONS) {
sfi->ci = ecm_db_connections_get_and_ref_first();
}
if (sfi->output_mask & ECM_DB_STATE_FILE_OUTPUT_MAPPINGS) {
sfi->mi = ecm_db_mappings_get_and_ref_first();
}
if (sfi->output_mask & ECM_DB_STATE_FILE_OUTPUT_HOSTS) {
sfi->hi = ecm_db_hosts_get_and_ref_first();
}
if (sfi->output_mask & ECM_DB_STATE_FILE_OUTPUT_NODES) {
sfi->ni = ecm_db_nodes_get_and_ref_first();
}
if (sfi->output_mask & ECM_DB_STATE_FILE_OUTPUT_INTERFACES) {
sfi->ii = ecm_db_interfaces_get_and_ref_first();
}
/*
* Cannot do this if the event processing thread is exiting
*/
spin_lock_bh(&ecm_db_lock);
if (ecm_db_terminate_pending) {
spin_unlock_bh(&ecm_db_lock);
if (sfi->ci) {
ecm_db_connection_deref(sfi->ci);
}
if (sfi->mi) {
ecm_db_mapping_deref(sfi->mi);
}
if (sfi->hi) {
ecm_db_host_deref(sfi->hi);
}
if (sfi->ni) {
ecm_db_node_deref(sfi->ni);
}
if (sfi->ii) {
ecm_db_iface_deref(sfi->ii);
}
kfree(sfi);
DEBUG_WARN("Terminating\n");
return -EBUSY;
}
spin_unlock_bh(&ecm_db_lock);
DEBUG_INFO("State opened %p\n", sfi);
return 0;
}
/*
* ecm_db_char_device_release()
* Called when a process closes the device file.
*/
static int ecm_db_char_device_release(struct inode *inode, struct file *file)
{
struct ecm_db_state_file_instance *sfi;
sfi = (struct ecm_db_state_file_instance *)file->private_data;
DEBUG_CHECK_MAGIC(sfi, ECM_DB_STATE_FILE_INSTANCE_MAGIC, "%p: magic failed", sfi);
DEBUG_INFO("%p: State close\n", sfi);
/*
* Release any references held
*/
if (sfi->ci) {
ecm_db_connection_deref(sfi->ci);
}
if (sfi->mi) {
ecm_db_mapping_deref(sfi->mi);
}
if (sfi->hi) {
ecm_db_host_deref(sfi->hi);
}
if (sfi->ni) {
ecm_db_node_deref(sfi->ni);
}
if (sfi->ii) {
ecm_db_iface_deref(sfi->ii);
}
DEBUG_CLEAR_MAGIC(sfi);
kfree(sfi);
return 0;
}
/*
* ecm_db_char_device_read()
* Called to read the state
*/
static ssize_t ecm_db_char_device_read(struct file *file, /* see include/linux/fs.h */
char *buffer, /* buffer to fill with data */
size_t length, /* length of the buffer */
loff_t *offset) /* Doesn't apply - this is a char file */
{
struct ecm_db_state_file_instance *sfi;
int bytes_read = 0; /* Number of bytes actually written to the buffer */
sfi = (struct ecm_db_state_file_instance *)file->private_data;
DEBUG_CHECK_MAGIC(sfi, ECM_DB_STATE_FILE_INSTANCE_MAGIC, "%p: magic failed", sfi);
DEBUG_TRACE("%p: State read up to length %d bytes\n", sfi, length);
do {
/*
* If there is still some message remaining to be output then complete that first
*/
if (sfi->msg_len) {
break;
}
if (!sfi->doc_start_written) {
sfi->msgp = sfi->msg_buffer;
sfi->msg_len = sprintf(sfi->msgp, "<ecm_db>\n");
sfi->doc_start_written = true;
break;
}
if (sfi->ci) {
struct ecm_db_connection_instance *cin;
if (!ecm_db_char_dev_conn_msg_prep(sfi)) {
return -EIO;
}
/*
* Next connection for when we return
*/
cin = ecm_db_connection_get_and_ref_next(sfi->ci);
ecm_db_connection_deref(sfi->ci);
sfi->ci = cin;
break;
}
if (sfi->mi) {
struct ecm_db_mapping_instance *min;
if (!ecm_db_char_dev_mapping_msg_prep(sfi)) {
return -EIO;
}
/*
* Next mapping for when we return
*/
min = ecm_db_mapping_get_and_ref_next(sfi->mi);
ecm_db_mapping_deref(sfi->mi);
sfi->mi = min;
break;
}
if (sfi->hi) {
struct ecm_db_host_instance *hin;
if (!ecm_db_char_dev_host_msg_prep(sfi)) {
return -EIO;
}
/*
* Next host for when we return
*/
hin = ecm_db_host_get_and_ref_next(sfi->hi);
ecm_db_host_deref(sfi->hi);
sfi->hi = hin;
break;
}
if (sfi->ni) {
struct ecm_db_node_instance *nin;
if (!ecm_db_char_dev_node_msg_prep(sfi)) {
return -EIO;
}
/*
* Next node for when we return
*/
nin = ecm_db_node_get_and_ref_next(sfi->ni);
ecm_db_node_deref(sfi->ni);
sfi->ni = nin;
break;
}
if (sfi->ii) {
struct ecm_db_iface_instance *iin;
if (!ecm_db_char_dev_iface_msg_prep(sfi)) {
return -EIO;
}
/*
* Next iface for when we return
*/
iin = ecm_db_interface_get_and_ref_next(sfi->ii);
ecm_db_iface_deref(sfi->ii);
sfi->ii = iin;
break;
}
if ((sfi->output_mask & ECM_DB_STATE_FILE_OUTPUT_CONNECTIONS_CHAIN) && (sfi->connection_hash_index < ECM_DB_CONNECTION_HASH_SLOTS)) {
if (!ecm_db_char_dev_conn_chain_msg_prep(sfi)) {
return -EIO;
}
sfi->connection_hash_index++;
break;
}
if ((sfi->output_mask & ECM_DB_STATE_FILE_OUTPUT_MAPPINGS_CHAIN) && (sfi->mapping_hash_index < ECM_DB_MAPPING_HASH_SLOTS)) {
if (!ecm_db_char_dev_mapping_chain_msg_prep(sfi)) {
return -EIO;
}
sfi->mapping_hash_index++;
break;
}
if ((sfi->output_mask & ECM_DB_STATE_FILE_OUTPUT_HOSTS_CHAIN) && (sfi->host_hash_index < ECM_DB_HOST_HASH_SLOTS)) {
if (!ecm_db_char_dev_host_chain_msg_prep(sfi)) {
return -EIO;
}
sfi->host_hash_index++;
break;
}
if ((sfi->output_mask & ECM_DB_STATE_FILE_OUTPUT_NODES_CHAIN) && (sfi->node_hash_index < ECM_DB_NODE_HASH_SLOTS)) {
if (!ecm_db_char_dev_node_chain_msg_prep(sfi)) {
return -EIO;
}
sfi->node_hash_index++;
break;
}
if ((sfi->output_mask & ECM_DB_STATE_FILE_OUTPUT_INTERFACES_CHAIN) && (sfi->iface_hash_index < ECM_DB_IFACE_HASH_SLOTS)) {
if (!ecm_db_char_dev_iface_chain_msg_prep(sfi)) {
return -EIO;
}
sfi->iface_hash_index++;
break;
}
if ((sfi->output_mask & ECM_DB_STATE_FILE_OUTPUT_PROTOCOL_COUNTS) && (sfi->protocol < 256)) {
if (!ecm_db_char_dev_protocol_count_msg_prep(sfi)) {
return -EIO;
}
sfi->protocol++;
break;
}
if (!sfi->doc_end_written) {
sfi->msgp = sfi->msg_buffer;
sfi->msg_len = sprintf(sfi->msgp, "</ecm_db>\n");
sfi->doc_end_written = true;
break;
}
/*
* EOF
*/
return 0;
} while (false);
/*
* If supplied buffer is small we limit what we output
*/
bytes_read = sfi->msg_len;
if (bytes_read > length) {
bytes_read = length;
}
if (copy_to_user(buffer, sfi->msgp, bytes_read)) {
return -EIO;
}
sfi->msg_len -= bytes_read;
sfi->msgp += bytes_read;
DEBUG_TRACE("State read done, bytes_read %d bytes\n", bytes_read);
/*
* Most read functions return the number of bytes put into the buffer
*/
return bytes_read;
}
/*
* ecm_db_char_device_write()
*/
static ssize_t ecm_db_char_device_write(struct file *filp, const char *buff, size_t len, loff_t * off)
{
return -EINVAL;
}
/*
* File operations used in the char device
* NOTE: The char device is a simple file that allows us to dump our connection tracking state
*/
static struct file_operations ecm_db_fops = {
.read = ecm_db_char_device_read,
.write = ecm_db_char_device_write,
.open = ecm_db_char_device_open,
.release = ecm_db_char_device_release
};
/*
* ecm_db_timer_callback()
* Manage expiration of connections
* NOTE: This is softirq context
*/
static void ecm_db_timer_callback(unsigned long data)
{
uint32_t timer;
/*
* Increment timer.
*/
spin_lock_bh(&ecm_db_lock);
timer = ++ecm_db_time;
spin_unlock_bh(&ecm_db_lock);
DEBUG_TRACE("Garbage timer tick %d\n", timer);
/*
* Check timer groups
*/
ecm_db_timer_groups_check(timer);
/*
* Set the timer for the next second
*/
ecm_db_timer.expires += HZ;
if (ecm_db_timer.expires <= jiffies) {
DEBUG_WARN("losing time %lu, jiffies = %lu\n", ecm_db_timer.expires, jiffies);
ecm_db_timer.expires = jiffies + HZ;
}
add_timer(&ecm_db_timer);
}
/*
* ecm_db_init()
*/
int ecm_db_init(void)
{
int result;
DEBUG_INFO("ECM Module init\n");
/*
* Initialise our global database lock
*/
spin_lock_init(&ecm_db_lock);
/*
* Register the sysfs class
*/
result = sysdev_class_register(&ecm_db_sysclass);
if (result) {
DEBUG_ERROR("Failed to register SysFS class %d\n", result);
return result;
}
/*
* Register SYSFS device control
*/
memset(&ecm_db_sys_dev, 0, sizeof(ecm_db_sys_dev));
ecm_db_sys_dev.id = 0;
ecm_db_sys_dev.cls = &ecm_db_sysclass;
result = sysdev_register(&ecm_db_sys_dev);
if (result) {
DEBUG_ERROR("Failed to register SysFS device %d\n", result);
goto task_cleanup_1;
}
/*
* Create files, one for each parameter supported by this module
*/
result = sysdev_create_file(&ecm_db_sys_dev, &attr_state_dev_major);
if (result) {
DEBUG_ERROR("Failed to register dev major file %d\n", result);
goto task_cleanup_2;
}
result = sysdev_create_file(&ecm_db_sys_dev, &attr_connection_count);
if (result) {
DEBUG_ERROR("Failed to register conn count SysFS file\n");
goto task_cleanup_2;
}
result = sysdev_create_file(&ecm_db_sys_dev, &attr_host_count);
if (result) {
DEBUG_ERROR("Failed to register host count SysFS file\n");
goto task_cleanup_2;
}
result = sysdev_create_file(&ecm_db_sys_dev, &attr_mapping_count);
if (result) {
DEBUG_ERROR("Failed to register mapping count SysFS file\n");
goto task_cleanup_2;
}
result = sysdev_create_file(&ecm_db_sys_dev, &attr_defunct_all);
if (result) {
DEBUG_ERROR("Failed to register expire all SysFS file\n");
goto task_cleanup_2;
}
result = sysdev_create_file(&ecm_db_sys_dev, &attr_node_count);
if (result) {
DEBUG_ERROR("Failed to register node count SysFS file\n");
goto task_cleanup_2;
}
result = sysdev_create_file(&ecm_db_sys_dev, &attr_iface_count);
if (result) {
DEBUG_ERROR("Failed to register iface count SysFS file\n");
goto task_cleanup_2;
}
result = sysdev_create_file(&ecm_db_sys_dev, &attr_connection_counts_simple);
if (result) {
DEBUG_ERROR("Failed to register connection counts simple SysFS file\n");
goto task_cleanup_2;
}
result = sysdev_create_file(&ecm_db_sys_dev, &attr_state_file_output_mask);
if (result) {
DEBUG_ERROR("Failed to register state_file_output_mask SysFS file\n");
goto task_cleanup_2;
}
/*
* Register a char device that we will use to provide a dump of our state
*/
result = register_chrdev(0, ecm_db_sysclass.name, &ecm_db_fops);
if (result < 0) {
DEBUG_ERROR("Failed to register chrdev %d\n", result);
goto task_cleanup_2;
}
ecm_db_dev_major_id = result;
DEBUG_TRACE("registered chr dev major id assigned %d\n", ecm_db_dev_major_id);
/*
* Set a timer to manage cleanup of expired connections
*/
init_timer(&ecm_db_timer);
ecm_db_timer.function = ecm_db_timer_callback;
ecm_db_timer.data = 0;
ecm_db_timer.expires = jiffies + HZ;
add_timer(&ecm_db_timer);
/*
* Initialise timer groups with time values
*/
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CLASSIFIER_DETERMINE_GENERIC_TIMEOUT].time = ECM_DB_CLASSIFIER_DETERMINE_GENERIC_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CLASSIFIER_DETERMINE_GENERIC_TIMEOUT].tg = ECM_DB_TIMER_GROUPS_CLASSIFIER_DETERMINE_GENERIC_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_GENERIC_TIMEOUT].time = ECM_DB_CONNECTION_GENERIC_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_GENERIC_TIMEOUT].tg = ECM_DB_TIMER_GROUPS_CONNECTION_GENERIC_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_IGMP_TIMEOUT].time = ECM_DB_CONNECTION_IGMP_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_IGMP_TIMEOUT].tg = ECM_DB_TIMER_GROUPS_CONNECTION_IGMP_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_UDP_GENERIC_TIMEOUT].time = ECM_DB_CONNECTION_UDP_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_UDP_GENERIC_TIMEOUT].tg = ECM_DB_TIMER_GROUPS_CONNECTION_UDP_GENERIC_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_UDP_WKP_TIMEOUT].time = ECM_DB_CONNECTION_UDP_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_UDP_WKP_TIMEOUT].tg = ECM_DB_TIMER_GROUPS_CONNECTION_UDP_WKP_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_ICMP_TIMEOUT].time = ECM_DB_CONNECTION_ICMP_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_ICMP_TIMEOUT].tg = ECM_DB_TIMER_GROUPS_CONNECTION_ICMP_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_TCP_SHORT_TIMEOUT].time = ECM_DB_CONNECTION_TCP_SHORT_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_TCP_SHORT_TIMEOUT].tg = ECM_DB_TIMER_GROUPS_CONNECTION_TCP_SHORT_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_TCP_RESET_TIMEOUT].time = ECM_DB_CONNECTION_TCP_RST_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_TCP_RESET_TIMEOUT].tg = ECM_DB_TIMER_GROUPS_CONNECTION_TCP_RESET_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_TCP_LONG_TIMEOUT].time = ECM_DB_CONNECTION_TCP_LONG_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_TCP_LONG_TIMEOUT].tg = ECM_DB_TIMER_GROUPS_CONNECTION_TCP_LONG_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_PPTP_DATA_TIMEOUT].time = ECM_DB_CONNECTION_PPTP_DATA_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_PPTP_DATA_TIMEOUT].tg = ECM_DB_TIMER_GROUPS_CONNECTION_PPTP_DATA_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_RTCP_TIMEOUT].time = ECM_DB_CONNECTION_RTCP_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_RTCP_TIMEOUT].tg = ECM_DB_TIMER_GROUPS_CONNECTION_RTCP_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_RTSP_TIMEOUT].time = ECM_DB_CONNECTION_TCP_LONG_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_RTSP_TIMEOUT].tg = ECM_DB_TIMER_GROUPS_CONNECTION_RTSP_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_RTSP_FAST_TIMEOUT].time = ECM_DB_CONNECTION_RTSP_FAST_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_RTSP_FAST_TIMEOUT].tg = ECM_DB_TIMER_GROUPS_CONNECTION_RTSP_FAST_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_RTSP_SLOW_TIMEOUT].time = ECM_DB_CONNECTION_RTSP_SLOW_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_RTSP_SLOW_TIMEOUT].tg = ECM_DB_TIMER_GROUPS_CONNECTION_RTSP_SLOW_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_DNS_TIMEOUT].time = ECM_DB_CONNECTION_DNS_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_DNS_TIMEOUT].tg = ECM_DB_TIMER_GROUPS_CONNECTION_DNS_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_FTP_TIMEOUT].time = ECM_DB_CONNECTION_FTP_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_FTP_TIMEOUT].tg = ECM_DB_TIMER_GROUPS_CONNECTION_FTP_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_BITTORRENT_TIMEOUT].time = ECM_DB_CONNECTION_BITTORRENT_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_BITTORRENT_TIMEOUT].tg = ECM_DB_TIMER_GROUPS_CONNECTION_BITTORRENT_TIMEOUT;
/*
* H323 timeout value is 8 hours (8h * 60m * 60s == 28800 seconds).
*/
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_H323_TIMEOUT].time = ECM_DB_CONNECTION_H323_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_H323_TIMEOUT].tg = ECM_DB_TIMER_GROUPS_CONNECTION_H323_TIMEOUT;
/*
* IKE Timeout (seconds) = 15 hours
*/
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_IKE_TIMEOUT].time = ECM_DB_CONNECTION_IKE_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_IKE_TIMEOUT].tg = ECM_DB_TIMER_GROUPS_CONNECTION_IKE_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_ESP_TIMEOUT].time = ECM_DB_CONNECTION_ESP_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_ESP_TIMEOUT].tg = ECM_DB_TIMER_GROUPS_CONNECTION_ESP_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_ESP_PENDING_TIMEOUT].time = ECM_DB_CONNECTION_ESP_PENDING_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_ESP_PENDING_TIMEOUT].tg = ECM_DB_TIMER_GROUPS_CONNECTION_ESP_PENDING_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_SDP_TIMEOUT].time = ECM_DB_CONNECTION_SDP_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_SDP_TIMEOUT].tg = ECM_DB_TIMER_GROUPS_CONNECTION_SDP_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_SIP_TIMEOUT].time = ECM_DB_CONNECTION_SIP_TIMEOUT;
ecm_db_timer_groups[ECM_DB_TIMER_GROUPS_CONNECTION_SIP_TIMEOUT].tg = ECM_DB_TIMER_GROUPS_CONNECTION_SIP_TIMEOUT;
/*
* Reset connection by protocol counters
*/
memset(ecm_db_connection_count_by_protocol, 0, sizeof(ecm_db_connection_count_by_protocol));
return 0;
task_cleanup_2:
sysdev_unregister(&ecm_db_sys_dev);
task_cleanup_1:
sysdev_class_unregister(&ecm_db_sysclass);
return result;
}
EXPORT_SYMBOL(ecm_db_init);
/*
* ecm_db_exit()
*/
void ecm_db_exit(void)
{
DEBUG_INFO("ECM DB Module exit\n");
spin_lock_bh(&ecm_db_lock);
ecm_db_terminate_pending = true;
spin_unlock_bh(&ecm_db_lock);
ecm_db_connection_defunct_all();
/*
* Destroy garbage timer
* Timer must be cancelled outside of holding db lock - if the
* timer callback runs on another CPU we would deadlock
* as we would wait for the callback to finish and it would wait
* indefinately for the lock to be released!
*/
del_timer_sync(&ecm_db_timer);
unregister_chrdev(ecm_db_dev_major_id, ecm_db_sysclass.name);
sysdev_unregister(&ecm_db_sys_dev);
sysdev_class_unregister(&ecm_db_sysclass);
}
EXPORT_SYMBOL(ecm_db_exit);