Skip to content
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions cfgmgr/intfmgr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,7 @@ bool IntfMgr::doIntfGeneralTask(const vector<string>& keys,
string grat_arp = "";
string mpls = "";
string ipv6_link_local_mode = "";
string loopback_action = "";

for (auto idx : data)
{
Expand Down Expand Up @@ -750,6 +751,10 @@ bool IntfMgr::doIntfGeneralTask(const vector<string>& keys,
{
vlanId = value;
}
else if (field == "loopback_action")
{
loopback_action = value;
}
}

if (op == SET_COMMAND)
Expand Down Expand Up @@ -791,6 +796,13 @@ bool IntfMgr::doIntfGeneralTask(const vector<string>& keys,
data.push_back(fvTuple);
}

/* Set loopback action */
if (!loopback_action.empty())
{
FieldValueTuple fvTuple("loopback_action", loopback_action);
data.push_back(fvTuple);
}

/* Set mpls */
if (!setIntfMpls(alias, mpls))
{
Expand Down
104 changes: 102 additions & 2 deletions orchagent/intfsorch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,21 @@ static const vector<sai_router_interface_stat_t> rifStatIds =
SAI_ROUTER_INTERFACE_STAT_OUT_ERROR_OCTETS,
};

// Translation of loopback action from string to sai type
const unordered_map<string, loopback_action_e> IntfsOrch::m_loopback_action_map =
{
{"none", LOOPBACK_ACTION_NONE},
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove "none" if it is not one of the available options for the user. I believe LOOPBACK_ACTION_NONE is used only internally within orchagent during intf creation.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

{"drop", LOOPBACK_ACTION_DROP},
{"forward", LOOPBACK_ACTION_FORWARD},
};

// Translation of loopback action from sonic to sai type
const unordered_map<loopback_action_e, sai_packet_action_t> IntfsOrch::m_sai_loopback_action_map =
{
{LOOPBACK_ACTION_DROP, SAI_PACKET_ACTION_DROP},
{LOOPBACK_ACTION_FORWARD, SAI_PACKET_ACTION_FORWARD},
};

IntfsOrch::IntfsOrch(DBConnector *db, string tableName, VRFOrch *vrf_orch, DBConnector *chassisAppDb) :
Orch(db, tableName, intfsorch_pri), m_vrfOrch(vrf_orch)
{
Expand Down Expand Up @@ -416,6 +431,62 @@ bool IntfsOrch::setIntfProxyArp(const string &alias, const string &proxy_arp)
return true;
}

bool IntfsOrch::setIntfLoopbackAction(const Port &port)
{
sai_attribute_t attr;
attr.id = SAI_ROUTER_INTERFACE_ATTR_LOOPBACK_PACKET_ACTION;
attr.value.s32 = m_sai_loopback_action_map.at(port.m_loopback_action);

string action_str = getIntfLoopbackActionStr(port.m_loopback_action);

sai_status_t status = sai_router_intfs_api->set_router_interface_attribute(port.m_rif_id, &attr);
if (status != SAI_STATUS_SUCCESS)
{
SWSS_LOG_ERROR("Loopback action [%s] set failed, interface [%s], rc [%d]",
action_str.c_str(), port.m_alias.c_str(), status);

task_process_status handle_status = handleSaiSetStatus(SAI_API_ROUTER_INTERFACE, status);
if (handle_status != task_success)
{
return parseHandleSaiStatusFailure(handle_status);
}
}

SWSS_LOG_NOTICE("Loopback action [%s] set success, interface [%s]",
action_str.c_str(), port.m_alias.c_str());
return true;
}

bool IntfsOrch::getIntfLoopbackAction(const std::string &actionStr, loopback_action_e &action)
{
auto it = m_loopback_action_map.find(actionStr);
if (it != m_loopback_action_map.end())
{
action = m_loopback_action_map.at(actionStr);
return true;
}
else
{
SWSS_LOG_WARN("Unsupported loopback action [%s]", actionStr.c_str());
return false;
}
}

string IntfsOrch::getIntfLoopbackActionStr(loopback_action_e action)
{
for (auto it = m_loopback_action_map.begin(); it != m_loopback_action_map.end(); ++it)
{
if (it->second == action)
{
return it->first;
}
}

SWSS_LOG_WARN("Failed to fetch action as string for action [%u]", action);
return string();
}


set<IpPrefix> IntfsOrch:: getSubnetRoutes()
{
SWSS_LOG_ENTER();
Expand All @@ -433,12 +504,15 @@ set<IpPrefix> IntfsOrch:: getSubnetRoutes()
return subnet_routes;
}

bool IntfsOrch::setIntf(const string& alias, sai_object_id_t vrf_id, const IpPrefix *ip_prefix, const bool adminUp, const uint32_t mtu)
bool IntfsOrch::setIntf(const string& alias, sai_object_id_t vrf_id, const IpPrefix *ip_prefix,
const bool adminUp, const uint32_t mtu, loopback_action_e loopbackAction)

{
SWSS_LOG_ENTER();

Port port;
gPortsOrch->getPort(alias, port);
port.m_loopback_action = loopbackAction;

auto it_intfs = m_syncdIntfses.find(alias);
if (it_intfs == m_syncdIntfses.end())
Expand Down Expand Up @@ -665,6 +739,7 @@ void IntfsOrch::doTask(Consumer &consumer)
string inband_type = "";
bool mpls = false;
string vlan = "";
loopback_action_e loopbackAction = LOOPBACK_ACTION_NONE;

for (auto idx : data)
{
Expand Down Expand Up @@ -757,6 +832,13 @@ void IntfsOrch::doTask(Consumer &consumer)
{
vlan = value;
}
else if (field == "loopback_action")
{
if(!getIntfLoopbackAction(value, loopbackAction))
{
continue;
}
}
}

if (alias == "eth0" || alias == "docker0")
Expand Down Expand Up @@ -874,7 +956,8 @@ void IntfsOrch::doTask(Consumer &consumer)
{
adminUp = port.m_admin_state_up;
}
if (!setIntf(alias, vrf_id, ip_prefix_in_key ? &ip_prefix : nullptr, adminUp, mtu))

if (!setIntf(alias, vrf_id, ip_prefix_in_key ? &ip_prefix : nullptr, adminUp, mtu, loopbackAction))
{
it++;
continue;
Expand Down Expand Up @@ -906,6 +989,16 @@ void IntfsOrch::doTask(Consumer &consumer)
setRouterIntfsMpls(port);
gPortsOrch->setPort(alias, port);
}

/* Set loopback action */
if ((loopbackAction != LOOPBACK_ACTION_NONE) and (port.m_loopback_action != loopbackAction))
{
port.m_loopback_action = loopbackAction;
if(setIntfLoopbackAction(port))
{
gPortsOrch->setPort(alias, port);
}
}
}
}

Expand Down Expand Up @@ -1067,6 +1160,13 @@ bool IntfsOrch::addRouterIntfs(sai_object_id_t vrf_id, Port &port)
attr.value.oid = vrf_id;
attrs.push_back(attr);

if(port.m_loopback_action != LOOPBACK_ACTION_NONE)
{
attr.id = SAI_ROUTER_INTERFACE_ATTR_LOOPBACK_PACKET_ACTION;
attr.value.s32 = m_sai_loopback_action_map.at(port.m_loopback_action);
attrs.push_back(attr);
}

attr.id = SAI_ROUTER_INTERFACE_ATTR_SRC_MAC_ADDRESS;
if (port.m_mac)
{
Expand Down
7 changes: 6 additions & 1 deletion orchagent/intfsorch.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ class IntfsOrch : public Orch
void addRifToFlexCounter(const string&, const string&, const string&);
void removeRifFromFlexCounter(const string&, const string&);

bool setIntf(const string& alias, sai_object_id_t vrf_id = gVirtualRouterId, const IpPrefix *ip_prefix = nullptr, const bool adminUp = true, const uint32_t mtu = 0);
bool setIntfLoopbackAction(const Port &port);
bool getIntfLoopbackAction(const std::string &action_str, loopback_action_e &action);
string getIntfLoopbackActionStr(loopback_action_e action);
bool setIntf(const string& alias, sai_object_id_t vrf_id = gVirtualRouterId, const IpPrefix *ip_prefix = nullptr, const bool adminUp = true, const uint32_t mtu = 0, loopback_action_e loopbackAction = LOOPBACK_ACTION_NONE);
bool removeIntf(const string& alias, sai_object_id_t vrf_id = gVirtualRouterId, const IpPrefix *ip_prefix = nullptr);

void addIp2MeRoute(sai_object_id_t vrf_id, const IpPrefix &ip_prefix);
Expand Down Expand Up @@ -88,6 +91,8 @@ class IntfsOrch : public Orch
unique_ptr<Table> m_vidToRidTable;
unique_ptr<ProducerTable> m_flexCounterTable;
unique_ptr<ProducerTable> m_flexCounterGroupTable;
static const unordered_map<string, loopback_action_e> m_loopback_action_map;
static const unordered_map<loopback_action_e, sai_packet_action_t> m_sai_loopback_action_map;

std::string getRifFlexCounterTableKey(std::string s);

Expand Down
8 changes: 8 additions & 0 deletions orchagent/port.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,13 @@ struct SystemLagInfo
int32_t spa_id = 0;
};

typedef enum loopback_action
{
LOOPBACK_ACTION_NONE,
LOOPBACK_ACTION_DROP,
LOOPBACK_ACTION_FORWARD,
} loopback_action_e;

class Port
{
public:
Expand Down Expand Up @@ -107,6 +114,7 @@ class Port
}

std::string m_alias;
loopback_action_e m_loopback_action;
Type m_type;
int m_index = 0; // PHY_PORT: index
uint32_t m_mtu = DEFAULT_MTU;
Expand Down
78 changes: 78 additions & 0 deletions tests/test_interface.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import time
import json
import pytest
import random

from swsscommon import swsscommon

VLAN_SUB_INTERFACE_SEPARATOR = '.'

class TestRouterInterface(object):
def setup_db(self, dvs):
self.pdb = swsscommon.DBConnector(0, dvs.redis_sock, 0)
Expand Down Expand Up @@ -2193,6 +2196,81 @@ def test_VLanInterfaceIpv6LinkLocalOnly(self, dvs, testlog):
# one loopback router interface
assert len(intf_entries) == 1

def set_loopback_action(self, interface, action):
if interface.startswith("PortChannel"):
tbl_name = "PORTCHANNEL_INTERFACE"
elif interface.startswith("Vlan"):
tbl_name = "VLAN_INTERFACE"
else:
sub_intf_sep_idx = interface.find(VLAN_SUB_INTERFACE_SEPARATOR)
if sub_intf_sep_idx != -1:
tbl_name = "VLAN_SUB_INTERFACE"
else:
tbl_name = "INTERFACE"

fvs = swsscommon.FieldValuePairs([("loopback_action", action)])
tbl = swsscommon.Table(self.cdb, tbl_name)
tbl.set(interface, fvs)
time.sleep(1)

def loopback_action_test(self, iface):
# create interface
self.create_l3_intf(iface, "")

# set interface loopback action in config database
action_map = {"drop": "SAI_PACKET_ACTION_DROP", "forward": "SAI_PACKET_ACTION_FORWARD"}
action = random.choice(list(action_map.keys()))
self.set_loopback_action(iface, action)

# check application database
tbl = swsscommon.Table(self.pdb, "INTF_TABLE")
(status, fvs) = tbl.get(iface)
assert status == True

action_found = False
for fv in fvs:
if fv[0] == "loopback_action":
action_found = True
assert fv[1] == action
assert action_found == True

# check asic database
tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE")
intf_entries = tbl.getKeys()

action_found = False
for key in intf_entries:
(status, fvs) = tbl.get(key)
assert status == True

for fv in fvs:
if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_LOOPBACK_PACKET_ACTION":
action_found = True
assert fv[1] == action_map[action]
assert action_found == True

# remove interface
self.remove_l3_intf(iface)

def test_interfaceLoopbackAction(self, dvs, testlog):
self.setup_db(dvs)
self.loopback_action_test("Ethernet8")

def test_subInterfaceLoopbackAction(self, dvs, testlog):
self.setup_db(dvs)
self.loopback_action_test("Ethernet8.1")

def test_vlanInterfaceLoopbackAction(self, dvs, testlog):
self.setup_db(dvs)
self.create_vlan("10")
self.loopback_action_test("Vlan10")
self.remove_vlan("10")

def test_portChannelInterfaceLoopbackAction(self, dvs, testlog):
self.setup_db(dvs)
self.create_port_channel("PortChannel009")
self.loopback_action_test("PortChannel009")
self.remove_port_channel("PortChannel009")

# Add Dummy always-pass test at end as workaroud
# for issue when Flaky fail on final test it invokes module tear-down before retrying
Expand Down