Skip to content

Commit

Permalink
L2-vxlan feature[#376]
Browse files Browse the repository at this point in the history
[vxlanorch]

* Adding the following logic:
- When creating vxlan tunnel, create its bridge port. If VXLAN tunnel
without bridge port, FDB MAC can not be learnt from or set to VXLAN
tunnel in ASIC.
- Add the tunnel name map to counter table, so that the ‘show mac’
command can display the FDB learnt from VXLAN tunnel.
- Send command to Linux kernel to create L2 VXLAN tunnel interface.

[fdborch]
* FDB MAC can be learnt from or set to VXLAN tunnel in ASIC

Signed-off-by: jianjun.dong <[email protected]>
  • Loading branch information
shine.chen committed Apr 30, 2019
1 parent ea4cba6 commit b8613f6
Show file tree
Hide file tree
Showing 3 changed files with 198 additions and 23 deletions.
113 changes: 91 additions & 22 deletions orchagent/fdborch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,18 @@
#include "crmorch.h"
#include "notifier.h"
#include "sai_serialize.h"
#include "directory.h"
#include "vxlanorch.h"
#include "swssnet.h"

using namespace swss;

extern sai_fdb_api_t *sai_fdb_api;

extern sai_object_id_t gSwitchId;
extern PortsOrch* gPortsOrch;
extern CrmOrch * gCrmOrch;
extern Directory<Orch*> gDirectory;

const int fdborch_pri = 20;

Expand Down Expand Up @@ -440,38 +446,68 @@ void FdbOrch::updateVlanMember(const VlanMemberUpdate& update)
bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, const string& type)
{
SWSS_LOG_ENTER();

#if 0
if (m_entries.count(entry) != 0) // we already have such entries
{
// FIXME: should we check that the entry are moving to another port?
// FIXME: should we check that the entry are changing its type?
SWSS_LOG_ERROR("FDB entry already exists. mac=%s bv_id=0x%lx", entry.mac.to_string().c_str(), entry.bv_id);
return true;
}

#endif
sai_fdb_entry_t fdb_entry;

fdb_entry.switch_id = gSwitchId;
memcpy(fdb_entry.mac_address, entry.mac.getMac(), sizeof(sai_mac_t));
fdb_entry.bv_id = entry.bv_id;

Port port;
/* Retry until port is created */
if (!m_portsOrch->getPort(port_name, port))
sai_object_id_t bridge_port_id = SAI_NULL_OBJECT_ID;
VxlanTunnelOrch* tunnel_orch = gDirectory.get<VxlanTunnelOrch*>();
VxlanTunnel *tunnel_obj = NULL;
if(strncmp(port_name.c_str(),"VTT",3) == 0)
{
SWSS_LOG_DEBUG("Saving a fdb entry until port %s becomes active", port_name.c_str());
saved_fdb_entries[port_name].push_back({entry, type});
/* Retry until tunnel is created */
if (!tunnel_orch->isTunnelExists(port_name))
{
SWSS_LOG_WARN("Vxlan tunnel '%s' doesn't exist", port_name.c_str());
return true;
}

return true;
}
tunnel_obj = tunnel_orch->getVxlanTunnel(port_name);

bridge_port_id = tunnel_obj->getBridgePortId();

/* Retry until port is added to the VLAN */
if (!port.m_bridge_port_id)
/* Retry until tunnel bridge port is created */
if (!bridge_port_id)
{
SWSS_LOG_WARN("Vxlan tunnel '%s' bridge port doesn't exist", port_name.c_str());
return true;
}
else
{
SWSS_LOG_NOTICE("Get tunnel bridge port id is %lu", bridge_port_id);
}
}
else
{
SWSS_LOG_DEBUG("Saving a fdb entry until port %s has got a bridge port ID", port_name.c_str());
saved_fdb_entries[port_name].push_back({entry, type});
/* Retry until port is created */
if (!m_portsOrch->getPort(port_name, port))
{
SWSS_LOG_DEBUG("Saving a fdb entry until port %s becomes active", port_name.c_str());
saved_fdb_entries[port_name].push_back({entry, type});

return true;
return true;
}

/* Retry until port is added to the VLAN */
if (!port.m_bridge_port_id)
{
SWSS_LOG_DEBUG("Saving a fdb entry until port %s has got a bridge port ID", port_name.c_str());
saved_fdb_entries[port_name].push_back({entry, type});

return true;
}
}

sai_attribute_t attr;
Expand All @@ -482,13 +518,36 @@ bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, const
attrs.push_back(attr);

