blob: 50453e6731c9c2c1d28f5b0d875205f84446efc1 [file] [log] [blame]
/*
**************************************************************************
* Copyright (c) 2014-2021, The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
**************************************************************************
*/
#include <net/netfilter/nf_conntrack.h>
#ifdef ECM_INTERFACE_OVS_BRIDGE_ENABLE
#include <ovsmgr.h>
#endif
/*
* Magic number
*/
#define ECM_DB_CONNECTION_INSTANCE_MAGIC 0xff23
typedef uint32_t ecm_db_connection_hash_t;
typedef uint32_t ecm_db_connection_serial_hash_t;
#ifdef ECM_DB_CTA_TRACK_ENABLE
/*
* struct ecm_db_connection_classifier_type_assignment
* List linkage
*/
struct ecm_db_connection_classifier_type_assignment {
struct ecm_db_connection_instance *next; /* Next connection assigned to a classifier of this type */
struct ecm_db_connection_instance *prev; /* Previous connection assigned to a classifier of this type */
int iteration_count; /* >0 if something is examining this list entry and it may not be unlinked. The connection will persist. */
bool pending_unassign; /* True when the connection has been unassigned from the type, when iteration_count drops to 0 it may be removed from the list */
#if (DEBUG_LEVEL > 0)
uint16_t magic;
#endif
};
#endif
/*
* 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 ip_version; /* RO: The version of IP protocol this connection was established for */
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 */
bool timer_no_touch; /* RO: Do no update timer when this flag is set */
uint16_t l2_encap_proto; /* L2 encap protocol of the flow of this connection */
uint32_t mark; /* The result value of mark classifier on this connection */
/*
* Connection endpoint mapping
* NOTE: For non-NAT connections mapping[ECM_DB_OBJ_DIR_FROM_NAT] and mapping[ECM_DB_OBJ_DIR_TO_NAT]
* would be identical to the endpoint mappings.
*/
struct ecm_db_mapping_instance *mapping[ECM_DB_OBJ_DIR_MAX]; /* The connection was established on this mapping in the specified direction */
/*
* From / To Node (NAT and non-NAT).
* Connections keep references to the nodes upon which they operate.
* Gut feeling would tell us this is unusual since it should be the case that
* the HOST refer to the node, e.g. IP address to a MAC address.
* However there are some 'interesting' usage models where the same IP address may appear
* from different nodes / MAC addresses because of this the unique element here is the connection
* and so we record the node information directly here.
*/
struct ecm_db_node_instance *node[ECM_DB_OBJ_DIR_MAX]; /* Node which this connection was established in the specified direction */
#ifdef ECM_DB_XREF_ENABLE
/*
* The connection has references to the mappings (both nat and non-nat) as required above.
* Also mappings keep lists of connections made to/from them so that they may be iterated
* to determine associated connections in each direction/situation (e.g. "defuncting all connections made to/from a mapping").
*/
struct ecm_db_connection_instance *mapping_next[ECM_DB_OBJ_DIR_MAX]; /* Next connection made on the same mapping in the specified direction */
struct ecm_db_connection_instance *mapping_prev[ECM_DB_OBJ_DIR_MAX]; /* Previous connection made on the same mapping in the specified direction */
/*
* Connection endpoint interface
* NOTE: For non-NAT connections from/to would be identical to the endpoint interface.
* GGG TODO Deprecated - use interface lists instead.
* To be removed when interface heirarchies are implemented to provide the same functionality.
*/
struct ecm_db_connection_instance *iface_next[ECM_DB_OBJ_DIR_MAX]; /* Next connection made on the same interface with the specified direction*/
struct ecm_db_connection_instance *iface_prev[ECM_DB_OBJ_DIR_MAX]; /* Previous connection made on the same interface with the specified direction */
/*
* As well as keeping a reference to the node which this connection uses the nodes
* also keep lists of connections made from/to them.
*/
struct ecm_db_connection_instance *node_next[ECM_DB_OBJ_DIR_MAX]; /* Next connection in the nodes specified direction list */
struct ecm_db_connection_instance *node_prev[ECM_DB_OBJ_DIR_MAX]; /* Prev connection in the nodes specified direction list */
#endif
/*
* From / To interfaces list
* From NAT / To NAT interfaces list
* GGG TODO Not sure if NAT interface lists are necessary or appropriate or practical.
* Needs to be assessed if it gives any clear benefit and possibly remove these if not.
*/
struct ecm_db_iface_instance *interfaces[ECM_DB_OBJ_DIR_MAX][ECM_DB_IFACE_HEIRARCHY_MAX];
/* The outermost to innnermost interface this connection is using in the specified direction
* which is defined in the first dimension of the array.
* Relationships are recorded from [ECM_DB_IFACE_HEIRARCHY_MAX - 1] to [0]
*/
int32_t interface_first[ECM_DB_OBJ_DIR_MAX]; /* The index of the first interface in the list */
bool interface_set[ECM_DB_OBJ_DIR_MAX]; /* True when a list has been set - even if there is NO list, it's still deliberately set that way. */
#ifdef ECM_MULTICAST_ENABLE
/*
* Destination Multicast interfaces list
*/
struct ecm_db_iface_instance *to_mcast_interfaces;
/* The outermost to innnermost interfaces this connection is using in multicast path.
* The size of the buffer allocated for the to_mcast_interfaces heirarchies is as large as
* sizeof(struct ecm_db_iface_instance *) * ECM_DB_MULTICAST_IF_MAX * ECM_DB_IFACE_HEIRARCHY_MAX. */
int32_t to_mcast_interface_first[ECM_DB_MULTICAST_IF_MAX];
/* The indexes of the first interfaces in the destinaiton interface list */
struct ecm_db_multicast_tuple_instance *ti; /* Multicast Connection instance */
bool to_mcast_interfaces_set; /* Flag to indicate if the destination interface list is currently empty or not */
#endif
/*
* 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_instance *assignments; /* A list of all classifiers that are still assigned to this connection.
* When a connection is created, one instance of every type of classifier is 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.
*/
#ifdef ECM_DB_CTA_TRACK_ENABLE
struct ecm_db_connection_classifier_type_assignment type_assignment[ECM_CLASSIFIER_TYPES];
/*
* Each classifier TYPE has a list of connections that are assigned to it.
* This permits a classifier TYPE to rapidly retrieve all connections associated with it.
*/
#endif
/*
* Re-generation.
* When system or classifier state changes, affected connections may need to have their state re-generated.
* This ensures that a connection does not continue to operate on stale state which could affect the sanity of acceleration rules.
* A connection needs to be re-generated when its regen_required is > 0.
* When a re-generation is completed successfully the counter is decremented.
* The counter ensures that any further changes of state while re-generation is under way is not missed.
* While a connection needs re-generation (regen_required > 0), acceleration should not be permitted.
* It may not always be practical to flag individual connections for re-generation (time consuming with large numbers of connections).
* The "generation" is a numerical counter comparison against the global "ecm_db_connection_generation".
* This ecm_db_connection_generation can be incremented causing a numerical difference between the connections counter and this global.
* This is enough to flag that a re-generation is needed.
* Further, it is possible that re-generation may be required DURING a rule construction. Since constructing a rule
* can require lengthy non-atomic processes there needs to be a way to ensure that changes during construction of a rule are caught.
* The regen_occurances is a counter that is incremented whenever regen_required is also incremented.
* However it is never decremented. This permits the caller to obtain this count before a non-atomic procedure and then afterwards.
* If there is any change in the counter value there is a change of generation! And the operation should be aborted.
*/
bool regen_in_progress; /* The connection is under regeneration right now and is used to provide atomic re-generation in SMP */
uint16_t regen_required; /* The connection needs to be re-generated when > 0 */
uint16_t regen_occurances; /* Total number of regens required */
uint16_t generation; /* Used to detect when a re-evaluation of this connection is necessary by comparing with ecm_db_connection_generation */
uint32_t regen_success; /* Tracks how many times re-generation was successfully completed */
uint32_t regen_fail; /* Tracks how many times re-generation failed */
struct ecm_front_end_connection_instance *feci; /* Front end instance specific to this connection */
ecm_db_connection_defunct_callback_t defunct; /* Callback to be called when connection has become defunct */
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; /* RO: 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 0x1 /* Connection is inserted into connection database tables */
#define ECM_DB_CONNECTION_FLAGS_PPPOE_BRIDGE 0x2 /* Connection is PPPoE bridge entry */
int _ecm_db_connection_count_get(void);
int ecm_db_connection_count_get(void);
int ecm_db_connection_count_by_protocol_get(int protocol);
struct ecm_front_end_connection_instance *ecm_db_connection_front_end_get_and_ref(struct ecm_db_connection_instance *ci);
int ecm_db_connection_elapsed_defunct_timer(struct ecm_db_connection_instance *ci);
bool ecm_db_connection_defunct_timer_reset(struct ecm_db_connection_instance *ci, ecm_db_timer_group_t tg);
bool ecm_db_connection_defunct_timer_touch(struct ecm_db_connection_instance *ci);
void ecm_db_connection_defunct_timer_no_touch_set(struct ecm_db_connection_instance *ci);
bool ecm_db_connection_defunct_timer_no_touch_get(struct ecm_db_connection_instance *ci);
ecm_db_timer_group_t ecm_db_connection_timer_group_get(struct ecm_db_connection_instance *ci);
void ecm_db_connection_defunct_timer_remove_and_set(struct ecm_db_connection_instance *ci, ecm_db_timer_group_t tg);
void ecm_db_connection_make_defunct(struct ecm_db_connection_instance *ci);
void ecm_db_connection_data_totals_update(struct ecm_db_connection_instance *ci,
bool is_from, uint64_t size, uint64_t packets);
void ecm_db_connection_data_totals_update_dropped(struct ecm_db_connection_instance *ci,
bool is_from, uint64_t size, uint64_t packets);
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);
uint32_t ecm_db_connection_serial_get(struct ecm_db_connection_instance *ci);
void ecm_db_connection_address_get(struct ecm_db_connection_instance *ci, ecm_db_obj_dir_t dir, ip_addr_t addr);
int ecm_db_connection_port_get(struct ecm_db_connection_instance *ci, ecm_db_obj_dir_t dir);
void ecm_db_connection_node_address_get(struct ecm_db_connection_instance *ci, ecm_db_obj_dir_t dir, uint8_t *address_buffer);
void ecm_db_connection_iface_name_get(struct ecm_db_connection_instance *ci, ecm_db_obj_dir_t dir, char *name_buffer);
int ecm_db_connection_iface_mtu_get(struct ecm_db_connection_instance *ci, ecm_db_obj_dir_t dir);
ecm_db_iface_type_t ecm_db_connection_iface_type_get(struct ecm_db_connection_instance *ci, ecm_db_obj_dir_t dir);
uint16_t ecm_db_connection_regeneration_occurrances_get(struct ecm_db_connection_instance *ci);
void ecm_db_connection_regeneration_completed(struct ecm_db_connection_instance *ci);
void ecm_db_connection_regeneration_failed(struct ecm_db_connection_instance *ci);
bool ecm_db_connection_regeneration_required_check(struct ecm_db_connection_instance *ci);
bool ecm_db_connection_regeneration_required_peek(struct ecm_db_connection_instance *ci);
void ecm_db_connection_regeneration_needed(struct ecm_db_connection_instance *ci);
void ecm_db_regeneration_needed(void);
void ecm_db_connection_regenerate(struct ecm_db_connection_instance *ci);
ecm_db_direction_t ecm_db_connection_direction_get(struct ecm_db_connection_instance *ci);
int ecm_db_connection_protocol_get(struct ecm_db_connection_instance *ci);
int ecm_db_connection_ip_version_get(struct ecm_db_connection_instance *ci);
bool ecm_db_connection_is_routed_get(struct ecm_db_connection_instance *ci);
bool ecm_db_connection_is_pppoe_bridged_get(struct ecm_db_connection_instance *ci);
void _ecm_db_connection_ref(struct ecm_db_connection_instance *ci);
void ecm_db_connection_ref(struct ecm_db_connection_instance *ci);
int ecm_db_connection_deref(struct ecm_db_connection_instance *ci);
struct ecm_db_connection_instance *ecm_db_connections_get_and_ref_first(void);
struct ecm_db_connection_instance *ecm_db_connection_get_and_ref_next(struct ecm_db_connection_instance *ci);
void ecm_db_connection_defunct_all(void);
void ecm_db_connection_defunct_by_port(int port, ecm_db_obj_dir_t dir);
void ecm_db_connection_defunct_by_protocol(int protocol);
void ecm_db_connection_defunct_ip_version(int ip_version);
struct ecm_db_connection_instance *ecm_db_connection_serial_find_and_ref(uint32_t serial);
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);
struct ecm_db_node_instance *ecm_db_connection_node_get_and_ref(struct ecm_db_connection_instance *ci, ecm_db_obj_dir_t dir);
struct ecm_db_mapping_instance *ecm_db_connection_mapping_get_and_ref(struct ecm_db_connection_instance *ci, ecm_db_obj_dir_t dir);
struct ecm_db_connection_instance *ecm_db_connection_iface_get_and_ref_next(struct ecm_db_connection_instance *ci, ecm_db_obj_dir_t dir);
struct ecm_db_connection_instance *ecm_db_connection_mapping_get_and_ref_next(struct ecm_db_connection_instance *ci, ecm_db_obj_dir_t dir);
void ecm_db_connection_interfaces_deref(struct ecm_db_iface_instance *interfaces[], int32_t first);
void ecm_db_connection_interfaces_reset(struct ecm_db_connection_instance *ci,
struct ecm_db_iface_instance *interfaces[], int32_t new_first, ecm_db_obj_dir_t dir);
void ecm_db_connection_interfaces_clear(struct ecm_db_connection_instance *ci, ecm_db_obj_dir_t dir);
bool ecm_db_connection_interfaces_set_check(struct ecm_db_connection_instance *ci, ecm_db_obj_dir_t dir);
int32_t ecm_db_connection_interfaces_get_count(struct ecm_db_connection_instance *ci, ecm_db_obj_dir_t dir);
int32_t ecm_db_connection_interfaces_get_and_ref(struct ecm_db_connection_instance *ci,
struct ecm_db_iface_instance *interfaces[],
ecm_db_obj_dir_t dir);
void ecm_db_connection_classifier_assign(struct ecm_db_connection_instance *ci,
struct ecm_classifier_instance *new_ca);
int ecm_db_connection_classifier_assignments_get_and_ref(struct ecm_db_connection_instance *ci,
struct ecm_classifier_instance *assignments[]);
void ecm_db_connection_classifier_unassign(struct ecm_db_connection_instance *ci,
struct ecm_classifier_instance *cci);
void ecm_db_connection_assignments_release(int assignment_count,
struct ecm_classifier_instance *assignments[]);
struct ecm_classifier_instance *
ecm_db_connection_assigned_classifier_find_and_ref(struct ecm_db_connection_instance *ci,
ecm_classifier_type_t type);
#ifdef ECM_DB_CTA_TRACK_ENABLE
struct ecm_db_connection_instance *
ecm_db_connection_by_classifier_type_assignment_get_and_ref_first(ecm_classifier_type_t ca_type);
struct ecm_db_connection_instance *
ecm_db_connection_by_classifier_type_assignment_get_and_ref_next(struct ecm_db_connection_instance *ci,
ecm_classifier_type_t ca_type);
void ecm_db_connection_by_classifier_type_assignment_deref(struct ecm_db_connection_instance *ci,
ecm_classifier_type_t ca_type);
void ecm_db_connection_regenerate_by_assignment_type(ecm_classifier_type_t ca_type);
void ecm_db_connection_make_defunct_by_assignment_type(ecm_classifier_type_t ca_type);
#endif
struct ecm_db_connection_instance *ecm_db_connection_alloc(void);
void ecm_db_connection_add(struct ecm_db_connection_instance *ci,
struct ecm_db_mapping_instance *mapping[],
struct ecm_db_node_instance *node[],
int ip_version, int protocol, ecm_db_direction_t dir,
ecm_db_connection_final_callback_t final,
ecm_db_connection_defunct_callback_t defunct,
ecm_db_timer_group_t tg, bool is_routed, void *arg);
#ifdef ECM_STATE_OUTPUT_ENABLE
int ecm_db_connection_heirarchy_state_get(struct ecm_state_file_instance *sfi,
struct ecm_db_iface_instance *interfaces[],
int32_t first_interface);
int ecm_db_connection_state_get(struct ecm_state_file_instance *sfi,
struct ecm_db_connection_instance *ci);
int ecm_db_connection_hash_table_lengths_get(int index);
int ecm_db_connection_hash_index_get_next(int index);
int ecm_db_connection_hash_index_get_first(void);
int ecm_db_protocol_get_next(int protocol);
int ecm_db_protocol_get_first(void);
#endif
struct ecm_db_connection_instance *ecm_db_connection_ipv4_from_ct_get_and_ref(struct nf_conn *ct);
struct ecm_db_connection_instance *ecm_db_connection_ipv6_from_ct_get_and_ref(struct nf_conn *ct);
#ifdef ECM_INTERFACE_OVS_BRIDGE_ENABLE
struct ecm_db_connection_instance *ecm_db_connection_from_ovs_flow_get_and_ref(struct ovsmgr_dp_flow *flow);
#endif
bool ecm_db_connection_decel_v4(__be32 src_ip, int src_port,
__be32 dest_ip, int dest_port, int protocol);
bool ecm_db_connection_decel_v6(struct in6_addr *src_ip, int src_port,
struct in6_addr *dest_ip, int dest_port, int protocol);
void ecm_db_front_end_instance_ref_and_set(struct ecm_db_connection_instance *ci,
struct ecm_front_end_connection_instance *feci);
void ecm_db_connection_flag_set(struct ecm_db_connection_instance *ci, uint32_t flag);
void ecm_db_connection_l2_encap_proto_set(struct ecm_db_connection_instance *ci, uint16_t l2_encap_proto);
uint16_t ecm_db_connection_l2_encap_proto_get(struct ecm_db_connection_instance *ci);
void ecm_db_connection_mark_set(struct ecm_db_connection_instance *ci, uint32_t mark);
uint32_t ecm_db_connection_mark_get(struct ecm_db_connection_instance *ci);
void ecm_db_connection_defunct_by_classifier(int ip_ver, ip_addr_t src_addr, uint16_t src_port, ip_addr_t dest_addr,
uint16_t dest_port, int proto, bool is_routed, ecm_classifier_type_t ca_type);
bool ecm_db_connection_init(struct dentry *dentry);
void ecm_db_connection_exit(void);