/*- * BSD LICENSE * * Copyright(c) 2010-2014 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name of Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include "test_table.h" #include "test_table_acl.h" #define IPv4(a, b, c, d) ((uint32_t)(((a) & 0xff) << 24) | \ (((b) & 0xff) << 16) | \ (((c) & 0xff) << 8) | \ ((d) & 0xff)) /* * Rule and trace formats definitions. **/ struct ipv4_5tuple { uint8_t proto; uint32_t ip_src; uint32_t ip_dst; uint16_t port_src; uint16_t port_dst; }; enum { PROTO_FIELD_IPV4, SRC_FIELD_IPV4, DST_FIELD_IPV4, SRCP_FIELD_IPV4, DSTP_FIELD_IPV4, NUM_FIELDS_IPV4 }; struct rte_acl_field_def ipv4_defs[NUM_FIELDS_IPV4] = { { .type = RTE_ACL_FIELD_TYPE_BITMASK, .size = sizeof(uint8_t), .field_index = PROTO_FIELD_IPV4, .input_index = PROTO_FIELD_IPV4, .offset = offsetof(struct ipv4_5tuple, proto), }, { .type = RTE_ACL_FIELD_TYPE_MASK, .size = sizeof(uint32_t), .field_index = SRC_FIELD_IPV4, .input_index = SRC_FIELD_IPV4, .offset = offsetof(struct ipv4_5tuple, ip_src), }, { .type = RTE_ACL_FIELD_TYPE_MASK, .size = sizeof(uint32_t), .field_index = DST_FIELD_IPV4, .input_index = DST_FIELD_IPV4, .offset = offsetof(struct ipv4_5tuple, ip_dst), }, { .type = RTE_ACL_FIELD_TYPE_RANGE, .size = sizeof(uint16_t), .field_index = SRCP_FIELD_IPV4, .input_index = SRCP_FIELD_IPV4, .offset = offsetof(struct ipv4_5tuple, port_src), }, { .type = RTE_ACL_FIELD_TYPE_RANGE, .size = sizeof(uint16_t), .field_index = DSTP_FIELD_IPV4, .input_index = SRCP_FIELD_IPV4, .offset = offsetof(struct ipv4_5tuple, port_dst), }, }; struct rte_table_acl_rule_add_params table_acl_IPv4_rule; typedef int (*parse_5tuple)(char *text, struct rte_table_acl_rule_add_params *rule); /* * The order of the fields in the rule string after the initial '@' */ enum { CB_FLD_SRC_ADDR, CB_FLD_DST_ADDR, CB_FLD_SRC_PORT_RANGE, CB_FLD_DST_PORT_RANGE, CB_FLD_PROTO, CB_FLD_NUM, }; #define GET_CB_FIELD(in, fd, base, lim, dlm) \ do { \ unsigned long val; \ char *end; \ \ errno = 0; \ val = strtoul((in), &end, (base)); \ if (errno != 0 || end[0] != (dlm) || val > (lim)) \ return -EINVAL; \ (fd) = (typeof(fd)) val; \ (in) = end + 1; \ } while (0) static int parse_ipv4_net(const char *in, uint32_t *addr, uint32_t *mask_len) { uint8_t a, b, c, d, m; GET_CB_FIELD(in, a, 0, UINT8_MAX, '.'); GET_CB_FIELD(in, b, 0, UINT8_MAX, '.'); GET_CB_FIELD(in, c, 0, UINT8_MAX, '.'); GET_CB_FIELD(in, d, 0, UINT8_MAX, '/'); GET_CB_FIELD(in, m, 0, sizeof(uint32_t) * CHAR_BIT, 0); addr[0] = IPv4(a, b, c, d); mask_len[0] = m; return 0; } static int parse_port_range(const char *in, uint16_t *port_low, uint16_t *port_high) { uint16_t a, b; GET_CB_FIELD(in, a, 0, UINT16_MAX, ':'); GET_CB_FIELD(in, b, 0, UINT16_MAX, 0); port_low[0] = a; port_high[0] = b; return 0; } static int parse_cb_ipv4_rule(char *str, struct rte_table_acl_rule_add_params *v) { int i, rc; char *s, *sp, *in[CB_FLD_NUM]; static const char *dlm = " \t\n"; /* ** Skip leading '@' */ if (strchr(str, '@') != str) return -EINVAL; s = str + 1; /* * Populate the 'in' array with the location of each * field in the string we're parsing */ for (i = 0; i != DIM(in); i++) { in[i] = strtok_r(s, dlm, &sp); if (in[i] == NULL) return -EINVAL; s = NULL; } /* Parse x.x.x.x/x */ rc = parse_ipv4_net(in[CB_FLD_SRC_ADDR], &v->field_value[SRC_FIELD_IPV4].value.u32, &v->field_value[SRC_FIELD_IPV4].mask_range.u32); if (rc != 0) { RTE_LOG(ERR, PIPELINE, "failed to read src address/mask: %s\n", in[CB_FLD_SRC_ADDR]); return rc; } printf("V=%u, mask=%u\n", v->field_value[SRC_FIELD_IPV4].value.u32, v->field_value[SRC_FIELD_IPV4].mask_range.u32); /* Parse x.x.x.x/x */ rc = parse_ipv4_net(in[CB_FLD_DST_ADDR], &v->field_value[DST_FIELD_IPV4].value.u32, &v->field_value[DST_FIELD_IPV4].mask_range.u32); if (rc != 0) { RTE_LOG(ERR, PIPELINE, "failed to read dest address/mask: %s\n", in[CB_FLD_DST_ADDR]); return rc; } printf("V=%u, mask=%u\n", v->field_value[DST_FIELD_IPV4].value.u32, v->field_value[DST_FIELD_IPV4].mask_range.u32); /* Parse n:n */ rc = parse_port_range(in[CB_FLD_SRC_PORT_RANGE], &v->field_value[SRCP_FIELD_IPV4].value.u16, &v->field_value[SRCP_FIELD_IPV4].mask_range.u16); if (rc != 0) { RTE_LOG(ERR, PIPELINE, "failed to read source port range: %s\n", in[CB_FLD_SRC_PORT_RANGE]); return rc; } printf("V=%u, mask=%u\n", v->field_value[SRCP_FIELD_IPV4].value.u16, v->field_value[SRCP_FIELD_IPV4].mask_range.u16); /* Parse n:n */ rc = parse_port_range(in[CB_FLD_DST_PORT_RANGE], &v->field_value[DSTP_FIELD_IPV4].value.u16, &v->field_value[DSTP_FIELD_IPV4].mask_range.u16); if (rc != 0) { RTE_LOG(ERR, PIPELINE, "failed to read dest port range: %s\n", in[CB_FLD_DST_PORT_RANGE]); return rc; } printf("V=%u, mask=%u\n", v->field_value[DSTP_FIELD_IPV4].value.u16, v->field_value[DSTP_FIELD_IPV4].mask_range.u16); /* parse 0/0xnn */ GET_CB_FIELD(in[CB_FLD_PROTO], v->field_value[PROTO_FIELD_IPV4].value.u8, 0, UINT8_MAX, '/'); GET_CB_FIELD(in[CB_FLD_PROTO], v->field_value[PROTO_FIELD_IPV4].mask_range.u8, 0, UINT8_MAX, 0); printf("V=%u, mask=%u\n", (unsigned int)v->field_value[PROTO_FIELD_IPV4].value.u8, v->field_value[PROTO_FIELD_IPV4].mask_range.u8); return 0; } static int parse_cb_ipv4_rule_del(char *str, struct rte_table_acl_rule_delete_params *v) { int i, rc; char *s, *sp, *in[CB_FLD_NUM]; static const char *dlm = " \t\n"; /* ** Skip leading '@' */ if (strchr(str, '@') != str) return -EINVAL; s = str + 1; /* * Populate the 'in' array with the location of each * field in the string we're parsing */ for (i = 0; i != DIM(in); i++) { in[i] = strtok_r(s, dlm, &sp); if (in[i] == NULL) return -EINVAL; s = NULL; } /* Parse x.x.x.x/x */ rc = parse_ipv4_net(in[CB_FLD_SRC_ADDR], &v->field_value[SRC_FIELD_IPV4].value.u32, &v->field_value[SRC_FIELD_IPV4].mask_range.u32); if (rc != 0) { RTE_LOG(ERR, PIPELINE, "failed to read src address/mask: %s\n", in[CB_FLD_SRC_ADDR]); return rc; } printf("V=%u, mask=%u\n", v->field_value[SRC_FIELD_IPV4].value.u32, v->field_value[SRC_FIELD_IPV4].mask_range.u32); /* Parse x.x.x.x/x */ rc = parse_ipv4_net(in[CB_FLD_DST_ADDR], &v->field_value[DST_FIELD_IPV4].value.u32, &v->field_value[DST_FIELD_IPV4].mask_range.u32); if (rc != 0) { RTE_LOG(ERR, PIPELINE, "failed to read dest address/mask: %s\n", in[CB_FLD_DST_ADDR]); return rc; } printf("V=%u, mask=%u\n", v->field_value[DST_FIELD_IPV4].value.u32, v->field_value[DST_FIELD_IPV4].mask_range.u32); /* Parse n:n */ rc = parse_port_range(in[CB_FLD_SRC_PORT_RANGE], &v->field_value[SRCP_FIELD_IPV4].value.u16, &v->field_value[SRCP_FIELD_IPV4].mask_range.u16); if (rc != 0) { RTE_LOG(ERR, PIPELINE, "failed to read source port range: %s\n", in[CB_FLD_SRC_PORT_RANGE]); return rc; } printf("V=%u, mask=%u\n", v->field_value[SRCP_FIELD_IPV4].value.u16, v->field_value[SRCP_FIELD_IPV4].mask_range.u16); /* Parse n:n */ rc = parse_port_range(in[CB_FLD_DST_PORT_RANGE], &v->field_value[DSTP_FIELD_IPV4].value.u16, &v->field_value[DSTP_FIELD_IPV4].mask_range.u16); if (rc != 0) { RTE_LOG(ERR, PIPELINE, "failed to read dest port range: %s\n", in[CB_FLD_DST_PORT_RANGE]); return rc; } printf("V=%u, mask=%u\n", v->field_value[DSTP_FIELD_IPV4].value.u16, v->field_value[DSTP_FIELD_IPV4].mask_range.u16); /* parse 0/0xnn */ GET_CB_FIELD(in[CB_FLD_PROTO], v->field_value[PROTO_FIELD_IPV4].value.u8, 0, UINT8_MAX, '/'); GET_CB_FIELD(in[CB_FLD_PROTO], v->field_value[PROTO_FIELD_IPV4].mask_range.u8, 0, UINT8_MAX, 0); printf("V=%u, mask=%u\n", (unsigned int)v->field_value[PROTO_FIELD_IPV4].value.u8, v->field_value[PROTO_FIELD_IPV4].mask_range.u8); return 0; } /* * The format for these rules DO NOT need the port ranges to be * separated by ' : ', just ':'. It's a lot more readable and * cleaner, IMO. */ char lines[][128] = { "@0.0.0.0/0 0.0.0.0/0 0:65535 0:65535 2/0xff", /* Protocol check */ "@192.168.3.1/32 0.0.0.0/0 0:65535 0:65535 0/0", /* Src IP checl */ "@0.0.0.0/0 10.4.4.1/32 0:65535 0:65535 0/0", /* dst IP check */ "@0.0.0.0/0 0.0.0.0/0 105:105 0:65535 0/0", /* src port check */ "@0.0.0.0/0 0.0.0.0/0 0:65535 206:206 0/0", /* dst port check */ }; char line[128]; static int setup_acl_pipeline(void) { int ret; int i; struct rte_pipeline_params pipeline_params = { .name = "PIPELINE", .socket_id = 0, }; uint32_t n; struct rte_table_acl_rule_add_params rule_params; struct rte_pipeline_table_acl_rule_delete_params *delete_params; parse_5tuple parser; char acl_name[64]; /* Pipeline configuration */ p = rte_pipeline_create(&pipeline_params); if (p == NULL) { RTE_LOG(INFO, PIPELINE, "%s: Failed to configure pipeline\n", __func__); goto fail; } /* Input port configuration */ for (i = 0; i < N_PORTS; i++) { struct rte_port_ring_reader_params port_ring_params = { .ring = rings_rx[i], }; struct rte_pipeline_port_in_params port_params = { .ops = &rte_port_ring_reader_ops, .arg_create = (void *) &port_ring_params, .f_action = NULL, .burst_size = BURST_SIZE, }; /* Put in action for some ports */ if (i) port_params.f_action = port_in_action; ret = rte_pipeline_port_in_create(p, &port_params, &port_in_id[i]); if (ret) { rte_panic("Unable to configure input port %d, ret:%d\n", i, ret); goto fail; } } /* output Port configuration */ for (i = 0; i < N_PORTS; i++) { struct rte_port_ring_writer_params port_ring_params = { .ring = rings_tx[i], .tx_burst_sz = BURST_SIZE, }; struct rte_pipeline_port_out_params port_params = { .ops = &rte_port_ring_writer_ops, .arg_create = (void *) &port_ring_params, .f_action = NULL, .arg_ah = NULL, }; if (rte_pipeline_port_out_create(p, &port_params, &port_out_id[i])) { rte_panic("Unable to configure output port %d\n", i); goto fail; } } /* Table configuration */ for (i = 0; i < N_PORTS; i++) { struct rte_pipeline_table_params table_params; /* Set up defaults for stub */ table_params.ops = &rte_table_stub_ops; table_params.arg_create = NULL; table_params.f_action_hit = action_handler_hit; table_params.f_action_miss = NULL; table_params.action_data_size = 0; RTE_LOG(INFO, PIPELINE, "miss_action=%x\n", table_entry_miss_action); printf("RTE_ACL_RULE_SZ(%zu) = %zu\n", DIM(ipv4_defs), RTE_ACL_RULE_SZ(DIM(ipv4_defs))); struct rte_table_acl_params acl_params; acl_params.n_rules = 1 << 5; acl_params.n_rule_fields = DIM(ipv4_defs); snprintf(acl_name, sizeof(acl_name), "ACL%d", i); acl_params.name = acl_name; memcpy(acl_params.field_format, ipv4_defs, sizeof(ipv4_defs)); table_params.ops = &rte_table_acl_ops; table_params.arg_create = &acl_params; if (rte_pipeline_table_create(p, &table_params, &table_id[i])) { rte_panic("Unable to configure table %u\n", i); goto fail; } if (connect_miss_action_to_table) { if (rte_pipeline_table_create(p, &table_params, &table_id[i+2])) { rte_panic("Unable to configure table %u\n", i); goto fail; } } } for (i = 0; i < N_PORTS; i++) { if (rte_pipeline_port_in_connect_to_table(p, port_in_id[i], table_id[i])) { rte_panic("Unable to connect input port %u to " "table %u\n", port_in_id[i], table_id[i]); goto fail; } } /* Add bulk entries to tables */ for (i = 0; i < N_PORTS; i++) { struct rte_table_acl_rule_add_params keys[5]; struct rte_pipeline_table_entry entries[5]; struct rte_table_acl_rule_add_params *key_array[5]; struct rte_pipeline_table_entry *table_entries[5]; int key_found[5]; struct rte_pipeline_table_entry *table_entries_ptr[5]; struct rte_pipeline_table_entry entries_ptr[5]; parser = parse_cb_ipv4_rule; for (n = 0; n < 5; n++) { memset(&keys[n], 0, sizeof(struct rte_table_acl_rule_add_params)); key_array[n] = &keys[n]; snprintf(line, sizeof(line), "%s", lines[n]); printf("PARSING [%s]\n", line); ret = parser(line, &keys[n]); if (ret != 0) { RTE_LOG(ERR, PIPELINE, "line %u: parse_cb_ipv4vlan_rule" " failed, error code: %d (%s)\n", n, ret, strerror(-ret)); return ret; } keys[n].priority = RTE_ACL_MAX_PRIORITY - n - 1; entries[n].action = RTE_PIPELINE_ACTION_PORT; entries[n].port_id = port_out_id[i^1]; table_entries[n] = &entries[n]; table_entries_ptr[n] = &entries_ptr[n]; } ret = rte_pipeline_table_entry_add_bulk(p, table_id[i], (void **)key_array, table_entries, 5, key_found, table_entries_ptr); if (ret < 0) { rte_panic("Add entry bulk to table %u failed (%d)\n", table_id[i], ret); goto fail; } } /* Delete bulk entries from tables */ for (i = 0; i < N_PORTS; i++) { struct rte_table_acl_rule_delete_params keys[5]; struct rte_table_acl_rule_delete_params *key_array[5]; struct rte_pipeline_table_entry *table_entries[5]; int key_found[5]; for (n = 0; n < 5; n++) { memset(&keys[n], 0, sizeof(struct rte_table_acl_rule_delete_params)); key_array[n] = &keys[n]; snprintf(line, sizeof(line), "%s", lines[n]); printf("PARSING [%s]\n", line); ret = parse_cb_ipv4_rule_del(line, &keys[n]); if (ret != 0) { RTE_LOG(ERR, PIPELINE, "line %u: parse_cb_ipv4vlan_rule" " failed, error code: %d (%s)\n", n, ret, strerror(-ret)); return ret; } } ret = rte_pipeline_table_entry_delete_bulk(p, table_id[i], (void **)key_array, 5, key_found, table_entries); if (ret < 0) { rte_panic("Delete bulk entries from table %u failed (%d)\n", table_id[i], ret); goto fail; } else printf("Bulk deleted rules.\n"); } /* Add entries to tables */ for (i = 0; i < N_PORTS; i++) { struct rte_pipeline_table_entry table_entry = { .action = RTE_PIPELINE_ACTION_PORT, {.port_id = port_out_id[i^1]}, }; int key_found; struct rte_pipeline_table_entry *entry_ptr; memset(&rule_params, 0, sizeof(rule_params)); parser = parse_cb_ipv4_rule; for (n = 1; n <= 5; n++) { snprintf(line, sizeof(line), "%s", lines[n-1]); printf("PARSING [%s]\n", line); ret = parser(line, &rule_params); if (ret != 0) { RTE_LOG(ERR, PIPELINE, "line %u: parse_cb_ipv4vlan_rule" " failed, error code: %d (%s)\n", n, ret, strerror(-ret)); return ret; } rule_params.priority = RTE_ACL_MAX_PRIORITY - n; ret = rte_pipeline_table_entry_add(p, table_id[i], &rule_params, &table_entry, &key_found, &entry_ptr); if (ret < 0) { rte_panic("Add entry to table %u failed (%d)\n", table_id[i], ret); goto fail; } } /* delete a few rules */ for (n = 2; n <= 3; n++) { snprintf(line, sizeof(line), "%s", lines[n-1]); printf("PARSING [%s]\n", line); ret = parser(line, &rule_params); if (ret != 0) { RTE_LOG(ERR, PIPELINE, "line %u: parse rule " " failed, error code: %d (%s)\n", n, ret, strerror(-ret)); return ret; } delete_params = (struct rte_pipeline_table_acl_rule_delete_params *) &(rule_params.field_value[0]); ret = rte_pipeline_table_entry_delete(p, table_id[i], delete_params, &key_found, NULL); if (ret < 0) { rte_panic("Add entry to table %u failed (%d)\n", table_id[i], ret); goto fail; } else printf("Deleted Rule.\n"); } /* Try to add duplicates */ for (n = 1; n <= 5; n++) { snprintf(line, sizeof(line), "%s", lines[n-1]); printf("PARSING [%s]\n", line); ret = parser(line, &rule_params); if (ret != 0) { RTE_LOG(ERR, PIPELINE, "line %u: parse rule" " failed, error code: %d (%s)\n", n, ret, strerror(-ret)); return ret; } rule_params.priority = RTE_ACL_MAX_PRIORITY - n; ret = rte_pipeline_table_entry_add(p, table_id[i], &rule_params, &table_entry, &key_found, &entry_ptr); if (ret < 0) { rte_panic("Add entry to table %u failed (%d)\n", table_id[i], ret); goto fail; } } } /* Enable input ports */ for (i = 0; i < N_PORTS ; i++) if (rte_pipeline_port_in_enable(p, port_in_id[i])) rte_panic("Unable to enable input port %u\n", port_in_id[i]); /* Check pipeline consistency */ if (rte_pipeline_check(p) < 0) { rte_panic("Pipeline consistency check failed\n"); goto fail; } return 0; fail: return -1; } static int test_pipeline_single_filter(int expected_count) { int i, j, ret, tx_count; struct ipv4_5tuple five_tuple; /* Allocate a few mbufs and manually insert into the rings. */ for (i = 0; i < N_PORTS; i++) { for (j = 0; j < 8; j++) { struct rte_mbuf *mbuf; mbuf = rte_pktmbuf_alloc(pool); if (mbuf == NULL) /* this will cause test failure after cleanup * of already enqueued mbufs, as the mbuf * counts won't match */ break; memset(rte_pktmbuf_mtod(mbuf, char *), 0x00, sizeof(struct ipv4_5tuple)); five_tuple.proto = j; five_tuple.ip_src = rte_bswap32(IPv4(192, 168, j, 1)); five_tuple.ip_dst = rte_bswap32(IPv4(10, 4, j, 1)); five_tuple.port_src = rte_bswap16(100 + j); five_tuple.port_dst = rte_bswap16(200 + j); memcpy(rte_pktmbuf_mtod(mbuf, char *), &five_tuple, sizeof(struct ipv4_5tuple)); RTE_LOG(INFO, PIPELINE, "%s: Enqueue onto ring %d\n", __func__, i); rte_ring_enqueue(rings_rx[i], mbuf); } } /* Run pipeline once */ for (i = 0; i< N_PORTS; i++) rte_pipeline_run(p); rte_pipeline_flush(p); tx_count = 0; for (i = 0; i < N_PORTS; i++) { void *objs[RING_TX_SIZE]; struct rte_mbuf *mbuf; ret = rte_ring_sc_dequeue_burst(rings_tx[i], objs, 10); if (ret <= 0) { printf("Got no objects from ring %d - error code %d\n", i, ret); } else { printf("Got %d object(s) from ring %d!\n", ret, i); for (j = 0; j < ret; j++) { mbuf = (struct rte_mbuf *)objs[j]; rte_hexdump(stdout, "mbuf", rte_pktmbuf_mtod(mbuf, char *), 64); rte_pktmbuf_free(mbuf); } tx_count += ret; } } if (tx_count != expected_count) { RTE_LOG(INFO, PIPELINE, "%s: Unexpected packets for ACL test, " "expected %d, got %d\n", __func__, expected_count, tx_count); goto fail; } rte_pipeline_free(p); return 0; fail: return -1; } int test_table_acl(void) { override_hit_mask = 0xFF; /* All packets are a hit */ setup_acl_pipeline(); if (test_pipeline_single_filter(10) < 0) return -1; return 0; }