attr.id = SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID;
attr.value.oid = port.m_bridge_port_id;
attrs.push_back(attr);
if(strncmp(port_name.c_str(),"VTT",3) != 0)
{
attr.value.oid = port.m_bridge_port_id;
attrs.push_back(attr);
}
else
{
attr.value.oid = bridge_port_id;
attrs.push_back(attr);

if(tunnel_obj)
{
attr.id = SAI_FDB_ENTRY_ATTR_ENDPOINT_IP;
swss::copy(attr.value.ipaddr, tunnel_obj->getDstIp());
attrs.push_back(attr);
char buf[INET6_ADDRSTRLEN];
inet_ntop(AF_INET, &attr.value.ipaddr.addr.ip4, buf, INET_ADDRSTRLEN);
SWSS_LOG_NOTICE("Create tunnel SAI_FDB_ENTRY_ATTR_ENDPOINT_IP %s, family %d", buf, attr.value.ipaddr.addr_family);
}
}

attr.id = SAI_FDB_ENTRY_ATTR_PACKET_ACTION;
attr.value.s32 = SAI_PACKET_ACTION_FORWARD;
attrs.push_back(attr);

if (m_entries.count(entry) != 0) // we already have such entries
{
removeFdbEntry(entry);
}

sai_status_t status = sai_fdb_api->create_fdb_entry(&fdb_entry, (uint32_t)attrs.size(), attrs.data());
if (status != SAI_STATUS_SUCCESS)
{
Expand All @@ -503,10 +562,14 @@ bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, const

gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_FDB_ENTRY);

FdbUpdate update = {entry, port, true};
for (auto observer: m_observers)
/*TBD: Vxlan tunnel is not in portlist, here skip observer, refine later*/
if(strncmp(port_name.c_str(),"VTT",3) != 0)
{
observer->update(SUBJECT_TYPE_FDB_CHANGE, &update);
FdbUpdate update = {entry, port, true};
for (auto observer: m_observers)
{
observer->update(SUBJECT_TYPE_FDB_CHANGE, &update);
}
}

return true;
Expand Down Expand Up @@ -541,12 +604,18 @@ bool FdbOrch::removeFdbEntry(const FdbEntry& entry)
gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_FDB_ENTRY);

Port port;
m_portsOrch->getPortByBridgePortId(entry.bv_id, port);
bool res = false;

FdbUpdate update = {entry, port, false};
for (auto observer: m_observers)
res = m_portsOrch->getPortByBridgePortId(entry.bv_id, port);

/*TBD: Vxlan tunnel is not in portlist, here skip observer, refine later*/
if(res == true)
{
observer->update(SUBJECT_TYPE_FDB_CHANGE, &update);
FdbUpdate update = {entry, port, false};
for (auto observer: m_observers)
{
observer->update(SUBJECT_TYPE_FDB_CHANGE, &update);
}
}

return true;
Expand Down
91 changes: 91 additions & 0 deletions orchagent/vxlanorch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@
#include "vxlanorch.h"
#include "directory.h"
#include "swssnet.h"
#include "exec.h"
#include "../cfgmgr/shellcmd.h"
#include "sai_serialize.h"

using namespace swss;

/* Global variables */
extern sai_object_id_t gSwitchId;
Expand All @@ -23,6 +28,8 @@ extern sai_next_hop_api_t *sai_next_hop_api;
extern Directory<Orch*> gDirectory;
extern PortsOrch* gPortsOrch;
extern sai_object_id_t gUnderlayIfId;
extern sai_bridge_api_t *sai_bridge_api;
extern sai_switch_api_t *sai_switch_api;

