/* Try to delete all flows, which should succeed */ static void test_flow_table_del_all(int argc, char *argv[]) { struct flow_key key1 = {1}; struct flow_key key2 = {2}; struct flow_key key_check = {0}; struct action action_multiple[MAX_ACTIONS] = {0}; struct action action_check[MAX_ACTIONS] = {0}; struct flow_stats stats_check = {0}; int ret = 0; flow_table_init(); action_output_build(&action_multiple[0], 1); action_output_build(&action_multiple[1], 2); action_null_build(&action_multiple[2]); /* Add two flowss to be deleted */ flow_table_add_flow(&key1, action_multiple); ret = flow_table_get_flow(&key1, NULL, NULL); assert(ret >= 0); flow_table_add_flow(&key2, action_multiple); ret = flow_table_get_flow(&key2, NULL, NULL); assert(ret >= 0); /* delete all flows and ensure flows are deleted */ flow_table_del_all(); ret = flow_table_get_first_flow(&key_check, action_check, &stats_check); assert(ret < 0); }
/* * Delete single flow or all flows. * * When request->key is empty delete all flows */ static void flow_cmd_del(struct dpdk_flow_message *request) { struct dpdk_message reply = {0}; struct flow_key empty = {0}; int pos = 0; if (!memcmp(&request->key, &empty, sizeof(request->key))) { flow_table_del_all(); reply.type = 0; } else { pos = flow_table_lookup(&request->key); if (pos < 0) { reply.type = ENOENT; } else { /* Retrieve flow stats*/ flow_table_get_flow(&request->key, NULL, &request->stats); flow_table_del_flow(&request->key); reply.type = 0; } } reply.flow_msg = *request; send_reply_to_vswitchd(&reply); }
/* * Add or modify flow table entry. * * When modifying, the stats can be optionally cleared */ static void flow_cmd_new(struct dpdk_flow_message *request) { struct dpdk_message reply = {0}; int pos = 0; pos = flow_table_lookup(&request->key); if (pos < 0) { if (request->flags & FLAG_CREATE) { flow_table_add_flow(&request->key, request->actions); reply.type = 0; } else { reply.type = ENOENT; } } else { if (request->flags & FLAG_REPLACE) { /* Retrieve flow stats*/ flow_table_get_flow(&request->key, NULL, &request->stats); /* Depending on the value of request->clear we will * either update or keep the same stats */ flow_table_mod_flow(&request->key, request->actions, request->clear); reply.type = 0; } else { reply.type = EEXIST; } } reply.flow_msg = *request; send_reply_to_vswitchd(&reply); }
/* * Delete single flow or all flows. * * When request->key is empty delete all flows */ static void flow_cmd_del(struct dpdk_flow_message *request) { struct dpdk_message reply = {0}; struct flow_key empty = {0}; int32_t pos = 0; if (!memcmp(&request->key, &empty, sizeof(request->key))) { /* if flow is empty, delete all flows */ for (pos = 0; pos < MAX_FLOWS; pos++) { if (flow_table->used[pos]) { rte_hash_del_key(handle, &request->key); flow_table_del_flow(pos); } } reply.type = 0; } else { /* delete specified flow */ pos = rte_hash_del_key(handle, &request->key); if (pos < 0) { reply.type = ENOENT; } else { flow_table_get_flow(pos, NULL, NULL, &request->stats); flow_table_del_flow(pos); reply.type = 0; } } reply.flow_msg = *request; send_reply_to_vswitchd(&reply); }
/* Try to modify a flow, which should succeed */ static void test_flow_table_mod_flow(int argc, char *argv[]) { struct flow_key key1 = {1}; struct action action_multiple[MAX_ACTIONS] = {0}; struct action action_check[MAX_ACTIONS] = {0}; struct flow_stats stats_zero = {0}; struct flow_stats stats_check = {0}; int ret = 0; flow_table_init(); /* add a flow with 2 actions */ action_output_build(&action_multiple[0], 2); action_output_build(&action_multiple[1], 1); action_null_build(&action_multiple[2]); flow_table_add_flow(&key1, action_multiple); action_output_build(&action_multiple[0], 1); action_null_build(&action_multiple[1]); /*modify it to only have 1 action */ flow_table_mod_flow(&key1, action_multiple, true); ret = flow_table_get_flow(&key1, action_check, &stats_check); assert(ret >= 0); assert(memcmp(&action_multiple[0], action_check, sizeof(struct action)) == 0); /* check that our flow now only has one entry */ assert(action_check[1].type == ACTION_NULL); assert(memcmp(&stats_zero, &stats_check , sizeof(struct flow_stats)) == 0 ); }
/* * Return flow entry to vswitchd if it exists */ static void flow_cmd_get(struct dpdk_flow_message *request) { struct dpdk_message reply = {0}; int ret = 0; ret = flow_table_get_flow(&request->key, request->actions, &request->stats); if (ret < 0) { reply.type = ENOENT; } else { reply.type = 0; } reply.flow_msg = *request; send_reply_to_vswitchd(&reply); }
/* * Dump all flows. * * The message that is received contains the key for the previously dumped * flow entry. If the key is zero then we are dumping the first entry. We * reply with EOF when we have dumped all flows * */ static void flow_cmd_dump(struct dpdk_flow_message *request) { struct dpdk_message reply = {0}; struct flow_key empty = {0}; int32_t pos = 0; if (!memcmp(&request->key, &empty, sizeof(request->key))) { /* * if flow is empty, it is first call of dump(), * and start searching from the first rule */ pos = 0; } else { /* last dumped flow */ pos = rte_hash_lookup(handle, &request->key); if (pos < 0) { /* send error reply - the flow must be in the flow table */ reply.type = ENOENT; goto out; } /* search starting from the next flow */ pos++; } /* find next using flow */ for(;pos < MAX_FLOWS && !flow_table->used[pos]; pos++) ; if (pos < MAX_FLOWS) { flow_table_get_flow(pos, &request->key, &request->action, &request->stats); reply.type = 0; } else { /* it was last flow, send message that no more flows here */ reply.type = EOF; } out: reply.flow_msg = *request; send_reply_to_vswitchd(&reply); }
/* * Return flow entry to vswitchd if it exists */ static void flow_cmd_get(struct dpdk_flow_message *request) { struct dpdk_message reply = {0}; int32_t pos = 0; pos = rte_hash_lookup(handle, &request->key); if (pos < 0) { reply.type = ENOENT; } else { flow_table_get_flow(pos, NULL, &request->action, &request->stats); reply.type = 0; } reply.flow_msg = *request; send_reply_to_vswitchd(&reply); }
/* * This function takes a packet and routes it as per the flow table. */ void switch_packet(struct rte_mbuf *pkt, uint8_t in_port) { int ret = 0; struct dpdk_upcall info = {0}; struct action action = {0}; flow_key_extract(pkt, in_port, &info.key); ret = flow_table_get_flow(&info.key, &action, NULL); if (ret >= 0) { flow_table_update_stats(&info.key, pkt); action_execute(&action, pkt); } else { /* flow table miss, send unmatched packet to the daemon */ info.cmd = PACKET_CMD_MISS; send_packet_to_vswitchd(pkt, &info); } }
/* Try to get a flow, which should succeed */ static void test_flow_table_get_flow(int argc, char *argv[]) { struct flow_key key1 = {1}; struct action action_multiple[MAX_ACTIONS] = {0}; struct action action_check[MAX_ACTIONS] = {0}; struct flow_stats stats_zero = {0}; struct flow_stats stats_check = {0}; int ret = 0; flow_table_init(); action_output_build(&action_multiple[0], 1); action_output_build(&action_multiple[1], 2); action_null_build(&action_multiple[2]); flow_table_add_flow(&key1, action_multiple); ret = flow_table_get_flow(&key1, action_check, &stats_check); assert(ret >= 0); assert(memcmp(&action_multiple[0], action_check, sizeof(struct action)) == 0); assert(memcmp(&action_multiple[1], &action_check[1], sizeof(struct action)) == 0); assert(memcmp(&stats_zero, &stats_check , sizeof(struct flow_stats)) == 0 ); }
/* Try to delete a normal flow and a non-existent flow, which should succeed * and fail with -1 respectively */ static void test_flow_table_del_flow(int argc, char *argv[]) { struct flow_key key1 = {1}; struct action action_multiple[MAX_ACTIONS] = {0}; int ret = 0; flow_table_init(); /* TODO: Break this into multiple tests? */ action_output_build(&action_multiple[0], 1); action_output_build(&action_multiple[1], 2); action_null_build(&action_multiple[2]); flow_table_add_flow(&key1, action_multiple); ret = flow_table_del_flow(&key1); assert(ret >= 0); /* check no flow match */ ret = flow_table_get_flow(&key1, NULL, NULL); assert(ret < 0); ret = flow_table_del_flow(&key1); assert(ret < 0); }