VOM: IP route ECMP support

Change-Id: Iede0c30aacfe7289f428062bb9540410097c40e2
Signed-off-by: Neale Ranns <nranns@cisco.com>
This commit is contained in:
Neale Ranns
2019-01-10 08:56:38 -08:00
committed by Dave Barach
parent 1bcad5cdb7
commit 6ad7231c00
4 changed files with 85 additions and 33 deletions

View File

@ -339,8 +339,9 @@ void
ip_route::sweep() ip_route::sweep()
{ {
if (m_hw) { if (m_hw) {
HW::enqueue( for (auto& p : m_paths)
new ip_route_cmds::delete_cmd(m_hw, m_rd->table_id(), m_prefix)); HW::enqueue(
new ip_route_cmds::delete_cmd(m_hw, m_rd->table_id(), m_prefix, p));
} }
HW::write(); HW::write();
} }
@ -349,8 +350,9 @@ void
ip_route::replay() ip_route::replay()
{ {
if (m_hw) { if (m_hw) {
HW::enqueue( for (auto& p : m_paths)
new ip_route_cmds::update_cmd(m_hw, m_rd->table_id(), m_prefix, m_paths)); HW::enqueue(
new ip_route_cmds::update_cmd(m_hw, m_rd->table_id(), m_prefix, p));
} }
} }
std::string std::string
@ -367,12 +369,37 @@ ip_route::to_string() const
void void
ip_route::update(const ip_route& r) ip_route::update(const ip_route& r)
{ {
/*
* create the table if it is not yet created
*/
if (rc_t::OK != m_hw.rc()) { if (rc_t::OK != m_hw.rc()) {
HW::enqueue( /*
new ip_route_cmds::update_cmd(m_hw, m_rd->table_id(), m_prefix, m_paths)); * route not yet installed. install each of the desired paths
*/
m_paths = r.m_paths;
for (auto& p : m_paths)
HW::enqueue(
new ip_route_cmds::update_cmd(m_hw, m_rd->table_id(), m_prefix, p));
} else {
/*
* add each path that is not installed yet and remove each that is no longer
* wanted
*/
path_list_t to_add;
set_difference(r.m_paths.begin(), r.m_paths.end(), m_paths.begin(),
m_paths.end(), std::inserter(to_add, to_add.begin()));
for (auto& p : to_add)
HW::enqueue(
new ip_route_cmds::update_cmd(m_hw, m_rd->table_id(), m_prefix, p));
path_list_t to_del;
set_difference(m_paths.begin(), m_paths.end(), r.m_paths.begin(),
r.m_paths.end(), std::inserter(to_del, to_del.begin()));
for (auto& p : to_del)
HW::enqueue(
new ip_route_cmds::delete_cmd(m_hw, m_rd->table_id(), m_prefix, p));
m_paths = r.m_paths;
} }
} }

View File

@ -25,20 +25,19 @@ namespace ip_route_cmds {
update_cmd::update_cmd(HW::item<bool>& item, update_cmd::update_cmd(HW::item<bool>& item,
table_id_t id, table_id_t id,
const prefix_t& prefix, const prefix_t& prefix,
const path_list_t& paths) const path& path)
: rpc_cmd(item) : rpc_cmd(item)
, m_id(id) , m_id(id)
, m_prefix(prefix) , m_prefix(prefix)
, m_paths(paths) , m_path(path)
{ {
// no multipath yet.
assert(paths.size() == 1);
} }
bool bool
update_cmd::operator==(const update_cmd& other) const update_cmd::operator==(const update_cmd& other) const
{ {
return ((m_prefix == other.m_prefix) && (m_id == other.m_id)); return ((m_prefix == other.m_prefix) && (m_id == other.m_id) &&
(m_path == other.m_path));
} }
rc_t rc_t
@ -54,9 +53,7 @@ update_cmd::issue(connection& con)
m_prefix.to_vpp(&payload.is_ipv6, payload.dst_address, m_prefix.to_vpp(&payload.is_ipv6, payload.dst_address,
&payload.dst_address_length); &payload.dst_address_length);
to_vpp(m_path, payload);
for (auto& p : m_paths)
to_vpp(p, payload);
VAPI_CALL(req.execute()); VAPI_CALL(req.execute());
@ -68,24 +65,27 @@ update_cmd::to_string() const
{ {
std::ostringstream s; std::ostringstream s;
s << "ip-route-create: " << m_hw_item.to_string() << " table-id:" << m_id s << "ip-route-create: " << m_hw_item.to_string() << " table-id:" << m_id
<< " prefix:" << m_prefix.to_string() << " paths:" << m_paths; << " prefix:" << m_prefix.to_string() << " paths:" << m_path.to_string();
return (s.str()); return (s.str());
} }
delete_cmd::delete_cmd(HW::item<bool>& item, delete_cmd::delete_cmd(HW::item<bool>& item,
table_id_t id, table_id_t id,
const prefix_t& prefix) const prefix_t& prefix,
const path& path)
: rpc_cmd(item) : rpc_cmd(item)
, m_id(id) , m_id(id)
, m_prefix(prefix) , m_prefix(prefix)
, m_path(path)
{ {
} }
bool bool
delete_cmd::operator==(const delete_cmd& other) const delete_cmd::operator==(const delete_cmd& other) const
{ {
return ((m_prefix == other.m_prefix) && (m_id == other.m_id)); return ((m_prefix == other.m_prefix) && (m_id == other.m_id) &&
(m_path == other.m_path));
} }
rc_t rc_t
@ -99,6 +99,7 @@ delete_cmd::issue(connection& con)
m_prefix.to_vpp(&payload.is_ipv6, payload.dst_address, m_prefix.to_vpp(&payload.is_ipv6, payload.dst_address,
&payload.dst_address_length); &payload.dst_address_length);
to_vpp(m_path, payload);
VAPI_CALL(req.execute()); VAPI_CALL(req.execute());
@ -113,7 +114,7 @@ delete_cmd::to_string() const
{ {
std::ostringstream s; std::ostringstream s;
s << "ip-route-delete: " << m_hw_item.to_string() << " id:" << m_id s << "ip-route-delete: " << m_hw_item.to_string() << " id:" << m_id
<< " prefix:" << m_prefix.to_string(); << " prefix:" << m_prefix.to_string() << " paths:" << m_path.to_string();
return (s.str()); return (s.str());
} }

View File

@ -38,7 +38,7 @@ public:
update_cmd(HW::item<bool>& item, update_cmd(HW::item<bool>& item,
table_id_t id, table_id_t id,
const prefix_t& prefix, const prefix_t& prefix,
const path_list_t& paths); const path& path);
/** /**
* Issue the command to VPP/HW * Issue the command to VPP/HW
@ -58,7 +58,7 @@ public:
private: private:
route::table_id_t m_id; route::table_id_t m_id;
prefix_t m_prefix; prefix_t m_prefix;
const path_list_t m_paths; const path m_path;
}; };
/** /**
@ -70,7 +70,10 @@ public:
/** /**
* Constructor * Constructor
*/ */
delete_cmd(HW::item<bool>& item, table_id_t id, const prefix_t& prefix); delete_cmd(HW::item<bool>& item,
table_id_t id,
const prefix_t& prefix,
const path& path);
/** /**
* Issue the command to VPP/HW * Issue the command to VPP/HW
@ -90,6 +93,7 @@ public:
private: private:
route::table_id_t m_id; route::table_id_t m_id;
prefix_t m_prefix; prefix_t m_prefix;
const path m_path;
}; };
/** /**

View File

@ -1732,23 +1732,39 @@ BOOST_AUTO_TEST_CASE(test_routing) {
* A route via interface 1 in the default table * A route via interface 1 in the default table
*/ */
route::prefix_t pfx_5("5.5.5.5", 32); route::prefix_t pfx_5("5.5.5.5", 32);
boost::asio::ip::address nh_9 = boost::asio::ip::address::from_string("10.10.10.9");
route::path *path_9 = new route::path(nh_9, itf1);
boost::asio::ip::address nh_10 = boost::asio::ip::address::from_string("10.10.10.11"); boost::asio::ip::address nh_10 = boost::asio::ip::address::from_string("10.10.10.11");
route::path *path_10 = new route::path(nh_10, itf1); route::path *path_10 = new route::path(nh_10, itf1);
route::ip_route *route_5 = new route::ip_route(pfx_5); route::ip_route *route_5 = new route::ip_route(pfx_5);
route_5->add(*path_10); route_5->add(*path_10);
route_5->add(*path_9);
HW::item<bool> hw_route_5(true, rc_t::OK); HW::item<bool> hw_route_5(true, rc_t::OK);
ADD_EXPECT(route::ip_route_cmds::update_cmd(hw_route_5, 0, pfx_5, {*path_10})); ADD_EXPECT(route::ip_route_cmds::update_cmd(hw_route_5, 0, pfx_5, *path_9));
ADD_EXPECT(route::ip_route_cmds::update_cmd(hw_route_5, 0, pfx_5, *path_10));
TRY_CHECK_RC(OM::write(ian, *route_5)); TRY_CHECK_RC(OM::write(ian, *route_5));
route_5->remove(*path_9);
ADD_EXPECT(route::ip_route_cmds::delete_cmd(hw_route_5, 0, pfx_5, *path_9));
TRY_CHECK_RC(OM::write(ian, *route_5));
delete path_9;
/* /*
* A route via interface 2 in the non-default table * A route via interface 2 in the non-default table
*/ */
boost::asio::ip::address nh_11 = boost::asio::ip::address::from_string("11.11.11.10"); boost::asio::ip::address nh_11 = boost::asio::ip::address::from_string("11.11.11.10");
route::path *path_11 = new route::path(nh_11, *itf2); route::path *path_11 = new route::path(nh_11, *itf2);
boost::asio::ip::address nh_12 = boost::asio::ip::address::from_string("11.11.11.12");
route::path *path_12 = new route::path(nh_12, *itf2);
route::ip_route *route_5_2 = new route::ip_route(rd4, pfx_5); route::ip_route *route_5_2 = new route::ip_route(rd4, pfx_5);
route_5_2->add(*path_11); route_5_2->add(*path_11);
HW::item<bool> hw_route_5_2(true, rc_t::OK); HW::item<bool> hw_route_5_2(true, rc_t::OK);
ADD_EXPECT(route::ip_route_cmds::update_cmd(hw_route_5_2, 1, pfx_5, {*path_11})); ADD_EXPECT(route::ip_route_cmds::update_cmd(hw_route_5_2, 1, pfx_5, *path_11));
TRY_CHECK_RC(OM::write(ian, *route_5_2));
route_5_2->add(*path_12);
ADD_EXPECT(route::ip_route_cmds::update_cmd(hw_route_5_2, 1, pfx_5, *path_12));
TRY_CHECK_RC(OM::write(ian, *route_5_2)); TRY_CHECK_RC(OM::write(ian, *route_5_2));
/* /*
@ -1768,7 +1784,7 @@ BOOST_AUTO_TEST_CASE(test_routing) {
route::ip_route *route_dvr = new route::ip_route(pfx_6); route::ip_route *route_dvr = new route::ip_route(pfx_6);
route_dvr->add(*path_l2); route_dvr->add(*path_l2);
HW::item<bool> hw_route_dvr(true, rc_t::OK); HW::item<bool> hw_route_dvr(true, rc_t::OK);
ADD_EXPECT(route::ip_route_cmds::update_cmd(hw_route_dvr, 0, pfx_6, {*path_l2})); ADD_EXPECT(route::ip_route_cmds::update_cmd(hw_route_dvr, 0, pfx_6, *path_l2));
TRY_CHECK_RC(OM::write(ian, *route_dvr)); TRY_CHECK_RC(OM::write(ian, *route_dvr));
/* /*
@ -1798,11 +1814,8 @@ BOOST_AUTO_TEST_CASE(test_routing) {
delete l3_10; delete l3_10;
delete itf2; delete itf2;
delete route_5; delete route_5;
delete path_10;
delete route_5_2; delete route_5_2;
delete path_11;
delete route_dvr; delete route_dvr;
delete path_l2;
delete ne; delete ne;
delete mroute_4; delete mroute_4;
@ -1817,9 +1830,16 @@ BOOST_AUTO_TEST_CASE(test_routing) {
delete mp2; delete mp2;
ADD_EXPECT(neighbour_cmds::delete_cmd(hw_neighbour, hw_ifh.data(), mac_n, nh_10)); ADD_EXPECT(neighbour_cmds::delete_cmd(hw_neighbour, hw_ifh.data(), mac_n, nh_10));
ADD_EXPECT(route::ip_route_cmds::delete_cmd(hw_route_dvr, 0, pfx_6)); ADD_EXPECT(route::ip_route_cmds::delete_cmd(hw_route_dvr, 0, pfx_6, *path_l2));
ADD_EXPECT(route::ip_route_cmds::delete_cmd(hw_route_5_2, 1, pfx_5)); ADD_EXPECT(route::ip_route_cmds::delete_cmd(hw_route_5_2, 1, pfx_5, *path_11));
ADD_EXPECT(route::ip_route_cmds::delete_cmd(hw_route_5, 0, pfx_5)); ADD_EXPECT(route::ip_route_cmds::delete_cmd(hw_route_5_2, 1, pfx_5, *path_12));
ADD_EXPECT(route::ip_route_cmds::delete_cmd(hw_route_5, 0, pfx_5, *path_10));
delete path_10;
delete path_11;
delete path_12;
delete path_l2;
ADD_EXPECT(l3_binding_cmds::unbind_cmd(hw_l3_10_unbind, hw_ifh.data(), pfx_10)); ADD_EXPECT(l3_binding_cmds::unbind_cmd(hw_l3_10_unbind, hw_ifh.data(), pfx_10));
ADD_EXPECT(l3_binding_cmds::unbind_cmd(hw_l3_11_unbind, hw_ifh2.data(), pfx_11)); ADD_EXPECT(l3_binding_cmds::unbind_cmd(hw_l3_11_unbind, hw_ifh2.data(), pfx_11));
ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_down, hw_ifh)); ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_down, hw_ifh));