diff --git a/include/aca_oam_port_manager.h b/include/aca_oam_port_manager.h new file mode 100644 index 00000000..a620c898 --- /dev/null +++ b/include/aca_oam_port_manager.h @@ -0,0 +1,55 @@ +// Copyright 2019 The Alcor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ACA_OAM_PORT_MANAGER_H +#define ACA_OAM_PORT_MANAGER_H + +#include +#include +#include +#include +#include + +using namespace std; + +namespace aca_oam_port_manager +{ +class Aca_Oam_Port_Manager { + public: + Aca_Oam_Port_Manager(); + ~Aca_Oam_Port_Manager(); + + static Aca_Oam_Port_Manager &get_instance(); + + void create_entry_unsafe(uint32_t port_number); + void add_vpc(uint32_t port, uint32_t tunnel_id); + int remove_vpc(uint32_t port, uint32_t tunnel_id); + + //Determine whether the port is an oam_server_port + bool is_oam_server_port(uint32_t port_number); + + private: + int _create_oam_ofp(uint32_t port_number); + int _delete_oam_ofp(uint32_t port_number); + void _clear_all_data(); + + // unordered_map + unordered_map > _oam_ports_table; + + // mutex for reading and writing to _oam_ports_table + mutex _oam_ports_table_mutex; +}; + +} // namespace aca_oam_port_manager +#endif //#ifndef ACA_OAM_PORT_MANAGER_H \ No newline at end of file diff --git a/include/aca_oam_server.h b/include/aca_oam_server.h new file mode 100644 index 00000000..aa474dbb --- /dev/null +++ b/include/aca_oam_server.h @@ -0,0 +1,123 @@ +// Copyright 2019 The Alcor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ACA_OAM_SERVER_H +#define ACA_OAM_SERVER_H + +#include +#include +#include +#include +#include + +#include "goalstateprovisioner.grpc.pb.h" + +using namespace std; +namespace aca_oam_server +{ +//OAM Message Type +#define SIZE_OP_CODE 8 +#define OAM_MSG_FLOW_INJECTION (0x0) +#define OAM_MSG_FLOW_DELETION (0x1) +#define OAM_MSG_NONE (0x3) +#define OAM_MSG_MAX (0x3) + +struct oam_match { + string sip; + string dip; + string sport; + string dport; + string proto; + string vni; +}; + +struct oam_action { + string inst_nw_dst; + string node_nw_dst; + string inst_dl_dst; + string node_dl_dst; + string idle_timeout; +}; + +struct flow_inject_msg { + struct in_addr inner_src_ip; // Inner Packet SIP + struct in_addr inner_dst_ip; // Inner Packet DIP + uint16_t src_port; // Inner Packet SPort + uint16_t dst_port; // Inner Packet DPort + uint8_t proto; // Inner Packet Protocol + uint8_t vni[4]; // Inner Packet Protocol + struct in_addr inst_dst_ip; // Destination Inst IP + struct in_addr node_dst_ip; // Destination Node IP + uint8_t inst_dst_mac[6]; // Destination Inst MAC + uint8_t node_dst_mac[6]; // Destination Node MAC + uint16_t idle_timeout; // 0 - 65536s +}; + +struct flow_del_msg { + struct in_addr inner_src_ip; + struct in_addr inner_dst_ip; + uint16_t src_port; + uint16_t dst_port; + uint8_t proto; + uint8_t vni[4]; +}; + +struct oam_message { + uint32_t op_code; + union op_data { + struct flow_inject_msg msg_inject_flow; + struct flow_del_msg msg_del_flow; + } data; +}; + +class ACA_Oam_Server { + public: + ACA_Oam_Server(); + ~ACA_Oam_Server(); + + static ACA_Oam_Server &get_instance(); + void oams_recv(uint32_t udp_dport, void *message); + + private: + uint8_t _get_message_type(oam_message *oammsg); + + oam_match _get_oam_match_field(oam_message *oammsg); + + oam_action _get_oam_action_field(oam_message *oammsg); + + int _add_direct_path(oam_match match, oam_action action); + + int _del_direct_path(oam_match match); + + void _init_oam_msg_ops(); + + bool _validate_oam_message(oam_message *oammsg); + + bool _check_oam_server_port(uint32_t udp_dport, oam_match match); + + void _parse_oam_flow_injection(uint32_t udp_dport, oam_message *oammsg); + + void _parse_oam_flow_deletion(uint32_t udp_dport, oam_message *oammsg); + + void _parse_oam_none(uint32_t /* in_port */, oam_message *oammsg); + + void (aca_oam_server::ACA_Oam_Server ::*_parse_oam_msg_ops[OAM_MSG_MAX])(uint32_t udp_dpost, + oam_message *oammsg); + + string _get_mac_addr(uint8_t *mac); + + string _get_vpc_id(uint8_t *vni); +}; +} // namespace aca_oam_server +#endif // #ifndef ACA_OAM_SERVER_H \ No newline at end of file diff --git a/include/aca_vlan_manager.h b/include/aca_vlan_manager.h index bfa69d14..5b4cdb49 100644 --- a/include/aca_vlan_manager.h +++ b/include/aca_vlan_manager.h @@ -33,12 +33,16 @@ namespace aca_vlan_manager { struct vpc_table_entry { uint vlan_id; + + uint32_t tunnel_id; // list of ovs_ports names on this host in the same VPC to share the same internal vlan_id list ovs_ports; // hashtable of output (e.g. vxlan) tunnel ports to the neighbor host communication // to neighbor port ID mapping in this VPC // unordered_map unordered_map > outports_neighbors_table; + + uint32_t oam_server_port; }; class ACA_Vlan_Manager { @@ -49,6 +53,8 @@ class ACA_Vlan_Manager { uint get_or_create_vlan_id(string vpc_id); + uint get_or_create_vlan_id(uint32_t tunnel_id); + int create_ovs_port(string vpc_id, string ovs_port, uint tunnel_id, ulong &culminative_time); int delete_ovs_port(string vpc_id, string ovs_port, uint tunnel_id, ulong &culminative_time); @@ -57,11 +63,24 @@ class ACA_Vlan_Manager { alcor::schema::NetworkType network_type, string remote_host_ip, uint tunnel_id, ulong &culminative_time); + // create a neighbor port without specifying vpc_id and neighbor ID + int create_neighbor_outport(alcor::schema::NetworkType network_type, + string remote_host_ip, uint tunnel_id, + ulong &culminative_time); + int delete_neighbor_outport(string neighbor_id, string vpc_id, string outport_name, ulong &culminative_time); int get_outports_unsafe(string vpc_id, string &outports); + int get_oam_server_port(string vpc_id); + + int get_oam_server_port(uint32_t tunnel_id); + + void set_oam_server_port(string vpc_id, uint32_t port_number); + + void set_oam_server_port(uint32_t tunnel_id, uint32_t port_number); + // compiler will flag error when below is called ACA_Vlan_Manager(ACA_Vlan_Manager const &) = delete; void operator=(ACA_Vlan_Manager const &) = delete; diff --git a/include/aca_zeta_programming.h b/include/aca_zeta_programming.h new file mode 100644 index 00000000..008c7a93 --- /dev/null +++ b/include/aca_zeta_programming.h @@ -0,0 +1,49 @@ +// Copyright 2019 The Alcor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef ACA_ZETA_PROGRAMMING_H +#define ACA_ZETA_PROGRAMMING_H + +#include +#include +#include "goalstateprovisioner.grpc.pb.h" + +using namespace std; + +namespace aca_zeta_programming +{ +struct zeta_config { + string group_id; + // + list zeta_buckets; + uint32_t port_inband_operation; +}; + +class ACA_Zeta_Programming { + public: + static ACA_Zeta_Programming &get_instance(); + + int create_or_update_zeta_config(const alcor::schema::AuxGateway current_AuxGateway, + const string vpc_id, uint32_t tunnel_id); + + int delete_zeta_config(const alcor::schema::AuxGateway current_AuxGateway, + const string vpc_id, uint32_t tunnel_id); + + private: + int _create_or_update_zeta_group_entry(zeta_config *zeta_config_in); + + int _delete_zeta_group_entry(zeta_config *zeta_config_in); +}; +} // namespace aca_zeta_programming +#endif // #ifndef ACA_ZETA_PROGRAMMING_H \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5c9fe696..5ca1e6bd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -16,7 +16,10 @@ set(SOURCES ./ovs/ovs_control.cpp ./ovs/aca_ovs_control.cpp ./dhcp/aca_dhcp_state_handler.cpp - ./dhcp/aca_dhcp_server.cpp + ./dhcp/aca_dhcp_server.cpp + ./zeta/aca_oam_server.cpp + ./zeta/aca_zeta_programming.cpp + ./zeta/aca_oam_port_manager.cpp ) FIND_LIBRARY(RDKAFKA rdkafka /usr/lib/x86_64-linux-gnu NO_DEFAULT_PATH) FIND_LIBRARY(CPPKAFKA cppkafka /usr/local/lib NO_DEFAULT_PATH) diff --git a/src/dp_abstraction/aca_dataplane_ovs.cpp b/src/dp_abstraction/aca_dataplane_ovs.cpp index 4f2bcc71..3d319724 100644 --- a/src/dp_abstraction/aca_dataplane_ovs.cpp +++ b/src/dp_abstraction/aca_dataplane_ovs.cpp @@ -22,11 +22,13 @@ #include "aca_util.h" #include #include +#include "aca_zeta_programming.h" using namespace std; using namespace alcor::schema; using namespace aca_ovs_l2_programmer; using namespace aca_ovs_l3_programmer; +using namespace aca_zeta_programming; namespace aca_dataplane_ovs { @@ -72,6 +74,34 @@ static bool aca_lookup_subnet_info(GoalState &parsed_struct, const string target return false; } +static bool aca_lookup_auxgateway_info(GoalState &parsed_struct, const string targeted_vpc_id, + AuxGateway &found_auxgateway) +{ + // TODO: cache the auxgateway information to a dictionary to provide + // a faster look up for the next run, only use the below loop for + // cache miss. + // Look up the vpc configuration to query for auxgateway + for (int i = 0; i < parsed_struct.vpc_states_size(); i++) { + VpcConfiguration current_VpcConfiguration = + parsed_struct.vpc_states(i).configuration(); + + ACA_LOG_DEBUG("current_VpcConfiguration Vpc ID: %s.\n", + current_VpcConfiguration.id().c_str()); + + if (parsed_struct.vpc_states(i).operation_type() == OperationType::INFO) { + if (current_VpcConfiguration.id() == targeted_vpc_id) { + found_auxgateway = + parsed_struct.vpc_states(i).configuration().auxiliary_gateway(); + return true; + } + } + } + + ACA_LOG_ERROR("Not able to find auxgateway info for port with vpc ID: %s.\n", + targeted_vpc_id.c_str()); + return false; +} + int ACA_Dataplane_OVS::initialize() { // TODO: improve the logging system, and add logging to this module @@ -82,6 +112,7 @@ int ACA_Dataplane_OVS::update_vpc_state_workitem(const VpcState /* current_VpcSt GoalStateOperationReply & /* gsOperationReply */) { // TO BE IMPLEMENTED + return ENOSYS; } @@ -136,6 +167,7 @@ int ACA_Dataplane_OVS::update_port_state_workitem(const PortState current_PortSt string virtual_mac_address; string host_ip_address; string port_cidr; + AuxGateway found_auxgateway; ulong culminative_dataplane_programming_time = 0; ulong culminative_network_configuration_time = 0; @@ -168,6 +200,14 @@ int ACA_Dataplane_OVS::update_port_state_workitem(const PortState current_PortSt goto EXIT; } + if (!aca_lookup_auxgateway_info(parsed_struct, current_PortConfiguration.vpc_id(), + found_auxgateway)) { + // mark as warning for now to support the current workflow + // the code should proceed assuming this is a non aux gateway (zeta) supported port + ACA_LOG_WARN("Not able to find auxgateway info for port with vpc ID: %s.\n", + current_PortConfiguration.vpc_id().c_str()); + } + switch (current_PortState.operation_type()) { case OperationType::CREATE: if (current_PortConfiguration.update_type() != UpdateType::FULL) { @@ -200,6 +240,13 @@ int ACA_Dataplane_OVS::update_port_state_workitem(const PortState current_PortSt current_PortConfiguration.vpc_id(), generated_port_name, port_cidr, found_tunnel_id, culminative_dataplane_programming_time); + if (found_auxgateway.aux_gateway_type() == ZETA) { + ACA_LOG_INFO("%s", "AuxGateway_type is zeta!\n"); + // Update the zeta settings of vpc + overall_rc = ACA_Zeta_Programming::get_instance().create_or_update_zeta_config( + found_auxgateway, current_PortConfiguration.vpc_id(), found_tunnel_id); + } + break; case OperationType::UPDATE: @@ -226,6 +273,13 @@ int ACA_Dataplane_OVS::update_port_state_workitem(const PortState current_PortSt current_PortConfiguration.vpc_id(), generated_port_name, found_tunnel_id, culminative_dataplane_programming_time); + if (found_auxgateway.aux_gateway_type() == ZETA) { + ACA_LOG_INFO("%s", "AuxGateway_type is zeta!\n"); + // Delete the zeta settings of vpc + overall_rc = ACA_Zeta_Programming::get_instance().delete_zeta_config( + found_auxgateway, current_PortConfiguration.vpc_id(), found_tunnel_id); + } + break; default: diff --git a/src/ovs/aca_ovs_control.cpp b/src/ovs/aca_ovs_control.cpp index 1966c957..53739525 100644 --- a/src/ovs/aca_ovs_control.cpp +++ b/src/ovs/aca_ovs_control.cpp @@ -33,6 +33,8 @@ #include #include #include "aca_dhcp_server.h" +#include "aca_oam_server.h" +#include "aca_oam_port_manager.h" using namespace std; using namespace ovs_control; @@ -82,7 +84,8 @@ int ACA_OVS_Control::control() packet_out(target, options); } else { cout << "Usage: -c -t -o " << endl; - cout << " commands: monitor, dump-flows, add-flow, mod-flows, del-flows, packet-out..." << endl; + cout << " commands: monitor, dump-flows, add-flow, mod-flows, del-flows, packet-out..." + << endl; cout << " target: swtich name, such as br-int, br-tun, ..." << endl; cout << " options: " << endl; cout << " moinor: \"[miss-len] [invalid-ttl] [resume] [watch:format]\"" << endl; @@ -103,7 +106,7 @@ int ACA_OVS_Control::dump_flows(const char *bridge, const char *opt) int ACA_OVS_Control::flow_exists(const char *bridge, const char *flow) { - int rc = -EINVAL; + int rc = -EINVAL; rc = OVS_Control::get_instance().dump_flows(bridge, flow, false); return rc; } @@ -228,8 +231,8 @@ void ACA_OVS_Control::parse_packet(uint32_t in_port, void *packet) ACA_LOG_INFO(" Payload (%d bytes):\n", size_payload); // print_payload(payload, size_payload); } - } - } else if (ip->ip_p == IPPROTO_UDP) { + } + } else if (ip->ip_p == IPPROTO_UDP) { /* define/compute udp header offset */ const struct sniff_udp *udp = (struct sniff_udp *)(base + SIZE_ETHERNET + vlan_len + size_ip); @@ -264,6 +267,14 @@ void ACA_OVS_Control::parse_packet(uint32_t in_port, void *packet) aca_dhcp_server::ACA_Dhcp_Server::get_instance().dhcps_recv( in_port, const_cast(payload)); } + + /* oam message procedure */ + if (aca_oam_port_manager::Aca_Oam_Port_Manager::get_instance().is_oam_server_port( + (uint32_t)udp_dport)) { + ACA_LOG_INFO("%s", " Message Type: OAM\n"); + aca_oam_server::ACA_Oam_Server::get_instance().oams_recv( + (uint32_t)udp_dport, const_cast(payload)); + } } } } diff --git a/src/ovs/aca_ovs_l2_programmer.cpp b/src/ovs/aca_ovs_l2_programmer.cpp index 0121741c..a6b99589 100644 --- a/src/ovs/aca_ovs_l2_programmer.cpp +++ b/src/ovs/aca_ovs_l2_programmer.cpp @@ -123,7 +123,13 @@ int ACA_OVS_L2_Programmer::setup_ovs_bridges_if_need() execute_openflow_command("add-flow br-tun \"table=0,priority=1,in_port=\"patch-int\" actions=resubmit(,2)\"", not_care_culminative_time, overall_rc); - execute_openflow_command("add-flow br-tun \"table=2,priority=1 actions=resubmit(,22)\"", + execute_openflow_command("add-flow br-tun \"table=2,priority=1,dl_dst=00:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,20)\"", + not_care_culminative_time, overall_rc); + + execute_openflow_command("add-flow br-tun \"table=2,priority=1,dl_dst=01:00:00:00:00:00/01:00:00:00:00:00 actions=resubmit(,22)\"", + not_care_culminative_time, overall_rc); + + execute_openflow_command("add-flow br-tun \"table=20,priority=1 actions=resubmit(,22)\"", not_care_culminative_time, overall_rc); execute_openflow_command("add-flow br-tun \"table=0,priority=25,arp,arp_op=1,in_port=\"patch-int\" actions=resubmit(,51)\"", @@ -135,7 +141,7 @@ int ACA_OVS_L2_Programmer::setup_ovs_bridges_if_need() execute_openflow_command("add-flow br-tun \"table=0,priority=25,icmp,icmp_type=8,in_port=\"patch-int\" actions=resubmit(,52)\"", not_care_culminative_time, overall_rc); - execute_openflow_command("add-flow br-tun \"table=52,priority=1 actions=resubmit(,22)\"", + execute_openflow_command("add-flow br-tun \"table=52,priority=1 actions=resubmit(,20)\"", not_care_culminative_time, overall_rc); } else { // case 3: only one of the br-int or br-tun is there, diff --git a/src/ovs/aca_vlan_manager.cpp b/src/ovs/aca_vlan_manager.cpp index c66a8c35..ecc1a145 100644 --- a/src/ovs/aca_vlan_manager.cpp +++ b/src/ovs/aca_vlan_manager.cpp @@ -163,7 +163,7 @@ int ACA_Vlan_Manager::delete_ovs_port(string vpc_id, string ovs_port, ACA_LOG_DEBUG("ACA_Vlan_Manager::delete_ovs_port <--- Exiting, overall_rc = %d\n", overall_rc); return overall_rc; -} +} // namespace aca_vlan_manager int ACA_Vlan_Manager::create_neighbor_outport(string neighbor_id, string vpc_id, alcor::schema::NetworkType network_type, @@ -357,4 +357,102 @@ int ACA_Vlan_Manager::get_outports_unsafe(string vpc_id, string &outports) return overall_rc; } +//query oam_port with vpc_id +int ACA_Vlan_Manager::get_oam_server_port(string vpc_id) +{ + ACA_LOG_DEBUG("%s", "ACA_Vlan_Manager::get_oam_server_port ---> Entering\n"); + + int overall_rc; + uint32_t port_number; + // -----critical section starts----- + _vpcs_table_mutex.lock(); + if (_vpcs_table.find(vpc_id) == _vpcs_table.end()) { + ACA_LOG_ERROR("vpc_id %s not find in vpc_table\n", vpc_id.c_str()); + // If the vpc cannot be found, set the port number to 0. + port_number = 0; + overall_rc = ENOENT; + } else { + port_number = _vpcs_table[vpc_id].oam_server_port; + overall_rc = EXIT_SUCCESS; + } + _vpcs_table_mutex.unlock(); + // -----critical section ends----- + ACA_LOG_DEBUG("ACA_Vlan_Manager::get_oam_server_port <--- Exiting, overall_rc = %d\n", + overall_rc); + + return port_number; +} + +//query oam_port with tunnel_id +int ACA_Vlan_Manager::get_oam_server_port(uint32_t tunnel_id) +{ + ACA_LOG_DEBUG("%s", "ACA_Vlan_Manager::get_oam_server_port ---> Entering\n"); + + uint32_t port_number = 0; + int overall_rc = ENOENT; + // -----critical section starts----- + _vpcs_table_mutex.lock(); + unordered_map::iterator iter; + for (iter = _vpcs_table.begin(); iter != _vpcs_table.end(); iter++) { + vpc_table_entry entry = iter->second; + if (entry.tunnel_id == tunnel_id) { + port_number = entry.oam_server_port; + overall_rc = EXIT_SUCCESS; + break; + } + } + + _vpcs_table_mutex.unlock(); + // -----critical section ends----- + ACA_LOG_DEBUG("ACA_Vlan_Manager::get_oam_server_port <--- Exiting, overall_rc = %d\n", + overall_rc); + + return port_number; +} + +// Bind oam_server_port to vpc +void ACA_Vlan_Manager::set_oam_server_port(string vpc_id, uint32_t port_number) +{ + ACA_LOG_DEBUG("%s", "ACA_Vlan_Manager::set_oam_server_port ---> Entering\n"); + + // -----critical section starts----- + _vpcs_table_mutex.lock(); + if (_vpcs_table.find(vpc_id) == _vpcs_table.end()) { + create_entry_unsafe(vpc_id); + } + _vpcs_table[vpc_id].oam_server_port = port_number; + _vpcs_table_mutex.unlock(); + // -----critical section ends----- + + ACA_LOG_DEBUG("%s", "ACA_Vlan_Manager::set_oam_server_port <--- Exiting\n"); +} + +#ifdef __GNUC__ +# define UNUSE(x) UNUSE_ ## x __attribute__((__unused__)) +#else +# define UNUSE(x) UNUSE_ ## x +#endif + +void ACA_Vlan_Manager::set_oam_server_port(uint32_t UNUSE(tunnel_id), uint32_t UNUSE(port_number)) +{ + ACA_LOG_DEBUG("%s", "ACA_Vlan_Manager::set_oam_server_port ---> Entering\n"); + + //TBD. + + ACA_LOG_DEBUG("%s", "ACA_Vlan_Manager::set_oam_server_port <--- Exiting\n"); +} + +// create a neighbor port without specifying vpc_id and neighbor ID +int ACA_Vlan_Manager::create_neighbor_outport(alcor::schema::NetworkType UNUSE(network_type), + string UNUSE(remote_host_ip), uint UNUSE(tunnel_id), + ulong &UNUSE(culminative_time)) +{ + int overall_rc = EXIT_FAILURE; + // TBD. + + ACA_LOG_DEBUG("%s", "ACA_Vlan_Manager::create_neighbor_outport <--- Exiting\n"); + + return overall_rc; +} + } // namespace aca_vlan_manager diff --git a/src/zeta/aca_oam_port_manager.cpp b/src/zeta/aca_oam_port_manager.cpp new file mode 100644 index 00000000..60b754fa --- /dev/null +++ b/src/zeta/aca_oam_port_manager.cpp @@ -0,0 +1,183 @@ + +// Copyright 2019 The Alcor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "aca_oam_port_manager.h" +#include "aca_log.h" +#include +#include "aca_ovs_control.h" + +using namespace aca_ovs_control; + +namespace aca_oam_port_manager +{ +Aca_Oam_Port_Manager::Aca_Oam_Port_Manager() +{ +} + +Aca_Oam_Port_Manager::~Aca_Oam_Port_Manager() +{ + //clear all oam punt rules + for (auto entry : _oam_ports_table) { + _delete_oam_ofp(entry.first); + } + _clear_all_data(); +} + +Aca_Oam_Port_Manager &Aca_Oam_Port_Manager::get_instance() +{ + // Instance is destroyed when program exits. + // It is instantiated on first use. + static Aca_Oam_Port_Manager instance; + return instance; +} + +// add the OAM punt rule +int Aca_Oam_Port_Manager::_create_oam_ofp(uint32_t port_number) +{ + int overall_rc; + + string opt = "table=0,priority=25,udp,udp_dst=" + to_string(port_number) + ",actions=CONTROLLER"; + + overall_rc = ACA_OVS_Control::get_instance().add_flow("br-int", opt.c_str()); + + if (overall_rc == EXIT_SUCCESS) { + ACA_LOG_INFO("%s", "creat_oam_ofp succeeded!\n"); + } else { + ACA_LOG_ERROR("creat_oam_ofp failed!!! overrall_rc: %d\n", overall_rc); + } + + return overall_rc; +} + +// delete the OAM punt rule +int Aca_Oam_Port_Manager::_delete_oam_ofp(uint32_t port_number) +{ + int overall_rc; + + string opt = "udp,udp_dst=" + to_string(port_number); + + overall_rc = ACA_OVS_Control::get_instance().del_flows("br-int", opt.c_str()); + + if (overall_rc == EXIT_SUCCESS) { + ACA_LOG_INFO("%s", "delete_oam_ofp succeeded!\n"); + } else { + ACA_LOG_ERROR("delete_oam_ofp failed!!! overrall_rc: %d\n", overall_rc); + } + return overall_rc; +} + +void Aca_Oam_Port_Manager::_clear_all_data() +{ + ACA_LOG_DEBUG("%s", "Aca_Oam_Port_Manager::clear_all_data ---> Entering\n"); + + // -----critical section starts----- + _oam_ports_table_mutex.lock(); + // All the elements in the unordered_map container are dropped: + // their destructors are called, and they are removed from the container, + // leaving _oam_ports_table with a size of 0. + _oam_ports_table.clear(); + _oam_ports_table_mutex.unlock(); + // -----critical section ends----- + + ACA_LOG_DEBUG("%s", "Aca_Oam_Port_Manager::clear_all_data <--- Exiting\n"); +} + +// unsafe function, needs to be called inside oam_ports_table_mutex lock +// this function assumes there is no existing entry for port_number +void Aca_Oam_Port_Manager::create_entry_unsafe(uint32_t port_number) +{ + ACA_LOG_DEBUG("%s", "Aca_Oam_Port_Manager::create_entry_unsafe ---> Entering\n"); + + unordered_set tunnel_ids_table; + _oam_ports_table.emplace(port_number, tunnel_ids_table); + + ACA_LOG_DEBUG("%s", "Aca_Oam_Port_Manager::create_entry_unsafe <--- Exiting\n"); +} + +// update oam_ports_table and add the OAM punt rule also if this is the first port in the VPC +void Aca_Oam_Port_Manager::add_vpc(uint32_t port_number, uint32_t tunnel_id) +{ + ACA_LOG_DEBUG("%s", "Aca_Oam_Port_Manager::add_vpc ---> Entering\n"); + // -----critical section starts----- + _oam_ports_table_mutex.lock(); + if (_oam_ports_table.find(port_number) == _oam_ports_table.end()) { + create_entry_unsafe(port_number); + _create_oam_ofp(port_number); + } + _oam_ports_table[port_number].emplace(tunnel_id); + + _oam_ports_table_mutex.unlock(); + // -----critical section ends----- + + ACA_LOG_DEBUG("%s", "ACA_OVS_Programmer::add_vpc <--- Exiting\n"); +} + +// update oam_ports_table and delete the OAM punt rule if the last port in the VPC has been deleted +int Aca_Oam_Port_Manager::remove_vpc(uint32_t port_number, uint32_t tunnel_id) +{ + ACA_LOG_DEBUG("%s", "Aca_Oam_Port_Manager::remove_vpc ---> Entering\n"); + + int overall_rc; + + // -----critical section starts----- + _oam_ports_table_mutex.lock(); + if (_oam_ports_table.find(port_number) == _oam_ports_table.end()) { + ACA_LOG_ERROR("port id %u not find in oam_ports_table\n", port_number); + overall_rc = ENOENT; + } else { + _oam_ports_table[port_number].erase(tunnel_id); + // clean up the oam_ports_table entry and oam flow rule if there is no port assoicated + if (_oam_ports_table[port_number].empty()) { + if (_oam_ports_table.erase(port_number) == 1 && _delete_oam_ofp(port_number) == EXIT_SUCCESS) { + ACA_LOG_INFO("Successfuly cleaned up entry for port_number %u\n", port_number); + overall_rc = EXIT_SUCCESS; + } else { + ACA_LOG_ERROR("Failed to clean up entry for port_number %u\n", port_number); + overall_rc = EXIT_FAILURE; + } + } + } + _oam_ports_table_mutex.unlock(); + // -----critical section ends----- + + ACA_LOG_DEBUG("Aca_Oam_Port_Manager::remove_vpc <--- Exiting, overall_rc = %d\n", overall_rc); + + return overall_rc; +} + +// Determine whether the port is oam server port +bool Aca_Oam_Port_Manager::is_oam_server_port(uint32_t port_number) +{ + ACA_LOG_DEBUG("%s", "Aca_Oam_Port_Manager::is_oam_server_port ---> Entering\n"); + + bool overall_rc; + + // -----critical section starts----- + _oam_ports_table_mutex.lock(); + if (_oam_ports_table.find(port_number) == _oam_ports_table.end()) { + ACA_LOG_ERROR("port id %u not find in oam_ports_table\n", port_number); + overall_rc = false; + } else { + overall_rc = true; + } + _oam_ports_table_mutex.unlock(); + // -----critical section ends----- + ACA_LOG_DEBUG("Aca_Oam_Port_Manager::is_oam_server_port <--- Exiting, overall_rc = %d\n", + overall_rc); + + return overall_rc; +} + +} // namespace aca_oam_port_manager \ No newline at end of file diff --git a/src/zeta/aca_oam_server.cpp b/src/zeta/aca_oam_server.cpp new file mode 100644 index 00000000..7380a103 --- /dev/null +++ b/src/zeta/aca_oam_server.cpp @@ -0,0 +1,309 @@ +// Copyright 2019 The Alcor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "aca_oam_server.h" +#include "aca_log.h" +#include "goalstateprovisioner.grpc.pb.h" +#include +#include +#include +#include "aca_ovs_l2_programmer.h" +#include "aca_util.h" +#include "aca_ovs_control.h" +#include "aca_vlan_manager.h" + +using namespace std; +using namespace aca_ovs_control; + +namespace aca_oam_server +{ +ACA_Oam_Server::ACA_Oam_Server() +{ + _init_oam_msg_ops(); +} + +ACA_Oam_Server::~ACA_Oam_Server() +{ +} + +ACA_Oam_Server &ACA_Oam_Server::get_instance() +{ + static ACA_Oam_Server instance; + return instance; +} + +bool ACA_Oam_Server::_validate_oam_message(oam_message *oammsg) +{ + int retcode = 0; + + if (!oammsg) { + ACA_LOG_ERROR("%s", "OAM message is null!\n"); + return false; + } + + if (OAM_MSG_FLOW_INJECTION != oammsg->op_code || OAM_MSG_FLOW_DELETION != oammsg->op_code) { + retcode = -1; + ACA_LOG_ERROR("%s", "Invalid 'op_code' field for OAM message!\n"); + } + + if (0 != retcode) { + return false; + } + + return true; +} + +void ACA_Oam_Server::oams_recv(uint32_t udp_dport, void *message) +{ + oam_message *oammsg = nullptr; + + if (!message) { + ACA_LOG_ERROR("%s", "OAN message is null!\n"); + return; + } + + oammsg = (oam_message *)message; + + if (_validate_oam_message(oammsg)) { + ACA_LOG_ERROR("%s", "Invalid OAM message!\n"); + return; + } + + uint8_t msg_type = (uint8_t)_get_message_type(oammsg); + + (this->*_parse_oam_msg_ops[msg_type])(udp_dport, oammsg); + + return; +} + +void ACA_Oam_Server::_init_oam_msg_ops() +{ + _parse_oam_msg_ops[OAM_MSG_FLOW_INJECTION] = + &aca_oam_server::ACA_Oam_Server::_parse_oam_flow_injection; + _parse_oam_msg_ops[OAM_MSG_FLOW_DELETION] = + &aca_oam_server::ACA_Oam_Server::_parse_oam_flow_deletion; + _parse_oam_msg_ops[OAM_MSG_NONE] = &aca_oam_server::ACA_Oam_Server::_parse_oam_none; +} + +uint8_t ACA_Oam_Server::_get_message_type(oam_message *oammsg) +{ + if (!oammsg) { + ACA_LOG_ERROR("%s", "OAM message is null!\n"); + return OAM_MSG_NONE; + } + + if (!oammsg->op_code) { + return OAM_MSG_NONE; + } + + return (uint8_t)(ntohl(oammsg->op_code)); +} + +string ACA_Oam_Server::_get_mac_addr(uint8_t *mac) +{ + string mac_string; + stringstream ss; + + //Convert mac address to string + // from uint8[6] to string + for (int i = 0; i < 6; i++) { + ss << std::hex << std::setw(2) << std::setfill('0') + << static_cast(mac[i]); + ss << ":"; + } + ss >> mac_string; + mac_string.pop_back(); + + return mac_string; +} + +string ACA_Oam_Server::_get_vpc_id(uint8_t *vni) +{ + string vpc_id; + stringstream ss; + + for (int i = 0; i < 3; i++) { + ss << std::hex << std::setw(2) << std::setfill('0') + << static_cast(vni[i]); + } + + ss >> vpc_id; + vpc_id.pop_back(); + + return vpc_id; +} + +//extract data for flow table matching from the oam message +oam_match ACA_Oam_Server::_get_oam_match_field(oam_message *oammsg) +{ + oam_match match; + + flow_inject_msg msg_data = oammsg->data.msg_inject_flow; + + match.sip = inet_ntoa(msg_data.inner_src_ip); + match.dip = inet_ntoa(msg_data.inner_dst_ip); + match.sport = to_string(ntohs(msg_data.src_port)); + match.dport = to_string(ntohs(msg_data.dst_port)); + match.proto = to_string(msg_data.proto); + match.vni = _get_vpc_id(msg_data.vni); + + return match; +} + +//extract the data used for the flow table action from the oam message +oam_action ACA_Oam_Server::_get_oam_action_field(oam_message *oammsg) +{ + oam_action action; + + flow_inject_msg msg_data = oammsg->data.msg_inject_flow; + + action.inst_nw_dst = inet_ntoa(msg_data.inst_dst_ip); + action.node_nw_dst = inet_ntoa(msg_data.node_dst_ip); + action.inst_dl_dst = _get_mac_addr(msg_data.inst_dst_mac); + action.node_dl_dst = _get_mac_addr(msg_data.node_dst_mac); + action.idle_timeout = to_string(msg_data.idle_timeout); + + return action; +} + +//check whether the udp_dport is the oam server port of the vpc +bool ACA_Oam_Server::_check_oam_server_port(uint32_t udp_dport, oam_match match) +{ + uint32_t oam_port = + aca_vlan_manager::ACA_Vlan_Manager::get_instance().get_oam_server_port(match.vni); + + if (udp_dport == oam_port) { + ACA_LOG_INFO("%s", "oam port is correct!\n"); + return true; + } else { + ACA_LOG_ERROR("%s", "oam port is incorrect!!!"); + return false; + } +} + +void ACA_Oam_Server::_parse_oam_flow_injection(uint32_t udp_dport, oam_message *oammsg) +{ + unsigned long not_care_culminative_time; + int overall_rc; + + oam_match match = _get_oam_match_field(oammsg); + + // check whether the udp_dport is the oam server port of the vpc + if (!_check_oam_server_port(udp_dport, match)) { + return; + } + + oam_action action = _get_oam_action_field(oammsg); + + string remote_host_ip = action.node_nw_dst; + uint32_t tunnel_id = strtoul(match.vni.c_str(), NULL, 10); + alcor::schema::NetworkType network_type = alcor::schema::NetworkType::VXLAN; + + if (!aca_is_port_on_same_host(remote_host_ip)) { + ACA_LOG_INFO("%s", "port_neighbor not exist!\n"); + //crate neighbor_port + aca_vlan_manager::ACA_Vlan_Manager::get_instance().create_neighbor_outport( + network_type, remote_host_ip, tunnel_id, not_care_culminative_time); + } + overall_rc = aca_oam_server::ACA_Oam_Server::_add_direct_path(match, action); + + if (overall_rc == EXIT_SUCCESS) { + ACA_LOG_INFO("%s", "Command succeeded!\n"); + } else { + ACA_LOG_ERROR("Command failed!!! overrall_rc: %d\n", overall_rc); + } + + return; +} + +void ACA_Oam_Server::_parse_oam_flow_deletion(uint32_t udp_dport, oam_message *oammsg) +{ + int overall_rc; + oam_match match = _get_oam_match_field(oammsg); + // check whether the udp_dport is the oam server port of the vpc + if (!_check_oam_server_port(udp_dport, match)) { + return; + } + + overall_rc = _del_direct_path(match); + + if (overall_rc == EXIT_SUCCESS) { + ACA_LOG_INFO("%s", "Command succeeded!\n"); + } else { + ACA_LOG_ERROR("Command failed!!! overrall_rc: %d\n", overall_rc); + } + + return; +} + +void ACA_Oam_Server::_parse_oam_none(uint32_t /* in_port */, oam_message *oammsg) +{ + ACA_LOG_ERROR("Wrong OAM message type! (Message type = %d)\n", _get_message_type(oammsg)); + return; +} + +int ACA_Oam_Server::_add_direct_path(oam_match match, oam_action action) +{ + int overall_rc; + // + + string vlan_id = to_string(aca_vlan_manager::ACA_Vlan_Manager::get_instance().get_or_create_vlan_id( + match.vni)); + string outport_name = + aca_get_outport_name(alcor::schema::NetworkType::VXLAN, action.node_nw_dst); + + string cmd_match = "ip,nw_proto=" + match.proto + ",nw_src=" + match.sip + + ",nw_dst=" + match.dip + ",tp_src=" + match.sport + + ",tp_dst=" + match.dport + ",dl_vlan=" + vlan_id; + string cmd_action = "action=\"strip_vlan,load:" + match.vni + + "->NXM_NX_TUN_ID[],mod_dl_dst=" + action.inst_dl_dst + + ",mod_nw_dst=" + action.inst_nw_dst + + ",idle_timeout=" + action.idle_timeout + ",output:" + outport_name; + + // Adding unicast rules in table20 + string opt = "table=20,priority=50," + cmd_match + "," + cmd_action; + overall_rc = ACA_OVS_Control::get_instance().add_flow("br-tun", opt.c_str()); + + if (overall_rc == EXIT_SUCCESS) { + ACA_LOG_INFO("%s", "Add direct path succeeded!\n"); + } else { + ACA_LOG_ERROR("Add direct path failed!!! overrall_rc: %d\n", overall_rc); + } + + return overall_rc; +} + +int ACA_Oam_Server::_del_direct_path(oam_match match) +{ + int overall_rc; + string vlan_id = to_string(aca_vlan_manager::ACA_Vlan_Manager::get_instance().get_or_create_vlan_id( + match.vni)); + + string opt = "table=20,priority=50,ip,nw_proto=" + match.proto + + ",nw_src=" + match.sip + ",nw_dst=" + match.dip + + ",tp_src=" + match.sport + ",tp_dst=" + match.dport + ",dl_vlan=" + vlan_id; + + // delete flow + overall_rc = ACA_OVS_Control::get_instance().del_flows("br-tun", opt.c_str()); + + if (overall_rc == EXIT_SUCCESS) { + ACA_LOG_INFO("%s", "Delete direct path succeeded!\n"); + } else { + ACA_LOG_ERROR("Delete direct path failed!!! overrall_rc: %d\n", overall_rc); + } + + return overall_rc; +} + +} // namespace aca_oam_server diff --git a/src/zeta/aca_zeta_programming.cpp b/src/zeta/aca_zeta_programming.cpp new file mode 100644 index 00000000..4a2ff1f5 --- /dev/null +++ b/src/zeta/aca_zeta_programming.cpp @@ -0,0 +1,138 @@ +// Copyright 2019 The Alcor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "aca_zeta_programming.h" +#include "aca_ovs_l2_programmer.h" +#include "aca_util.h" +#include "aca_log.h" +#include "aca_vlan_manager.h" +#include "aca_oam_port_manager.h" + +using namespace aca_vlan_manager; +using namespace aca_oam_port_manager; +using namespace alcor::schema; +namespace aca_zeta_programming +{ +ACA_Zeta_Programming &ACA_Zeta_Programming::get_instance() +{ + static ACA_Zeta_Programming instance; + return instance; +} + +int ACA_Zeta_Programming::create_or_update_zeta_config(const alcor::schema::AuxGateway current_AuxGateway, + const string vpc_id, uint32_t tunnel_id) +{ + unsigned long not_care_culminative_time; + int overall_rc; + zeta_config stZetaCfg; + + stZetaCfg.group_id = current_AuxGateway.id(); + for (auto destination : current_AuxGateway.destinations()) { + stZetaCfg.zeta_buckets.push_back(destination.ip_address()); + string remote_host_ip = destination.ip_address(); + if (!aca_is_port_on_same_host(remote_host_ip)) { + ACA_LOG_INFO("%s", "port_neighbor not exist!\n"); + //crate neighbor_port + aca_vlan_manager::ACA_Vlan_Manager::get_instance().create_neighbor_outport( + alcor::schema::NetworkType::VXLAN, remote_host_ip, tunnel_id, + not_care_culminative_time); + } + } + uint32_t oam_server_port = current_AuxGateway.zeta_info().port_inband_operation(); + + uint32_t oam_port = ACA_Vlan_Manager::get_instance().get_oam_server_port(vpc_id); + // oam_server_port is not set + if (oam_port == 0) { + ACA_Vlan_Manager::get_instance().set_oam_server_port(vpc_id, oam_server_port); + + //update oam_ports_table and add the OAM punt rule also if this is the first port in the VPC + Aca_Oam_Port_Manager::get_instance().add_vpc(oam_server_port, tunnel_id); + } + // add the group bucket rule + overall_rc = _create_or_update_zeta_group_entry(&stZetaCfg); + + return overall_rc; +} + +int ACA_Zeta_Programming::delete_zeta_config(const alcor::schema::AuxGateway current_AuxGateway, + const string vpc_id, uint32_t tunnel_id) +{ + zeta_config stZetaCfg; + int overall_rc; + + stZetaCfg.group_id = current_AuxGateway.id(); + for (auto destination : current_AuxGateway.destinations()) { + stZetaCfg.zeta_buckets.push_back(destination.ip_address()); + } + uint32_t oam_server_port = current_AuxGateway.zeta_info().port_inband_operation(); + + // Reset oam_server_port to 0 + ACA_Vlan_Manager::get_instance().set_oam_server_port(vpc_id, 0); + + // update oam_ports_table and delete the OAM punt rule if the last port in the VPC has been deleted + Aca_Oam_Port_Manager::get_instance().remove_vpc(oam_server_port, tunnel_id); + + // delete the group bucket rule + overall_rc = _delete_zeta_group_entry(&stZetaCfg); + + return overall_rc; +} + +int ACA_Zeta_Programming::_create_or_update_zeta_group_entry(zeta_config *zeta_cfg) +{ + unsigned long not_care_culminative_time; + int overall_rc; + + //adding group table + string cmd = "-O OpenFlow13 add-group br-tun group_id=" + zeta_cfg->group_id + ",type=select"; + list::iterator it; + for (it = zeta_cfg->zeta_buckets.begin(); it != zeta_cfg->zeta_buckets.end(); it++) { + string outport_name = aca_get_outport_name(alcor::schema::NetworkType::VXLAN, *it); + cmd += ",bucket=output:" + outport_name; + } + + //add flow for i + aca_ovs_l2_programmer::ACA_OVS_L2_Programmer::get_instance().execute_openflow_command( + cmd, not_care_culminative_time, overall_rc); + + if (overall_rc == EXIT_SUCCESS) { + ACA_LOG_INFO("%s", "update_zeta_group_entry succeeded!\n"); + } else { + ACA_LOG_ERROR("update_zeta_group_entry failed!!! overrall_rc: %d\n", overall_rc); + } + + return overall_rc; +} + +int ACA_Zeta_Programming::_delete_zeta_group_entry(zeta_config *zeta_cfg) +{ + unsigned long not_care_culminative_time; + + int overall_rc; + + //deleting group table + string cmd = "-O OpenFlow13 del-groups br-tun group_id=" + zeta_cfg->group_id; + aca_ovs_l2_programmer::ACA_OVS_L2_Programmer::get_instance().execute_openflow_command( + cmd, not_care_culminative_time, overall_rc); + + if (overall_rc == EXIT_SUCCESS) { + ACA_LOG_INFO("%s", "delete_zeta_group_entry succeeded!\n"); + } else { + ACA_LOG_ERROR("delete_zeta_group_entry failed!!! overrall_rc: %d\n", overall_rc); + } + + return overall_rc; +} + +} // namespace aca_zeta_programming \ No newline at end of file diff --git a/test/func_tests/gs_tests.cpp b/test/func_tests/gs_tests.cpp index 74bfc611..6a1a4db8 100644 --- a/test/func_tests/gs_tests.cpp +++ b/test/func_tests/gs_tests.cpp @@ -747,4 +747,4 @@ int main(int argc, char *argv[]) aca_cleanup(); return rc; -} +} \ No newline at end of file diff --git a/test/gtest/aca_test_oam.cpp b/test/gtest/aca_test_oam.cpp new file mode 100644 index 00000000..a1184166 --- /dev/null +++ b/test/gtest/aca_test_oam.cpp @@ -0,0 +1,143 @@ +// Copyright 2019 The Alcor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "gtest/gtest.h" +#include "aca_oam_server.h" +#include "aca_util.h" +#include "goalstateprovisioner.grpc.pb.h" +#include +#include "aca_ovs_control.h" +#include "aca_vlan_manager.h" + +#define private public + +using namespace aca_oam_server; + +extern string vmac_address_1; +extern string vmac_address_2; +extern string vmac_address_3; +extern string vmac_address_4; +extern string vip_address_1; +extern string vip_address_2; +extern string vip_address_3; +extern string vip_address_4; +extern string remote_ip_1; +extern string remote_ip_2; + +alcor::schema::NetworkType network_type = alcor::schema::NetworkType::VXLAN; + +string tunnel_id_1 = "555"; +string tunnel_id_2 = "666"; + +string vlan_id_1 = "100"; +string vlan_id_2 = "200"; + +string outport_name_1= "vx9999"; + +using namespace aca_ovs_control; + +TEST(oam_message_test_cases, oams_recv_valid) +{ + int retcode = 0; + oam_message stOamMsg; + + stOamMsg.op_code = 0x0; + + stOamMsg.data.msg_inject_flow.inner_src_ip.s_addr = 55; + stOamMsg.data.msg_inject_flow.inner_dst_ip.s_addr = 66; + stOamMsg.data.msg_inject_flow.src_port = 500; + stOamMsg.data.msg_inject_flow.dst_port = 500; + stOamMsg.data.msg_inject_flow.proto = 0; + stOamMsg.data.msg_inject_flow.vni[0] = 0xaf; + stOamMsg.data.msg_inject_flow.vni[1] = 0x02; + stOamMsg.data.msg_inject_flow.vni[2] = 0xee; + stOamMsg.data.msg_inject_flow.node_dst_ip.s_addr = 77; + stOamMsg.data.msg_inject_flow.inst_dst_ip.s_addr = 88; + stOamMsg.data.msg_inject_flow.inst_dst_mac[0] = 0x3c; + stOamMsg.data.msg_inject_flow.inst_dst_mac[1] = 0xf0; + stOamMsg.data.msg_inject_flow.inst_dst_mac[2] = 0x11; + stOamMsg.data.msg_inject_flow.inst_dst_mac[3] = 0x12; + stOamMsg.data.msg_inject_flow.inst_dst_mac[4] = 0x56; + stOamMsg.data.msg_inject_flow.inst_dst_mac[5] = 0x65; + + retcode = ACA_Oam_Server::get_instance()._validate_oam_message(&stOamMsg); + EXPECT_EQ(retcode, EXIT_SUCCESS); + + retcode = ACA_Oam_Server::get_instance()._get_message_type(&stOamMsg); + EXPECT_EQ(retcode, OAM_MSG_FLOW_INJECTION); +} + +TEST(oam_message_test_cases, add_direct_path_valid) +{ + int retcode = 0; + + oam_match match; + oam_action action; + + match.sip = vip_address_1; + match.dip = vip_address_2; + match.sport = "55"; + match.dport = "77"; + match.vni = "300"; + match.proto = "6"; + + action.inst_nw_dst = vip_address_3; + action.node_nw_dst = remote_ip_1; + action.inst_dl_dst = vmac_address_1; + action.node_dl_dst = vmac_address_2; + action.idle_timeout = 10; + + string vlan_id = to_string(aca_vlan_manager::ACA_Vlan_Manager::get_instance().get_or_create_vlan_id(match.vni)); + + aca_oam_server::ACA_Oam_Server::get_instance()._add_direct_path(match, action); + + string cmd = "table=55,priority=50,ip,nw_proto=" + match.proto + ",nw_src=" + match.sip + ",nw_dst=" + match.dip + ",tp_src=" + + match.sport + ",tp_dst=" + match.dport + ",dl_vlan=" + vlan_id; + + retcode = ACA_OVS_Control::get_instance().flow_exists("br-tun", cmd.c_str()); + EXPECT_EQ(retcode, EXIT_SUCCESS); +} + +TEST(oam_message_test_cases, del_direct_path_valid) +{ + int retcode = 0; + + oam_match match; + oam_action action; + + match.sip = vip_address_1; + match.dip = vip_address_2; + match.sport = "55"; + match.dport = "77"; + match.vni = "300"; + match.proto = "6"; + + action.inst_nw_dst = vip_address_3; + action.node_nw_dst = remote_ip_1; + action.inst_dl_dst = vmac_address_1; + action.node_dl_dst = vmac_address_2; + action.idle_timeout = 10; + + string vlan_id = to_string(aca_vlan_manager::ACA_Vlan_Manager::get_instance().get_or_create_vlan_id(match.vni)); + + aca_oam_server::ACA_Oam_Server::get_instance()._add_direct_path(match, action); + + aca_oam_server::ACA_Oam_Server::get_instance()._del_direct_path(match); + + string cmd = "table=55,priority=50,ip,nw_proto=" + match.proto + ",nw_src=" + match.sip + ",nw_dst=" + match.dip + ",tp_src=" + + match.sport + ",tp_dst=" + match.dport + ",dl_vlan=" + vlan_id; + + retcode = ACA_OVS_Control::get_instance().flow_exists("br-tun", cmd.c_str()); + EXPECT_EQ(retcode, EXIT_SUCCESS); +} \ No newline at end of file