/* SPDX-License-Identifier: BSD-3-Clause * Copyright(c) 2010-2018 Intel Corporation */ #include #include #include #include #include #include #include #ifdef RTE_LIBRTE_KNI #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #ifdef RTE_LIBRTE_KNI #include "kni.h" #endif #include "link.h" #include "mempool.h" #include "pipeline.h" #include "tap.h" #include "tmgr.h" #include "swq.h" #include "cryptodev.h" #ifndef PIPELINE_MSGQ_SIZE #define PIPELINE_MSGQ_SIZE 64 #endif #ifndef TABLE_LPM_NUMBER_TBL8 #define TABLE_LPM_NUMBER_TBL8 256 #endif static struct pipeline_list pipeline_list; int pipeline_init(void) { TAILQ_INIT(&pipeline_list); return 0; } struct pipeline * pipeline_find(const char *name) { struct pipeline *pipeline; if (name == NULL) return NULL; TAILQ_FOREACH(pipeline, &pipeline_list, node) if (strcmp(name, pipeline->name) == 0) return pipeline; return NULL; } struct pipeline * pipeline_create(const char *name, struct pipeline_params *params) { char msgq_name[NAME_MAX]; struct rte_pipeline_params pp; struct pipeline *pipeline; struct rte_pipeline *p; struct rte_ring *msgq_req; struct rte_ring *msgq_rsp; /* Check input params */ if ((name == NULL) || pipeline_find(name) || (params == NULL) || (params->timer_period_ms == 0)) return NULL; /* Resource create */ snprintf(msgq_name, sizeof(msgq_name), "%s-MSGQ-REQ", name); msgq_req = rte_ring_create(msgq_name, PIPELINE_MSGQ_SIZE, params->cpu_id, RING_F_SP_ENQ | RING_F_SC_DEQ); if (msgq_req == NULL) return NULL; snprintf(msgq_name, sizeof(msgq_name), "%s-MSGQ-RSP", name); msgq_rsp = rte_ring_create(msgq_name, PIPELINE_MSGQ_SIZE, params->cpu_id, RING_F_SP_ENQ | RING_F_SC_DEQ); if (msgq_rsp == NULL) { rte_ring_free(msgq_req); return NULL; } pp.name = name; pp.socket_id = (int) params->cpu_id; pp.offset_port_id = params->offset_port_id; p = rte_pipeline_create(&pp); if (p == NULL) { rte_ring_free(msgq_rsp); rte_ring_free(msgq_req); return NULL; } /* Node allocation */ pipeline = calloc(1, sizeof(struct pipeline)); if (pipeline == NULL) { rte_pipeline_free(p); rte_ring_free(msgq_rsp); rte_ring_free(msgq_req); return NULL; } /* Node fill in */ strlcpy(pipeline->name, name, sizeof(pipeline->name)); pipeline->p = p; pipeline->n_ports_in = 0; pipeline->n_ports_out = 0; pipeline->n_tables = 0; pipeline->msgq_req = msgq_req; pipeline->msgq_rsp = msgq_rsp; pipeline->timer_period_ms = params->timer_period_ms; pipeline->enabled = 0; pipeline->cpu_id = params->cpu_id; /* Node add to list */ TAILQ_INSERT_TAIL(&pipeline_list, pipeline, node); return pipeline; } int pipeline_port_in_create(const char *pipeline_name, struct port_in_params *params, int enabled) { struct rte_pipeline_port_in_params p; union { struct rte_port_ethdev_reader_params ethdev; struct rte_port_ring_reader_params ring; struct rte_port_sched_reader_params sched; struct rte_port_fd_reader_params fd; #ifdef RTE_LIBRTE_KNI struct rte_port_kni_reader_params kni; #endif struct rte_port_source_params source; struct rte_port_sym_crypto_reader_params sym_crypto; } pp; struct pipeline *pipeline; struct port_in *port_in; struct port_in_action_profile *ap; struct rte_port_in_action *action; uint32_t port_id; int status; memset(&p, 0, sizeof(p)); memset(&pp, 0, sizeof(pp)); /* Check input params */ if ((pipeline_name == NULL) || (params == NULL) || (params->burst_size == 0) || (params->burst_size > RTE_PORT_IN_BURST_SIZE_MAX)) return -1; pipeline = pipeline_find(pipeline_name); if (pipeline == NULL) return -1; ap = NULL; if (params->action_profile_name) { ap = port_in_action_profile_find(params->action_profile_name); if (ap == NULL) return -1; } switch (params->type) { case PORT_IN_RXQ: { struct link *link; link = link_find(params->dev_name); if (link == NULL) return -1; if (params->rxq.queue_id >= link->n_rxq) return -1; pp.ethdev.port_id = link->port_id; pp.ethdev.queue_id = params->rxq.queue_id; p.ops = &rte_port_ethdev_reader_ops; p.arg_create = &pp.ethdev; break; } case PORT_IN_SWQ: { struct swq *swq; swq = swq_find(params->dev_name); if (swq == NULL) return -1; pp.ring.ring = swq->r; p.ops = &rte_port_ring_reader_ops; p.arg_create = &pp.ring; break; } case PORT_IN_TMGR: { struct tmgr_port *tmgr_port; tmgr_port = tmgr_port_find(params->dev_name); if (tmgr_port == NULL) return -1; pp.sched.sched = tmgr_port->s; p.ops = &rte_port_sched_reader_ops; p.arg_create = &pp.sched; break; } case PORT_IN_TAP: { struct tap *tap; struct mempool *mempool; tap = tap_find(params->dev_name); mempool = mempool_find(params->tap.mempool_name); if ((tap == NULL) || (mempool == NULL)) return -1; pp.fd.fd = tap->fd; pp.fd.mempool = mempool->m; pp.fd.mtu = params->tap.mtu; p.ops = &rte_port_fd_reader_ops; p.arg_create = &pp.fd; break; } #ifdef RTE_LIBRTE_KNI case PORT_IN_KNI: { struct kni *kni; kni = kni_find(params->dev_name); if (kni == NULL) return -1; pp.kni.kni = kni->k; p.ops = &rte_port_kni_reader_ops; p.arg_create = &pp.kni; break; } #endif case PORT_IN_SOURCE: { struct mempool *mempool; mempool = mempool_find(params->source.mempool_name); if (mempool == NULL) return -1; pp.source.mempool = mempool->m; pp.source.file_name = params->source.file_name; pp.source.n_bytes_per_pkt = params->source.n_bytes_per_pkt; p.ops = &rte_port_source_ops; p.arg_create = &pp.source; break; } case PORT_IN_CRYPTODEV: { struct cryptodev *cryptodev; cryptodev = cryptodev_find(params->dev_name); if (cryptodev == NULL) return -1; if (params->rxq.queue_id > cryptodev->n_queues - 1) return -1; pp.sym_crypto.cryptodev_id = cryptodev->dev_id; pp.sym_crypto.queue_id = params->cryptodev.queue_id; pp.sym_crypto.f_callback = params->cryptodev.f_callback; pp.sym_crypto.arg_callback = params->cryptodev.arg_callback; p.ops = &rte_port_sym_crypto_reader_ops; p.arg_create = &pp.sym_crypto; break; } default: return -1; } p.burst_size = params->burst_size; /* Resource create */ action = NULL; p.f_action = NULL; p.arg_ah = NULL; if (ap) { action = rte_port_in_action_create(ap->ap, pipeline->cpu_id); if (action == NULL) return -1; status = rte_port_in_action_params_get( action, &p); if (status) { rte_port_in_action_free(action); return -1; } } status = rte_pipeline_port_in_create(pipeline->p, &p, &port_id); if (status) { rte_port_in_action_free(action); return -1; } if (enabled) rte_pipeline_port_in_enable(pipeline->p, port_id); /* Pipeline */ port_in = &pipeline->port_in[pipeline->n_ports_in]; memcpy(&port_in->params, params, sizeof(*params)); port_in->ap = ap; port_in->a = action; pipeline->n_ports_in++; return 0; } int pipeline_port_in_connect_to_table(const char *pipeline_name, uint32_t port_id, uint32_t table_id) { struct pipeline *pipeline; int status; /* Check input params */ if (pipeline_name == NULL) return -1; pipeline = pipeline_find(pipeline_name); if ((pipeline == NULL) || (port_id >= pipeline->n_ports_in) || (table_id >= pipeline->n_tables)) return -1; /* Resource */ status = rte_pipeline_port_in_connect_to_table(pipeline->p, port_id, table_id); return status; } int pipeline_port_out_create(const char *pipeline_name, struct port_out_params *params) { struct rte_pipeline_port_out_params p; union { struct rte_port_ethdev_writer_params ethdev; struct rte_port_ring_writer_params ring; struct rte_port_sched_writer_params sched; struct rte_port_fd_writer_params fd; #ifdef RTE_LIBRTE_KNI struct rte_port_kni_writer_params kni; #endif struct rte_port_sink_params sink; struct rte_port_sym_crypto_writer_params sym_crypto; } pp; union { struct rte_port_ethdev_writer_nodrop_params ethdev; struct rte_port_ring_writer_nodrop_params ring; struct rte_port_fd_writer_nodrop_params fd; #ifdef RTE_LIBRTE_KNI struct rte_port_kni_writer_nodrop_params kni; #endif struct rte_port_sym_crypto_writer_nodrop_params sym_crypto; } pp_nodrop; struct pipeline *pipeline; uint32_t port_id; int status; memset(&p, 0, sizeof(p)); memset(&pp, 0, sizeof(pp)); memset(&pp_nodrop, 0, sizeof(pp_nodrop)); /* Check input params */ if ((pipeline_name == NULL) || (params == NULL) || (params->burst_size == 0) || (params->burst_size > RTE_PORT_IN_BURST_SIZE_MAX)) return -1; pipeline = pipeline_find(pipeline_name); if (pipeline == NULL) return -1; switch (params->type) { case PORT_OUT_TXQ: { struct link *link; link = link_find(params->dev_name); if (link == NULL) return -1; if (params->txq.queue_id >= link->n_txq) return -1; pp.ethdev.port_id = link->port_id; pp.ethdev.queue_id = params->txq.queue_id; pp.ethdev.tx_burst_sz = params->burst_size; pp_nodrop.ethdev.port_id = link->port_id; pp_nodrop.ethdev.queue_id = params->txq.queue_id; pp_nodrop.ethdev.tx_burst_sz = params->burst_size; pp_nodrop.ethdev.n_retries = params->n_retries; if (params->retry == 0) { p.ops = &rte_port_ethdev_writer_ops; p.arg_create = &pp.ethdev; } else { p.ops = &rte_port_ethdev_writer_nodrop_ops; p.arg_create = &pp_nodrop.ethdev; } break; } case PORT_OUT_SWQ: { struct swq *swq; swq = swq_find(params->dev_name); if (swq == NULL) return -1; pp.ring.ring = swq->r; pp.ring.tx_burst_sz = params->burst_size; pp_nodrop.ring.ring = swq->r; pp_nodrop.ring.tx_burst_sz = params->burst_size; pp_nodrop.ring.n_retries = params->n_retries; if (params->retry == 0) { p.ops = &rte_port_ring_writer_ops; p.arg_create = &pp.ring; } else { p.ops = &rte_port_ring_writer_nodrop_ops; p.arg_create = &pp_nodrop.ring; } break; } case PORT_OUT_TMGR: { struct tmgr_port *tmgr_port; tmgr_port = tmgr_port_find(params->dev_name); if (tmgr_port == NULL) return -1; pp.sched.sched = tmgr_port->s; pp.sched.tx_burst_sz = params->burst_size; p.ops = &rte_port_sched_writer_ops; p.arg_create = &pp.sched; break; } case PORT_OUT_TAP: { struct tap *tap; tap = tap_find(params->dev_name); if (tap == NULL) return -1; pp.fd.fd = tap->fd; pp.fd.tx_burst_sz = params->burst_size; pp_nodrop.fd.fd = tap->fd; pp_nodrop.fd.tx_burst_sz = params->burst_size; pp_nodrop.fd.n_retries = params->n_retries; if (params->retry == 0) { p.ops = &rte_port_fd_writer_ops; p.arg_create = &pp.fd; } else { p.ops = &rte_port_fd_writer_nodrop_ops; p.arg_create = &pp_nodrop.fd; } break; } #ifdef RTE_LIBRTE_KNI case PORT_OUT_KNI: { struct kni *kni; kni = kni_find(params->dev_name); if (kni == NULL) return -1; pp.kni.kni = kni->k; pp.kni.tx_burst_sz = params->burst_size; pp_nodrop.kni.kni = kni->k; pp_nodrop.kni.tx_burst_sz = params->burst_size; pp_nodrop.kni.n_retries = params->n_retries; if (params->retry == 0) { p.ops = &rte_port_kni_writer_ops; p.arg_create = &pp.kni; } else { p.ops = &rte_port_kni_writer_nodrop_ops; p.arg_create = &pp_nodrop.kni; } break; } #endif case PORT_OUT_SINK: { pp.sink.file_name = params->sink.file_name; pp.sink.max_n_pkts = params->sink.max_n_pkts; p.ops = &rte_port_sink_ops; p.arg_create = &pp.sink; break; } case PORT_OUT_CRYPTODEV: { struct cryptodev *cryptodev; cryptodev = cryptodev_find(params->dev_name); if (cryptodev == NULL) return -1; if (params->cryptodev.queue_id >= cryptodev->n_queues) return -1; pp.sym_crypto.cryptodev_id = cryptodev->dev_id; pp.sym_crypto.queue_id = params->cryptodev.queue_id; pp.sym_crypto.tx_burst_sz = params->burst_size; pp.sym_crypto.crypto_op_offset = params->cryptodev.op_offset; pp_nodrop.sym_crypto.cryptodev_id = cryptodev->dev_id; pp_nodrop.sym_crypto.queue_id = params->cryptodev.queue_id; pp_nodrop.sym_crypto.tx_burst_sz = params->burst_size; pp_nodrop.sym_crypto.n_retries = params->retry; pp_nodrop.sym_crypto.crypto_op_offset = params->cryptodev.op_offset; if (params->retry == 0) { p.ops = &rte_port_sym_crypto_writer_ops; p.arg_create = &pp.sym_crypto; } else { p.ops = &rte_port_sym_crypto_writer_nodrop_ops; p.arg_create = &pp_nodrop.sym_crypto; } break; } default: return -1; } p.f_action = NULL; p.arg_ah = NULL; /* Resource create */ status = rte_pipeline_port_out_create(pipeline->p, &p, &port_id); if (status) return -1; /* Pipeline */ pipeline->n_ports_out++; return 0; } static const struct rte_acl_field_def table_acl_field_format_ipv4[] = { /* Protocol */ [0] = { .type = RTE_ACL_FIELD_TYPE_BITMASK, .size = sizeof(uint8_t), .field_index = 0, .input_index = 0, .offset = offsetof(struct rte_ipv4_hdr, next_proto_id), }, /* Source IP address (IPv4) */ [1] = { .type = RTE_ACL_FIELD_TYPE_MASK, .size = sizeof(uint32_t), .field_index = 1, .input_index = 1, .offset = offsetof(struct rte_ipv4_hdr, src_addr), }, /* Destination IP address (IPv4) */ [2] = { .type = RTE_ACL_FIELD_TYPE_MASK, .size = sizeof(uint32_t), .field_index = 2, .input_index = 2, .offset = offsetof(struct rte_ipv4_hdr, dst_addr), }, /* Source Port */ [3] = { .type = RTE_ACL_FIELD_TYPE_RANGE, .size = sizeof(uint16_t), .field_index = 3, .input_index = 3, .offset = sizeof(struct rte_ipv4_hdr) + offsetof(struct rte_tcp_hdr, src_port), }, /* Destination Port */ [4] = { .type = RTE_ACL_FIELD_TYPE_RANGE, .size = sizeof(uint16_t), .field_index = 4, .input_index = 3, .offset = sizeof(struct rte_ipv4_hdr) + offsetof(struct rte_tcp_hdr, dst_port), }, }; static const struct rte_acl_field_def table_acl_field_format_ipv6[] = { /* Protocol */ [0] = { .type = RTE_ACL_FIELD_TYPE_BITMASK, .size = sizeof(uint8_t), .field_index = 0, .input_index = 0, .offset = offsetof(struct rte_ipv6_hdr, proto), }, /* Source IP address (IPv6) */ [1] = { .type = RTE_ACL_FIELD_TYPE_MASK, .size = sizeof(uint32_t), .field_index = 1, .input_index = 1, .offset = offsetof(struct rte_ipv6_hdr, src_addr[0]), }, [2] = { .type = RTE_ACL_FIELD_TYPE_MASK, .size = sizeof(uint32_t), .field_index = 2, .input_index = 2, .offset = offsetof(struct rte_ipv6_hdr, src_addr[4]), }, [3] = { .type = RTE_ACL_FIELD_TYPE_MASK, .size = sizeof(uint32_t), .field_index = 3, .input_index = 3, .offset = offsetof(struct rte_ipv6_hdr, src_addr[8]), }, [4] = { .type = RTE_ACL_FIELD_TYPE_MASK, .size = sizeof(uint32_t), .field_index = 4, .input_index = 4, .offset = offsetof(struct rte_ipv6_hdr, src_addr[12]), }, /* Destination IP address (IPv6) */ [5] = { .type = RTE_ACL_FIELD_TYPE_MASK, .size = sizeof(uint32_t), .field_index = 5, .input_index = 5, .offset = offsetof(struct rte_ipv6_hdr, dst_addr[0]), }, [6] = { .type = RTE_ACL_FIELD_TYPE_MASK, .size = sizeof(uint32_t), .field_index = 6, .input_index = 6, .offset = offsetof(struct rte_ipv6_hdr, dst_addr[4]), }, [7] = { .type = RTE_ACL_FIELD_TYPE_MASK, .size = sizeof(uint32_t), .field_index = 7, .input_index = 7, .offset = offsetof(struct rte_ipv6_hdr, dst_addr[8]), }, [8] = { .type = RTE_ACL_FIELD_TYPE_MASK, .size = sizeof(uint32_t), .field_index = 8, .input_index = 8, .offset = offsetof(struct rte_ipv6_hdr, dst_addr[12]), }, /* Source Port */ [9] = { .type = RTE_ACL_FIELD_TYPE_RANGE, .size = sizeof(uint16_t), .field_index = 9, .input_index = 9, .offset = sizeof(struct rte_ipv6_hdr) + offsetof(struct rte_tcp_hdr, src_port), }, /* Destination Port */ [10] = { .type = RTE_ACL_FIELD_TYPE_RANGE, .size = sizeof(uint16_t), .field_index = 10, .input_index = 9, .offset = sizeof(struct rte_ipv6_hdr) + offsetof(struct rte_tcp_hdr, dst_port), }, }; int pipeline_table_create(const char *pipeline_name, struct table_params *params) { char name[NAME_MAX]; struct rte_pipeline_table_params p; union { struct rte_table_acl_params acl; struct rte_table_array_params array; struct rte_table_hash_params hash; struct rte_table_lpm_params lpm; struct rte_table_lpm_ipv6_params lpm_ipv6; } pp; struct pipeline *pipeline; struct table *table; struct table_action_profile *ap; struct rte_table_action *action; uint32_t table_id; int status; memset(&p, 0, sizeof(p)); memset(&pp, 0, sizeof(pp)); /* Check input params */ if ((pipeline_name == NULL) || (params == NULL)) return -1; pipeline = pipeline_find(pipeline_name); if ((pipeline == NULL) || (pipeline->n_tables >= RTE_PIPELINE_TABLE_MAX)) return -1; ap = NULL; if (params->action_profile_name) { ap = table_action_profile_find(params->action_profile_name); if (ap == NULL) return -1; } snprintf(name, NAME_MAX, "%s_table%u", pipeline_name, pipeline->n_tables); switch (params->match_type) { case TABLE_ACL: { uint32_t ip_header_offset = params->match.acl.ip_header_offset - (sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM); uint32_t i; if (params->match.acl.n_rules == 0) return -1; pp.acl.name = name; pp.acl.n_rules = params->match.acl.n_rules; if (params->match.acl.ip_version) { memcpy(&pp.acl.field_format, &table_acl_field_format_ipv4, sizeof(table_acl_field_format_ipv4)); pp.acl.n_rule_fields = RTE_DIM(table_acl_field_format_ipv4); } else { memcpy(&pp.acl.field_format, &table_acl_field_format_ipv6, sizeof(table_acl_field_format_ipv6)); pp.acl.n_rule_fields = RTE_DIM(table_acl_field_format_ipv6); } for (i = 0; i < pp.acl.n_rule_fields; i++) pp.acl.field_format[i].offset += ip_header_offset; p.ops = &rte_table_acl_ops; p.arg_create = &pp.acl; break; } case TABLE_ARRAY: { if (params->match.array.n_keys == 0) return -1; pp.array.n_entries = params->match.array.n_keys; pp.array.offset = params->match.array.key_offset; p.ops = &rte_table_array_ops; p.arg_create = &pp.array; break; } case TABLE_HASH: { struct rte_table_ops *ops; rte_table_hash_op_hash f_hash; if (params->match.hash.n_keys == 0) return -1; switch (params->match.hash.key_size) { case 8: f_hash = rte_table_hash_crc_key8; break; case 16: f_hash = rte_table_hash_crc_key16; break; case 24: f_hash = rte_table_hash_crc_key24; break; case 32: f_hash = rte_table_hash_crc_key32; break; case 40: f_hash = rte_table_hash_crc_key40; break; case 48: f_hash = rte_table_hash_crc_key48; break; case 56: f_hash = rte_table_hash_crc_key56; break; case 64: f_hash = rte_table_hash_crc_key64; break; default: return -1; } pp.hash.name = name; pp.hash.key_size = params->match.hash.key_size; pp.hash.key_offset = params->match.hash.key_offset; pp.hash.key_mask = params->match.hash.key_mask; pp.hash.n_keys = params->match.hash.n_keys; pp.hash.n_buckets = params->match.hash.n_buckets; pp.hash.f_hash = f_hash; pp.hash.seed = 0; if (params->match.hash.extendable_bucket) switch (params->match.hash.key_size) { case 8: ops = &rte_table_hash_key8_ext_ops; break; case 16: ops = &rte_table_hash_key16_ext_ops; break; default: ops = &rte_table_hash_ext_ops; } else switch (params->match.hash.key_size) { case 8: ops = &rte_table_hash_key8_lru_ops; break; case 16: ops = &rte_table_hash_key16_lru_ops; break; default: ops = &rte_table_hash_lru_ops; } p.ops = ops; p.arg_create = &pp.hash; break; } case TABLE_LPM: { if (params->match.lpm.n_rules == 0) return -1; switch (params->match.lpm.key_size) { case 4: { pp.lpm.name = name; pp.lpm.n_rules = params->match.lpm.n_rules; pp.lpm.number_tbl8s = TABLE_LPM_NUMBER_TBL8; pp.lpm.flags = 0; pp.lpm.entry_unique_size = p.action_data_size + sizeof(struct rte_pipeline_table_entry); pp.lpm.offset = params->match.lpm.key_offset; p.ops = &rte_table_lpm_ops; p.arg_create = &pp.lpm; break; } case 16: { pp.lpm_ipv6.name = name; pp.lpm_ipv6.n_rules = params->match.lpm.n_rules; pp.lpm_ipv6.number_tbl8s = TABLE_LPM_NUMBER_TBL8; pp.lpm_ipv6.entry_unique_size = p.action_data_size + sizeof(struct rte_pipeline_table_entry); pp.lpm_ipv6.offset = params->match.lpm.key_offset; p.ops = &rte_table_lpm_ipv6_ops; p.arg_create = &pp.lpm_ipv6; break; } default: return -1; } break; } case TABLE_STUB: { p.ops = &rte_table_stub_ops; p.arg_create = NULL; break; } default: return -1; } /* Resource create */ action = NULL; p.f_action_hit = NULL; p.f_action_miss = NULL; p.arg_ah = NULL; if (ap) { action = rte_table_action_create(ap->ap, pipeline->cpu_id); if (action == NULL) return -1; status = rte_table_action_table_params_get( action, &p); if (status || ((p.action_data_size + sizeof(struct rte_pipeline_table_entry)) > TABLE_RULE_ACTION_SIZE_MAX)) { rte_table_action_free(action); return -1; } } if (params->match_type == TABLE_LPM) { if (params->match.lpm.key_size == 4) pp.lpm.entry_unique_size = p.action_data_size + sizeof(struct rte_pipeline_table_entry); if (params->match.lpm.key_size == 16) pp.lpm_ipv6.entry_unique_size = p.action_data_size + sizeof(struct rte_pipeline_table_entry); } status = rte_pipeline_table_create(pipeline->p, &p, &table_id); if (status) { rte_table_action_free(action); return -1; } /* Pipeline */ table = &pipeline->table[pipeline->n_tables]; memcpy(&table->params, params, sizeof(*params)); table->ap = ap; table->a = action; TAILQ_INIT(&table->rules); table->rule_default = NULL; pipeline->n_tables++; return 0; } struct table_rule * table_rule_find(struct table *table, struct table_rule_match *match) { struct table_rule *rule; TAILQ_FOREACH(rule, &table->rules, node) if (memcmp(&rule->match, match, sizeof(*match)) == 0) return rule; return NULL; } void table_rule_add(struct table *table, struct table_rule *new_rule) { struct table_rule *existing_rule; existing_rule = table_rule_find(table, &new_rule->match); if (existing_rule == NULL) TAILQ_INSERT_TAIL(&table->rules, new_rule, node); else { TAILQ_INSERT_AFTER(&table->rules, existing_rule, new_rule, node); TAILQ_REMOVE(&table->rules, existing_rule, node); free(existing_rule); } } void table_rule_add_bulk(struct table *table, struct table_rule_list *list, uint32_t n_rules) { uint32_t i; for (i = 0; i < n_rules; i++) { struct table_rule *existing_rule, *new_rule; new_rule = TAILQ_FIRST(list); if (new_rule == NULL) break; TAILQ_REMOVE(list, new_rule, node); existing_rule = table_rule_find(table, &new_rule->match); if (existing_rule == NULL) TAILQ_INSERT_TAIL(&table->rules, new_rule, node); else { TAILQ_INSERT_AFTER(&table->rules, existing_rule, new_rule, node); TAILQ_REMOVE(&table->rules, existing_rule, node); free(existing_rule); } } } void table_rule_delete(struct table *table, struct table_rule_match *match) { struct table_rule *rule; rule = table_rule_find(table, match); if (rule == NULL) return; TAILQ_REMOVE(&table->rules, rule, node); free(rule); } void table_rule_default_add(struct table *table, struct table_rule *rule) { free(table->rule_default); table->rule_default = rule; } void table_rule_default_delete(struct table *table) { free(table->rule_default); table->rule_default = NULL; }