const map<MAP_T, uint32_t> vxlanTunnelMap =
{
Expand Down Expand Up @@ -328,6 +335,58 @@ create_tunnel_termination(
return term_table_id;
}

static sai_object_id_t create_tunnel_bridge_port(sai_object_id_t tunnel_oid)
{
SWSS_LOG_ENTER();

sai_attribute_t attr;
vector<sai_attribute_t> attrs;

vector<sai_attribute_t> attrs_bridge;
sai_object_id_t default1QBridge;
sai_status_t status;

attr.id = SAI_SWITCH_ATTR_DEFAULT_1Q_BRIDGE_ID;
attrs_bridge.push_back(attr);

status = sai_switch_api->get_switch_attribute(gSwitchId, (uint32_t)attrs_bridge.size(), attrs_bridge.data());
if (status != SAI_STATUS_SUCCESS)
{
SWSS_LOG_ERROR("Failed to get default 1Q bridge, rv:%d", status);
throw "PortsOrch initialization failure";
}

default1QBridge = attrs_bridge[0].value.oid;

attr.id = SAI_BRIDGE_PORT_ATTR_TYPE;
attr.value.s32 = SAI_BRIDGE_PORT_TYPE_TUNNEL;
attrs.push_back(attr);

attr.id = SAI_BRIDGE_PORT_ATTR_TUNNEL_ID;
attr.value.oid = tunnel_oid;
attrs.push_back(attr);

attr.id = SAI_BRIDGE_PORT_ATTR_BRIDGE_ID;
attr.value.oid = default1QBridge;
attrs.push_back(attr);

/* Create a bridge port with admin status set to UP */
attr.id = SAI_BRIDGE_PORT_ATTR_ADMIN_STATE;
attr.value.booldata = true;
attrs.push_back(attr);

sai_object_id_t m_bridge_port_id;
status = sai_bridge_api->create_bridge_port(&m_bridge_port_id, gSwitchId, (uint32_t)attrs.size(), attrs.data());
if (status != SAI_STATUS_SUCCESS)
{
throw std::runtime_error("Failed to add tunnel bridge port");
}

SWSS_LOG_NOTICE("Add tunnel bridge port to default 1Q bridge, bridge port id is %lu", m_bridge_port_id);

return m_bridge_port_id;
}

bool VxlanTunnel::createTunnel(MAP_T encap, MAP_T decap)
{
try
Expand All @@ -350,6 +409,15 @@ bool VxlanTunnel::createTunnel(MAP_T encap, MAP_T decap)

ids_.tunnel_id = create_tunnel(ids_.tunnel_encap_id, ids_.tunnel_decap_id, ip, gUnderlayIfId);

shared_ptr<DBConnector> m_counter_db = shared_ptr<DBConnector>(new DBConnector(COUNTERS_DB, DBConnector::DEFAULT_UNIXSOCKET, 0));
unique_ptr<Table> m_counterTable = unique_ptr<Table>(new Table(m_counter_db.get(), COUNTERS_PORT_NAME_MAP));

/* Add tunnel name map to counter table */
FieldValueTuple tuple(tunnel_name_, sai_serialize_object_id(ids_.tunnel_id));
vector<FieldValueTuple> fields;
fields.push_back(tuple);
m_counterTable->set("", fields);

ip = nullptr;
if (!dst_ip_.isZero())
{
Expand All @@ -360,6 +428,9 @@ bool VxlanTunnel::createTunnel(MAP_T encap, MAP_T decap)
ids_.tunnel_term_id = create_tunnel_termination(ids_.tunnel_id, ips, ip, gVirtualRouterId);
active_ = true;
tunnel_map_ = { encap, decap };

ids_.tunnel_bridge_port_id = create_tunnel_bridge_port(ids_.tunnel_id);
SWSS_LOG_NOTICE("Create tunnel bridge port id is %lu", ids_.tunnel_bridge_port_id);
}
catch (const std::runtime_error& error)
{
Expand Down Expand Up @@ -731,6 +802,26 @@ bool VxlanTunnelMapOrch::addOperation(const Request& request)
SWSS_LOG_NOTICE("Vxlan tunnel map entry '%s' for tunnel '%s' was created",
tunnel_map_entry_name.c_str(), tunnel_name.c_str());

//ip link add <vxlan_dev_name> type vxlan id <vni> local <src_ip> remote <dst_ip> dstport 4789
//ip link set <vxlan_dev_name> master DOT1Q_BRIDGE_NAME
//bridge vlan add vid <vlan_id> dev <vxlan_dev_name>
//ip link set <vxlan_dev_name> up
IpAddress ips, ipd;
std::string vxlan_dev_name;
ips = tunnel_obj->getSrcIp();
ipd = tunnel_obj->getDstIp();
vxlan_dev_name = std::string("") + std::string(tunnel_name) + "-" + std::to_string(vni_id);
const std::string cmds = std::string("")
+ BASH_CMD + " -c \""
+ IP_CMD + " link add " + vxlan_dev_name + " type vxlan id " + std::to_string(vni_id)
+ " local " + ips.to_string().c_str() + (ipd.isZero() ? "" : (" remote " + ipd.to_string())) + " dstport 4789 " + " && "
+ IP_CMD + " link set " + vxlan_dev_name + " master Bridge " + " && "
+ BRIDGE_CMD + " vlan add vid " + std::to_string(vlan_id) + " dev " + vxlan_dev_name + " && "
+ IP_CMD + " link set " + vxlan_dev_name + " up " + "\"";

std::string res;
EXEC_WITH_ERROR_THROW(cmds, res);

return true;
}

Expand Down
17 changes: 16 additions & 1 deletion orchagent/vxlanorch.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ struct tunnel_ids_t
sai_object_id_t tunnel_decap_id;
sai_object_id_t tunnel_id;
sai_object_id_t tunnel_term_id;
sai_object_id_t tunnel_bridge_port_id;
};

struct nh_key_t
Expand Down Expand Up @@ -104,7 +105,21 @@ class VxlanTunnel
return ids_.tunnel_encap_id;
}

void updateNextHop(IpAddress& ipAddr, MacAddress macAddress, uint32_t vni, sai_object_id_t nhId);
sai_object_id_t getBridgePortId() const
{
return ids_.tunnel_bridge_port_id;
}

IpAddress getSrcIp() const
{
return src_ip_;
}

IpAddress getDstIp() const
{
return dst_ip_;
}

bool removeNextHop(IpAddress& ipAddr, MacAddress macAddress, uint32_t vni);
sai_object_id_t getNextHop(IpAddress& ipAddr, MacAddress macAddress, uint32_t vni) const;

Expand Down

0 comments on commit b8613f6

Please sign in to comment.