FD.io VPP  v19.08.3-2-gbabecb413
Vector Packet Processing
route.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2017 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "vom/route.hpp"
17 #include "vom/api_types.hpp"
18 #include "vom/mroute_cmds.hpp"
19 #include "vom/route_api_types.hpp"
20 #include "vom/route_cmds.hpp"
22 
23 namespace VOM {
24 namespace route {
25 ip_route::event_handler ip_route::m_evh;
26 ip_mroute::event_handler ip_mroute::m_evh;
27 singular_db<ip_route::key_t, ip_route> ip_route::m_db;
28 singular_db<ip_mroute::key_t, ip_mroute> ip_mroute::m_db;
29 
30 const path::special_t path::special_t::STANDARD(0, "standard");
31 const path::special_t path::special_t::LOCAL(1, "local");
32 const path::special_t path::special_t::DROP(2, "standard");
33 const path::special_t path::special_t::UNREACH(3, "unreachable");
34 const path::special_t path::special_t::PROHIBIT(4, "prohibit");
35 
36 path::special_t::special_t(int v, const std::string& s)
37  : enum_base<path::special_t>(v, s)
38 {
39 }
40 
41 const path::flags_t path::flags_t::NONE(0, "none");
42 const path::flags_t path::flags_t::DVR((1 << 0), "dvr");
43 
44 path::flags_t::flags_t(int v, const std::string& s)
45  : enum_base<path::flags_t>(v, s)
46 {
47 }
48 
49 const itf_flags_t itf_flags_t::NONE(0, "none");
50 const itf_flags_t itf_flags_t::ACCEPT((1 << 1), "accept");
51 const itf_flags_t itf_flags_t::FORWARD((1 << 2), "forward");
52 
53 itf_flags_t::itf_flags_t(int v, const std::string& s)
54  : enum_base<itf_flags_t>(v, s)
55 {
56 }
57 
58 path::path(special_t special, const nh_proto_t& proto)
59  : m_type(special)
60  , m_nh_proto(proto)
61  , m_flags(flags_t::NONE)
62  , m_nh()
63  , m_rd(nullptr)
64  , m_interface(nullptr)
65  , m_weight(1)
66  , m_preference(0)
67 {
68 }
69 
71  const interface& interface,
72  uint8_t weight,
73  uint8_t preference)
74  : m_type(special_t::STANDARD)
75  , m_nh_proto(nh_proto_t::from_address(nh))
76  , m_flags(flags_t::NONE)
77  , m_nh(nh)
78  , m_rd(nullptr)
79  , m_interface(interface.singular())
80  , m_weight(weight)
81  , m_preference(preference)
82 {
83 }
84 
87  uint8_t weight,
88  uint8_t preference)
89  : m_type(special_t::STANDARD)
90  , m_nh_proto(nh_proto_t::from_address(nh))
91  , m_flags(flags_t::NONE)
92  , m_nh(nh)
93  , m_rd(rd.singular())
94  , m_interface(nullptr)
95  , m_weight(weight)
96  , m_preference(preference)
97 {
98 }
99 
101  const nh_proto_t& proto,
102  const flags_t& flags,
103  uint8_t weight,
104  uint8_t preference)
105  : m_type(special_t::STANDARD)
106  , m_nh_proto(proto)
107  , m_flags(flags)
108  , m_nh()
109  , m_rd(nullptr)
110  , m_interface(interface.singular())
111  , m_weight(weight)
112  , m_preference(preference)
113 {
114 }
115 
116 path::path(const path& p)
117  : m_type(p.m_type)
118  , m_nh_proto(p.m_nh_proto)
119  , m_flags(p.m_flags)
120  , m_nh(p.m_nh)
121  , m_rd(p.m_rd)
122  , m_interface(p.m_interface)
123  , m_weight(p.m_weight)
124  , m_preference(p.m_preference)
125 {
126 }
127 
128 bool
129 path::operator<(const path& p) const
130 {
131  if (m_nh_proto < p.m_nh_proto)
132  return true;
133  if (m_flags < p.m_flags)
134  return true;
135  if (m_type < p.m_type)
136  return true;
137  if (m_rd && !p.m_rd)
138  return false;
139  if (!m_rd && p.m_rd)
140  return true;
141  if (m_rd && p.m_rd) {
142  if (m_rd->table_id() < p.m_rd->table_id())
143  return true;
144  else if (m_rd->table_id() > p.m_rd->table_id())
145  return false;
146  }
147  if (m_nh < p.m_nh)
148  return true;
149  if (m_interface && !p.m_interface)
150  return false;
151  if (!m_interface && p.m_interface)
152  return true;
153  if (m_interface && p.m_interface) {
154  if (m_interface->handle() < p.m_interface->handle())
155  return true;
156  if (p.m_interface->handle() < m_interface->handle())
157  return false;
158  }
159 
160  return (false);
161 }
162 
164 {
165 }
166 
167 bool
168 path::operator==(const path& p) const
169 {
170  bool result = true;
171  if (m_rd && !p.m_rd)
172  return false;
173  if (!m_rd && p.m_rd)
174  return false;
175  if (m_rd && p.m_rd)
176  result &= (*m_rd == *p.m_rd);
177  if (m_interface && !p.m_interface)
178  return false;
179  if (!m_interface && p.m_interface)
180  return false;
181  if (m_interface && p.m_interface)
182  result &= (*m_interface == *p.m_interface);
183  return (result && (m_type == p.m_type) && (m_nh == p.m_nh) &&
184  (m_nh_proto == p.m_nh_proto) && (m_flags == p.m_flags));
185 }
186 
187 std::string
189 {
190  std::ostringstream s;
191 
192  s << "path:["
193  << "type:" << m_type.to_string() << " proto:" << m_nh_proto.to_string()
194  << " flags:" << m_flags.to_string() << " neighbour:" << m_nh.to_string();
195  if (m_rd) {
196  s << " " << m_rd->to_string();
197  }
198  if (m_interface) {
199  s << " " << m_interface->to_string();
200  }
201  s << " weight:" << static_cast<int>(m_weight)
202  << " preference:" << static_cast<int>(m_preference) << "]";
203 
204  return (s.str());
205 }
206 
208 path::type() const
209 {
210  return m_type;
211 }
212 
215 {
216  return m_nh_proto;
217 }
218 
220 path::flags() const
221 {
222  return m_flags;
223 }
224 
226 path::nh() const
227 {
228  return m_nh;
229 }
230 
231 std::shared_ptr<route_domain>
232 path::rd() const
233 {
234  return m_rd;
235 }
236 
237 std::shared_ptr<interface>
238 path::itf() const
239 {
240  return m_interface;
241 }
242 
243 uint8_t
245 {
246  return m_weight;
247 }
248 
249 uint8_t
251 {
252  return m_preference;
253 }
254 
256  : m_hw(false)
257  , m_rd(route_domain::get_default())
258  , m_prefix(prefix)
259  , m_paths({ p })
260 {
261 }
262 
264  : m_hw(false)
265  , m_rd(route_domain::get_default())
266  , m_prefix(prefix)
267  , m_paths()
268 {
269 }
270 
272  : m_hw(r.m_hw)
273  , m_rd(r.m_rd)
274  , m_prefix(r.m_prefix)
275  , m_paths(r.m_paths)
276 {
277 }
278 
280  : m_hw(false)
281  , m_rd(rd.singular())
282  , m_prefix(prefix)
283  , m_paths()
284 {
285 }
286 
288  const prefix_t& prefix,
289  const path& p)
290  : m_hw(false)
291  , m_rd(rd.singular())
292  , m_prefix(prefix)
293  , m_paths({ p })
294 {
295 }
296 
298 {
299  sweep();
300 
301  // not in the DB anymore.
302  m_db.release(key(), this);
303  m_paths.clear();
304 }
305 
306 const ip_route::key_t
308 {
309  return (std::make_pair(m_rd->table_id(), m_prefix));
310 }
311 
312 bool
314 {
315  return ((key() == i.key()) && (m_paths == i.m_paths));
316 }
317 
318 void
320 {
321  m_paths.insert(path);
322 }
323 
324 void
326 {
327  m_paths.erase(path);
328 }
329 
330 void
331 ip_route::sweep()
332 {
333  if (m_hw) {
334  HW::enqueue(
335  new ip_route_cmds::delete_cmd(m_hw, m_rd->table_id(), m_prefix));
336  }
337  HW::write();
338 }
339 
340 void
342 {
343  if (m_hw) {
344  HW::enqueue(
345  new ip_route_cmds::update_cmd(m_hw, m_rd->table_id(), m_prefix, m_paths));
346  }
347 }
348 std::string
350 {
351  std::ostringstream s;
352  s << "route:[" << m_rd->to_string() << ", " << m_prefix.to_string() << " ["
353  << m_paths << "]"
354  << "]";
355 
356  return (s.str());
357 }
358 
359 void
360 ip_route::update(const ip_route& r)
361 {
362  m_paths = r.m_paths;
363  HW::enqueue(
364  new ip_route_cmds::update_cmd(m_hw, m_rd->table_id(), m_prefix, m_paths));
365 }
366 
367 std::shared_ptr<ip_route>
368 ip_route::find_or_add(const ip_route& temp)
369 {
370  return (m_db.find_or_add(temp.key(), temp));
371 }
372 
373 std::shared_ptr<ip_route>
375 {
376  return (m_db.find(k));
377 }
378 
379 std::shared_ptr<ip_route>
381 {
382  return find_or_add(*this);
383 }
384 
385 void
386 ip_route::dump(std::ostream& os)
387 {
388  db_dump(m_db, os);
389 }
390 
392 {
393  OM::register_listener(this);
394  inspect::register_handler({ "ip-route" }, "ip route configurations", this);
395 }
396 
397 void
398 ip_route::event_handler::handle_replay()
399 {
400  m_db.replay();
401 }
402 
403 void
404 ip_route::event_handler::handle_populate(const client_db::key_t& key)
405 {
406  // for each known route-domain
407  auto it = route_domain::cbegin();
408 
409  while (it != route_domain::cend()) {
410 
411  std::vector<l3_proto_t> l3s = { l3_proto_t::IPV4, l3_proto_t::IPV4 };
412 
413  for (auto l3 : l3s) {
414  std::shared_ptr<ip_route_cmds::dump_cmd> cmd =
415  std::make_shared<ip_route_cmds::dump_cmd>(it->second.lock()->table_id(),
416  l3);
417 
418  HW::enqueue(cmd);
419  HW::write();
420 
421  for (auto& record : *cmd) {
422  auto& payload = record.get_payload();
423 
424  std::shared_ptr<route_domain> rd =
425  route_domain::find(payload.route.table_id);
426 
427  if (!rd) {
428  continue;
429  }
430 
431  prefix_t pfx = from_api(payload.route.prefix);
432  ip_route ip_r(*rd, pfx);
433 
434  for (unsigned int i = 0; i < payload.route.n_paths; i++) {
435  ip_r.add(from_api(payload.route.paths[i]));
436  }
437 
438  VOM_LOG(log_level_t::DEBUG) << "ip-route-dump: " << ip_r.to_string();
439 
440  /*
441  * Write each of the discovered interfaces into the OM,
442  * but disable the HW Command q whilst we do, so that no
443  * commands are sent to VPP
444  */
445  OM::commit(key, ip_r);
446  }
447  }
448  ++it;
449  }
450 }
451 
453 ip_route::event_handler::order() const
454 {
455  return (dependency_t::TABLE);
456 }
457 
458 void
459 ip_route::event_handler::show(std::ostream& os)
460 {
461  db_dump(m_db, os);
462 }
463 
465  : m_hw(false)
466  , m_rd(route_domain::get_default())
467  , m_mprefix(mprefix)
468  , m_paths()
469 {
470 }
471 
473  : m_hw(r.m_hw)
474  , m_rd(r.m_rd)
475  , m_mprefix(r.m_mprefix)
476  , m_paths(r.m_paths)
477 {
478 }
479 
481  : m_hw(false)
482  , m_rd(rd.singular())
483  , m_mprefix(mprefix)
484  , m_paths()
485 {
486 }
487 
488 void
489 ip_mroute::add(const path& path, const itf_flags_t& flag)
490 {
491  m_paths.insert(std::make_pair(path, flag));
492 }
493 
495 {
496  sweep();
497  m_db.release(key(), this);
498 }
499 
500 const ip_mroute::key_t
502 {
503  return (std::make_pair(m_rd->table_id(), m_mprefix));
504 }
505 
506 bool
508 {
509  return ((key() == i.key()) && (m_paths == i.m_paths));
510 }
511 
512 void
513 ip_mroute::sweep()
514 {
515  if (m_hw) {
516  for (auto& p : m_paths)
517  HW::enqueue(new ip_mroute_cmds::delete_cmd(m_hw, m_rd->table_id(),
518  m_mprefix, p.first, p.second));
519  }
520  HW::write();
521 }
522 
523 void
525 {
526  if (m_hw) {
527  for (auto& p : m_paths)
528  HW::enqueue(new ip_mroute_cmds::update_cmd(m_hw, m_rd->table_id(),
529  m_mprefix, p.first, p.second));
530  }
531 }
532 std::string
534 {
535  std::ostringstream s;
536  s << "route:[" << m_rd->to_string() << ", " << m_mprefix.to_string() << " ["
537  << m_paths << "]"
538  << "]";
539 
540  return (s.str());
541 }
542 
543 void
544 ip_mroute::update(const ip_mroute& r)
545 {
546  if (rc_t::OK != m_hw.rc()) {
547  for (auto& p : m_paths)
548  HW::enqueue(new ip_mroute_cmds::update_cmd(m_hw, m_rd->table_id(),
549  m_mprefix, p.first, p.second));
550  }
551 }
552 
553 std::shared_ptr<ip_mroute>
554 ip_mroute::find_or_add(const ip_mroute& temp)
555 {
556  return (m_db.find_or_add(temp.key(), temp));
557 }
558 
559 std::shared_ptr<ip_mroute>
561 {
562  return (m_db.find(k));
563 }
564 
565 std::shared_ptr<ip_mroute>
567 {
568  return find_or_add(*this);
569 }
570 
571 void
572 ip_mroute::dump(std::ostream& os)
573 {
574  db_dump(m_db, os);
575 }
576 
578 {
579  OM::register_listener(this);
580  inspect::register_handler({ "ip-mroute" },
581  "ip multicast route configurations", this);
582 }
583 
584 void
585 ip_mroute::event_handler::handle_replay()
586 {
587  m_db.replay();
588 }
589 
590 void
591 ip_mroute::event_handler::handle_populate(const client_db::key_t& key)
592 {
593  // for each known route-domain
594  auto it = route_domain::cbegin();
595 
596  while (it != route_domain::cend()) {
597 
598  std::vector<l3_proto_t> l3s = { l3_proto_t::IPV4, l3_proto_t::IPV4 };
599 
600  for (auto l3 : l3s) {
601  std::shared_ptr<ip_mroute_cmds::dump_cmd> cmd =
602  std::make_shared<ip_mroute_cmds::dump_cmd>(
603  it->second.lock()->table_id(), l3);
604 
605  HW::enqueue(cmd);
606  HW::write();
607 
608  VOM_LOG(log_level_t::DEBUG) << "ip-mroute-dump: ";
609 
610  for (auto& record : *cmd) {
611  auto& payload = record.get_payload();
612 
613  std::shared_ptr<route_domain> rd =
614  route_domain::find(payload.route.table_id);
615 
616  if (!rd) {
617  continue;
618  }
619 
620  mprefix_t pfx = from_api(payload.route.prefix);
621  ip_mroute ip_r(*rd, pfx);
622 
623  for (unsigned int i = 0; i < payload.route.n_paths; i++) {
624  ip_r.add(from_api(payload.route.paths[i].path),
625  from_api(payload.route.paths[i].itf_flags));
626  }
627 
628  VOM_LOG(log_level_t::DEBUG) << "ip-mroute-dump: " << ip_r.to_string();
629 
630  /*
631  * Write each of the discovered interfaces into the OM,
632  * but disable the HW Command q whilst we do, so that no
633  * commands are sent to VPP
634  */
635  OM::commit(key, ip_r);
636  }
637  }
638  ++it;
639  }
640 }
641 
643 ip_mroute::event_handler::order() const
644 {
645  return (dependency_t::TABLE);
646 }
647 
648 void
649 ip_mroute::event_handler::show(std::ostream& os)
650 {
651  db_dump(m_db, os);
652 }
653 
654 std::ostream&
655 operator<<(std::ostream& os, const ip_route::key_t& key)
656 {
657  os << "[" << key.first << ", " << key.second.to_string() << "]";
658 
659  return (os);
660 }
661 
662 std::ostream&
663 operator<<(std::ostream& os, const ip_mroute::key_t& key)
664 {
665  os << "[" << key.first << ", " << key.second.to_string() << "]";
666 
667  return (os);
668 }
669 
670 std::ostream&
671 operator<<(std::ostream& os, const path_list_t& key)
672 {
673  os << "[";
674  for (auto k : key) {
675  os << k.to_string() << " ";
676  }
677  os << "]";
678 
679  return (os);
680 }
681 
682 std::ostream&
683 operator<<(std::ostream& os, const mpath_list_t& key)
684 {
685  os << "[";
686  for (auto k : key) {
687  os << "[" << k.first.to_string() << ", " << k.second.to_string() << "]";
688  }
689  os << "]";
690 
691  return (os);
692 }
693 
694 }; // namespace route
695 }; // namespace VOM
696 
697 /*
698  * fd.io coding-style-patch-verification: ON
699  *
700  * Local Variables:
701  * eval: (c-set-style "mozilla")
702  * End:
703  */
void remove(const path &path)
remove a path.
Definition: route.cpp:325
std::pair< route::table_id_t, mprefix_t > key_t
The key for a route.
Definition: route.hpp:431
typedef address
Definition: ip_types.api:83
static std::shared_ptr< route_domain > find(const key_t &temp)
Find the instnace of the route domain in the OM.
std::shared_ptr< ip_mroute > singular() const
Return the matching &#39;singular instance&#39;.
Definition: route.cpp:566
A cmd class that deletes a route.
Definition: mroute_cmds.hpp:69
static const special_t STANDARD
A standard path type.
Definition: route.hpp:45
#define VOM_LOG(lvl)
Definition: logger.hpp:181
A command class that creates or updates the route.
Definition: route_cmds.hpp:32
void db_dump(const DB &db, std::ostream &os)
Print each of the objects in the DB into the stream provided.
std::string to_string() const
convert to string format for debug purposes
Definition: prefix.cpp:214
const std::string key_t
In the opflex world each entity is known by a URI which can be converted into a string.
Definition: client_db.hpp:51
std::shared_ptr< route_domain > rd() const
Definition: route.cpp:232
A path for IP or MPLS routes.
Definition: route.hpp:32
static void register_handler(const std::vector< std::string > &cmds, const std::string &help, command_handler *ch)
Register a command handler for inspection.
Definition: inspect.cpp:85
ip_route(const prefix_t &prefix)
Construct a route in the default table.
Definition: route.cpp:263
int i
static rc_t write()
Write/Execute all commands hitherto enqueued.
Definition: hw.cpp:255
rc_t rc() const
Get the HW return code.
Definition: hw.hpp:119
static const log_level_t DEBUG
Definition: logger.hpp:32
std::string to_string() const
convert to string format for debug purposes
Definition: route.cpp:188
std::string to_string() const
Convert to string for debugging.
Definition: route.cpp:349
vl_api_mprefix_t prefix
Definition: ip.api:456
const boost::asio::ip::address & nh() const
Definition: route.cpp:226
void add(const path &path)
Add a path.
Definition: route.cpp:319
~ip_mroute()
Destructor.
Definition: route.cpp:494
A route-domain is a VRF.
static const special_t LOCAL
A local/for-us/recieve.
Definition: route.hpp:50
static const special_t DROP
drop path
Definition: route.hpp:55
~path()
Destructor.
Definition: route.cpp:163
bool operator==(const path &p) const
comparison operator
Definition: route.cpp:168
bool operator==(const ip_mroute &i) const
Comparison operator.
Definition: route.cpp:507
std::ostream & operator<<(std::ostream &os, const ip_route::key_t &key)
Definition: route.cpp:655
Types belonging to Routing.
Definition: prefix.hpp:31
void add(const path &path, const itf_flags_t &flag)
Definition: route.cpp:489
static const l3_proto_t IPV4
Definition: prefix.hpp:55
static const special_t UNREACH
a path will return ICMP unreachables
Definition: route.hpp:60
special_t type() const
Getters.
Definition: route.cpp:208
const key_t key() const
Get the route&#39;s key.
Definition: route.cpp:307
uint8_t preference() const
Definition: route.cpp:250
~ip_route()
Destructor.
Definition: route.cpp:297
A command class that creates or updates the route.
Definition: mroute_cmds.hpp:32
A IP multicast route.
Definition: route.hpp:425
static const special_t PROHIBIT
a path will return ICMP prohibit
Definition: route.hpp:65
bool operator<(const path &p) const
Less than operator for set insertion.
Definition: route.cpp:129
std::set< path > path_list_t
A path-list is a set of paths.
Definition: route.hpp:231
A representation of an interface in VPP.
Definition: interface.hpp:41
std::set< std::pair< path, itf_flags_t > > mpath_list_t
A mpath-list is a set of paths and interface flags.
Definition: route.hpp:236
const std::string & to_string() const
convert to string format for debug purposes
Definition: enum_base.hpp:36
Tables in which entries are added, e.g bridge/route-domains.
uint8_t weight() const
Definition: route.cpp:244
static rc_t commit(const client_db::key_t &key, const OBJ &obj)
Make the State in VPP reflect the expressed desired state.
Definition: om.hpp:202
void event_handler(void *tls_async)
Definition: tls_async.c:338
Special path types.
Definition: route.hpp:38
static std::shared_ptr< ip_mroute > find(const ip_mroute &temp)
Find the instnace of the route domain in the OM.
dependency_t
There needs to be a strict order in which object types are read from VPP (at boot time) and replayed ...
Definition: types.hpp:43
ip_mroute(const mprefix_t &mprefix)
Construct a route in the default table.
Definition: route.cpp:464
static const rc_t OK
The HW write was successfull.
Definition: types.hpp:109
static void enqueue(cmd *f)
Enqueue A command for execution.
Definition: hw.cpp:212
std::string to_string() const
Convert to string for debugging.
Definition: route.cpp:533
A cmd class that deletes a route.
Definition: route_cmds.hpp:67
nh_proto_t nh_proto() const
Definition: route.cpp:214
typedef mprefix
Definition: ip_types.api:93
bool operator==(const ip_route &i) const
Comparison operator.
Definition: route.cpp:313
path(special_t special, const nh_proto_t &proto=nh_proto_t::IPV4)
constructor for special paths
Definition: route.cpp:58
static std::shared_ptr< ip_route > find(const ip_route &temp)
Find the instnace of the route domain in the OM.
The VPP Object Model (VOM) library.
Definition: acl_binding.cpp:19
A prefix defintion.
Definition: prefix.hpp:252
void replay(void)
replay the object to create it in hardware
Definition: route.cpp:341
const neighbour::flags_t from_api(vapi_enum_ip_neighbor_flags f)
Definition: api_types.cpp:36
A representation of a method call to VPP.
Definition: cmd.hpp:32
static const_iterator_t cbegin()
std::shared_ptr< ip_route > singular() const
Return the matching &#39;singular instance&#39;.
Definition: route.cpp:380
A IP route.
Definition: route.hpp:247
static void dump(std::ostream &os)
Dump all route-doamin into the stream provided.
Definition: route.cpp:386
void show(char *chroot_path, int verbose)
Definition: svmtool.c:105
void replay(void)
replay the object to create it in hardware
Definition: route.cpp:524
static const_iterator_t cend()
std::shared_ptr< interface > itf() const
Definition: route.cpp:238
std::string to_string() const
convert to string format for debug purposes
Definition: prefix.cpp:574
flags_t flags() const
Definition: route.cpp:220
static void dump(std::ostream &os)
Dump all route-doamin into the stream provided.
Definition: route.cpp:572
static bool register_listener(listener *listener)
Register a listener of events.
Definition: om.cpp:127
A prefix defintion.
Definition: prefix.hpp:131
const key_t key() const
Get the route&#39;s key.
Definition: route.cpp:501
vl_api_fib_path_nh_proto_t proto
Definition: fib_types.api:125
std::pair< route::table_id_t, prefix_t > key_t
The key for a route.
Definition: route.hpp:253