VOM: mroutes
[vpp.git] / extras / vom / vom / route.cpp
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"
21 #include "vom/singular_db_funcs.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 << 2), "accept");
51 const itf_flags_t itf_flags_t::FORWARD((1 << 3), "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 const itf_flags_t&
58 itf_flags_t::from_vpp(uint32_t val)
59 {
60   if (itf_flags_t::ACCEPT == (int)val)
61     return itf_flags_t::ACCEPT;
62   else
63     return itf_flags_t::FORWARD;
64 }
65
66 path::path(special_t special)
67   : m_type(special)
68   , m_nh_proto(nh_proto_t::IPV4)
69   , m_flags(flags_t::NONE)
70   , m_nh()
71   , m_rd(nullptr)
72   , m_interface(nullptr)
73   , m_weight(1)
74   , m_preference(0)
75 {
76 }
77
78 path::path(const boost::asio::ip::address& nh,
79            const interface& interface,
80            uint8_t weight,
81            uint8_t preference)
82   : m_type(special_t::STANDARD)
83   , m_nh_proto(nh_proto_t::from_address(nh))
84   , m_flags(flags_t::NONE)
85   , m_nh(nh)
86   , m_rd(nullptr)
87   , m_interface(interface.singular())
88   , m_weight(weight)
89   , m_preference(preference)
90 {
91 }
92
93 path::path(const route_domain& rd,
94            const boost::asio::ip::address& nh,
95            uint8_t weight,
96            uint8_t preference)
97   : m_type(special_t::STANDARD)
98   , m_nh_proto(nh_proto_t::from_address(nh))
99   , m_flags(flags_t::NONE)
100   , m_nh(nh)
101   , m_rd(rd.singular())
102   , m_interface(nullptr)
103   , m_weight(weight)
104   , m_preference(preference)
105 {
106 }
107
108 path::path(const interface& interface,
109            const nh_proto_t& proto,
110            const flags_t& flags,
111            uint8_t weight,
112            uint8_t preference)
113   : m_type(special_t::STANDARD)
114   , m_nh_proto(proto)
115   , m_flags(flags)
116   , m_nh()
117   , m_rd(nullptr)
118   , m_interface(interface.singular())
119   , m_weight(weight)
120   , m_preference(preference)
121 {
122 }
123
124 path::path(const path& p)
125   : m_type(p.m_type)
126   , m_nh_proto(p.m_nh_proto)
127   , m_flags(p.m_flags)
128   , m_nh(p.m_nh)
129   , m_rd(p.m_rd)
130   , m_interface(p.m_interface)
131   , m_weight(p.m_weight)
132   , m_preference(p.m_preference)
133 {
134 }
135
136 bool
137 path::operator<(const path& p) const
138 {
139   if (m_nh_proto < p.m_nh_proto)
140     return true;
141   if (m_flags < p.m_flags)
142     return true;
143   if (m_type < p.m_type)
144     return true;
145   if (m_rd && !p.m_rd)
146     return false;
147   if (!m_rd && p.m_rd)
148     return true;
149   if (m_rd && p.m_rd) {
150     if (m_rd->table_id() < p.m_rd->table_id())
151       return true;
152     else if (m_rd->table_id() > p.m_rd->table_id())
153       return false;
154   }
155   if (m_nh < p.m_nh)
156     return true;
157   if (m_interface && !p.m_interface)
158     return false;
159   if (!m_interface && p.m_interface)
160     return true;
161   if (m_interface && p.m_interface) {
162     if (m_interface->handle() < p.m_interface->handle())
163       return true;
164     if (p.m_interface->handle() < m_interface->handle())
165       return false;
166   }
167
168   return (false);
169 }
170
171 path::~path()
172 {
173 }
174
175 bool
176 path::operator==(const path& p) const
177 {
178   bool result = true;
179   if (m_rd && !p.m_rd)
180     return false;
181   if (!m_rd && p.m_rd)
182     return false;
183   if (m_rd && p.m_rd)
184     result &= (*m_rd == *p.m_rd);
185   if (m_interface && !p.m_interface)
186     return false;
187   if (!m_interface && p.m_interface)
188     return false;
189   if (m_interface && p.m_interface)
190     result &= (*m_interface == *p.m_interface);
191   return (result && (m_type == p.m_type) && (m_nh == p.m_nh) &&
192           (m_nh_proto == p.m_nh_proto) && (m_flags == p.m_flags));
193 }
194
195 std::string
196 path::to_string() const
197 {
198   std::ostringstream s;
199
200   s << "path:["
201     << "type:" << m_type.to_string() << " proto:" << m_nh_proto.to_string()
202     << " flags:" << m_flags.to_string() << " neighbour:" << m_nh.to_string();
203   if (m_rd) {
204     s << " " << m_rd->to_string();
205   }
206   if (m_interface) {
207     s << " " << m_interface->to_string();
208   }
209   s << " weight:" << static_cast<int>(m_weight)
210     << " preference:" << static_cast<int>(m_preference) << "]";
211
212   return (s.str());
213 }
214
215 path::special_t
216 path::type() const
217 {
218   return m_type;
219 }
220
221 nh_proto_t
222 path::nh_proto() const
223 {
224   return m_nh_proto;
225 }
226
227 path::flags_t
228 path::flags() const
229 {
230   return m_flags;
231 }
232
233 const boost::asio::ip::address&
234 path::nh() const
235 {
236   return m_nh;
237 }
238
239 std::shared_ptr<route_domain>
240 path::rd() const
241 {
242   return m_rd;
243 }
244
245 std::shared_ptr<interface>
246 path::itf() const
247 {
248   return m_interface;
249 }
250
251 uint8_t
252 path::weight() const
253 {
254   return m_weight;
255 }
256
257 uint8_t
258 path::preference() const
259 {
260   return m_preference;
261 }
262
263 ip_route::ip_route(const prefix_t& prefix, const path& p)
264   : m_hw(false)
265   , m_rd(route_domain::get_default())
266   , m_prefix(prefix)
267   , m_paths({ p })
268 {
269 }
270
271 ip_route::ip_route(const prefix_t& prefix)
272   : m_hw(false)
273   , m_rd(route_domain::get_default())
274   , m_prefix(prefix)
275   , m_paths()
276 {
277 }
278
279 ip_route::ip_route(const ip_route& r)
280   : m_hw(r.m_hw)
281   , m_rd(r.m_rd)
282   , m_prefix(r.m_prefix)
283   , m_paths(r.m_paths)
284 {
285 }
286
287 ip_route::ip_route(const route_domain& rd, const prefix_t& prefix)
288   : m_hw(false)
289   , m_rd(rd.singular())
290   , m_prefix(prefix)
291   , m_paths()
292 {
293 }
294
295 ip_route::ip_route(const route_domain& rd,
296                    const prefix_t& prefix,
297                    const path& p)
298   : m_hw(false)
299   , m_rd(rd.singular())
300   , m_prefix(prefix)
301   , m_paths({ p })
302 {
303 }
304
305 ip_route::~ip_route()
306 {
307   sweep();
308
309   // not in the DB anymore.
310   m_db.release(key(), this);
311   m_paths.clear();
312 }
313
314 const ip_route::key_t
315 ip_route::key() const
316 {
317   return (std::make_pair(m_rd->table_id(), m_prefix));
318 }
319
320 bool
321 ip_route::operator==(const ip_route& i) const
322 {
323   return ((key() == i.key()) && (m_paths == i.m_paths));
324 }
325
326 void
327 ip_route::add(const path& path)
328 {
329   m_paths.insert(path);
330 }
331
332 void
333 ip_route::remove(const path& path)
334 {
335   m_paths.erase(path);
336 }
337
338 void
339 ip_route::sweep()
340 {
341   if (m_hw) {
342     HW::enqueue(
343       new ip_route_cmds::delete_cmd(m_hw, m_rd->table_id(), m_prefix));
344   }
345   HW::write();
346 }
347
348 void
349 ip_route::replay()
350 {
351   if (m_hw) {
352     HW::enqueue(
353       new ip_route_cmds::update_cmd(m_hw, m_rd->table_id(), m_prefix, m_paths));
354   }
355 }
356 std::string
357 ip_route::to_string() const
358 {
359   std::ostringstream s;
360   s << "route:[" << m_rd->to_string() << ", " << m_prefix.to_string() << " ["
361     << m_paths << "]"
362     << "]";
363
364   return (s.str());
365 }
366
367 void
368 ip_route::update(const ip_route& r)
369 {
370   /*
371 * create the table if it is not yet created
372 */
373   if (rc_t::OK != m_hw.rc()) {
374     HW::enqueue(
375       new ip_route_cmds::update_cmd(m_hw, m_rd->table_id(), m_prefix, m_paths));
376   }
377 }
378
379 std::shared_ptr<ip_route>
380 ip_route::find_or_add(const ip_route& temp)
381 {
382   return (m_db.find_or_add(temp.key(), temp));
383 }
384
385 std::shared_ptr<ip_route>
386 ip_route::find(const key_t& k)
387 {
388   return (m_db.find(k));
389 }
390
391 std::shared_ptr<ip_route>
392 ip_route::singular() const
393 {
394   return find_or_add(*this);
395 }
396
397 void
398 ip_route::dump(std::ostream& os)
399 {
400   db_dump(m_db, os);
401 }
402
403 ip_route::event_handler::event_handler()
404 {
405   OM::register_listener(this);
406   inspect::register_handler({ "ip-route" }, "ip route configurations", this);
407 }
408
409 void
410 ip_route::event_handler::handle_replay()
411 {
412   m_db.replay();
413 }
414
415 void
416 ip_route::event_handler::handle_populate(const client_db::key_t& key)
417 {
418   std::shared_ptr<ip_route_cmds::dump_v4_cmd> cmd_v4 =
419     std::make_shared<ip_route_cmds::dump_v4_cmd>();
420   std::shared_ptr<ip_route_cmds::dump_v6_cmd> cmd_v6 =
421     std::make_shared<ip_route_cmds::dump_v6_cmd>();
422
423   HW::enqueue(cmd_v4);
424   HW::enqueue(cmd_v6);
425   HW::write();
426
427   for (auto& record : *cmd_v4) {
428     auto& payload = record.get_payload();
429
430     prefix_t pfx(0, payload.address, payload.address_length);
431
432     /**
433      * populating the route domain here
434      */
435     route_domain rd_temp(payload.table_id);
436     std::shared_ptr<route_domain> rd = route_domain::find(payload.table_id);
437     if (!rd) {
438       OM::commit(key, rd_temp);
439     }
440     ip_route ip_r(rd_temp, pfx);
441
442     for (unsigned int i = 0; i < payload.count; i++) {
443       ip_r.add(from_vpp(payload.path[i], nh_proto_t::IPV4));
444     }
445     VOM_LOG(log_level_t::DEBUG) << "ip-route-dump: " << ip_r.to_string();
446
447     /*
448      * Write each of the discovered interfaces into the OM,
449      * but disable the HW Command q whilst we do, so that no
450      * commands are sent to VPP
451      */
452     OM::commit(key, ip_r);
453   }
454
455   for (auto& record : *cmd_v6) {
456     auto& payload = record.get_payload();
457
458     prefix_t pfx(1, payload.address, payload.address_length);
459     route_domain rd_temp(payload.table_id);
460     std::shared_ptr<route_domain> rd = route_domain::find(payload.table_id);
461     if (!rd) {
462       OM::commit(key, rd_temp);
463     }
464     ip_route ip_r(rd_temp, pfx);
465
466     for (unsigned int i = 0; i < payload.count; i++) {
467       ip_r.add(from_vpp(payload.path[i], nh_proto_t::IPV6));
468     }
469     VOM_LOG(log_level_t::DEBUG) << "ip-route-dump: " << ip_r.to_string();
470
471     /*
472      * Write each of the discovered interfaces into the OM,
473      * but disable the HW Command q whilst we do, so that no
474      * commands are sent to VPP
475      */
476     OM::commit(key, ip_r);
477   }
478 }
479
480 dependency_t
481 ip_route::event_handler::order() const
482 {
483   return (dependency_t::TABLE);
484 }
485
486 void
487 ip_route::event_handler::show(std::ostream& os)
488 {
489   db_dump(m_db, os);
490 }
491
492 ip_mroute::ip_mroute(const mprefix_t& mprefix)
493   : m_hw(false)
494   , m_rd(route_domain::get_default())
495   , m_mprefix(mprefix)
496   , m_paths()
497 {
498 }
499
500 ip_mroute::ip_mroute(const ip_mroute& r)
501   : m_hw(r.m_hw)
502   , m_rd(r.m_rd)
503   , m_mprefix(r.m_mprefix)
504   , m_paths(r.m_paths)
505 {
506 }
507
508 ip_mroute::ip_mroute(const route_domain& rd, const mprefix_t& mprefix)
509   : m_hw(false)
510   , m_rd(rd.singular())
511   , m_mprefix(mprefix)
512   , m_paths()
513 {
514 }
515
516 void
517 ip_mroute::add(const path& path, const itf_flags_t& flag)
518 {
519   m_paths.insert(std::make_pair(path, flag));
520 }
521
522 ip_mroute::~ip_mroute()
523 {
524   sweep();
525   m_db.release(key(), this);
526 }
527
528 const ip_mroute::key_t
529 ip_mroute::key() const
530 {
531   return (std::make_pair(m_rd->table_id(), m_mprefix));
532 }
533
534 bool
535 ip_mroute::operator==(const ip_mroute& i) const
536 {
537   return ((key() == i.key()) && (m_paths == i.m_paths));
538 }
539
540 void
541 ip_mroute::sweep()
542 {
543   if (m_hw) {
544     for (auto& p : m_paths)
545       HW::enqueue(new ip_mroute_cmds::delete_cmd(m_hw, m_rd->table_id(),
546                                                  m_mprefix, p.first, p.second));
547   }
548   HW::write();
549 }
550
551 void
552 ip_mroute::replay()
553 {
554   if (m_hw) {
555     for (auto& p : m_paths)
556       HW::enqueue(new ip_mroute_cmds::update_cmd(m_hw, m_rd->table_id(),
557                                                  m_mprefix, p.first, p.second));
558   }
559 }
560 std::string
561 ip_mroute::to_string() const
562 {
563   std::ostringstream s;
564   s << "route:[" << m_rd->to_string() << ", " << m_mprefix.to_string() << " ["
565     << m_paths << "]"
566     << "]";
567
568   return (s.str());
569 }
570
571 void
572 ip_mroute::update(const ip_mroute& r)
573 {
574   if (rc_t::OK != m_hw.rc()) {
575     for (auto& p : m_paths)
576       HW::enqueue(new ip_mroute_cmds::update_cmd(m_hw, m_rd->table_id(),
577                                                  m_mprefix, p.first, p.second));
578   }
579 }
580
581 std::shared_ptr<ip_mroute>
582 ip_mroute::find_or_add(const ip_mroute& temp)
583 {
584   return (m_db.find_or_add(temp.key(), temp));
585 }
586
587 std::shared_ptr<ip_mroute>
588 ip_mroute::find(const key_t& k)
589 {
590   return (m_db.find(k));
591 }
592
593 std::shared_ptr<ip_mroute>
594 ip_mroute::singular() const
595 {
596   return find_or_add(*this);
597 }
598
599 void
600 ip_mroute::dump(std::ostream& os)
601 {
602   db_dump(m_db, os);
603 }
604
605 ip_mroute::event_handler::event_handler()
606 {
607   OM::register_listener(this);
608   inspect::register_handler({ "ip-route" }, "ip route configurations", this);
609 }
610
611 void
612 ip_mroute::event_handler::handle_replay()
613 {
614   m_db.replay();
615 }
616
617 void
618 ip_mroute::event_handler::handle_populate(const client_db::key_t& key)
619 {
620   std::shared_ptr<ip_mroute_cmds::dump_v4_cmd> cmd_v4 =
621     std::make_shared<ip_mroute_cmds::dump_v4_cmd>();
622   std::shared_ptr<ip_mroute_cmds::dump_v6_cmd> cmd_v6 =
623     std::make_shared<ip_mroute_cmds::dump_v6_cmd>();
624
625   HW::enqueue(cmd_v4);
626   HW::enqueue(cmd_v6);
627   HW::write();
628
629   VOM_LOG(log_level_t::INFO) << "ip-mroute-dump: ";
630
631   for (auto& record : *cmd_v4) {
632     auto& payload = record.get_payload();
633
634     ip_address_t gaddr = from_bytes(0, payload.grp_address);
635     ip_address_t saddr = from_bytes(0, payload.src_address);
636     mprefix_t pfx(saddr, gaddr, payload.address_length);
637
638     /**
639      * populating the route domain here
640      */
641     route_domain rd_temp(payload.table_id);
642     std::shared_ptr<route_domain> rd = route_domain::find(payload.table_id);
643     if (!rd) {
644       OM::commit(key, rd_temp);
645     }
646     ip_mroute ip_r(rd_temp, pfx);
647
648     for (unsigned int i = 0; i < payload.count; i++) {
649       vapi_type_mfib_path& p = payload.path[i];
650       ip_r.add(from_vpp(p.path, nh_proto_t::IPV4),
651                itf_flags_t::from_vpp(p.itf_flags));
652     }
653     VOM_LOG(log_level_t::INFO) << "ip-mroute-dump: " << ip_r.to_string();
654
655     /*
656      * Write each of the discovered interfaces into the OM,
657      * but disable the HW Command q whilst we do, so that no
658      * commands are sent to VPP
659      */
660     OM::commit(key, ip_r);
661   }
662
663   for (auto& record : *cmd_v6) {
664     auto& payload = record.get_payload();
665
666     ip_address_t gaddr = from_bytes(1, payload.grp_address);
667     ip_address_t saddr = from_bytes(1, payload.src_address);
668     mprefix_t pfx(saddr, gaddr, payload.address_length);
669
670     route_domain rd_temp(payload.table_id);
671     std::shared_ptr<route_domain> rd = route_domain::find(payload.table_id);
672     if (!rd) {
673       OM::commit(key, rd_temp);
674     }
675     ip_mroute ip_r(rd_temp, pfx);
676
677     for (unsigned int i = 0; i < payload.count; i++) {
678       vapi_type_mfib_path& p = payload.path[i];
679       ip_r.add(from_vpp(p.path, nh_proto_t::IPV6),
680                itf_flags_t::from_vpp(p.itf_flags));
681     }
682     VOM_LOG(log_level_t::INFO) << "ip-mroute-dump: " << ip_r.to_string();
683
684     /*
685      * Write each of the discovered interfaces into the OM,
686      * but disable the HW Command q whilst we do, so that no
687      * commands are sent to VPP
688      */
689     OM::commit(key, ip_r);
690   }
691 }
692
693 dependency_t
694 ip_mroute::event_handler::order() const
695 {
696   return (dependency_t::TABLE);
697 }
698
699 void
700 ip_mroute::event_handler::show(std::ostream& os)
701 {
702   db_dump(m_db, os);
703 }
704
705 std::ostream&
706 operator<<(std::ostream& os, const ip_route::key_t& key)
707 {
708   os << "[" << key.first << ", " << key.second.to_string() << "]";
709
710   return (os);
711 }
712
713 std::ostream&
714 operator<<(std::ostream& os, const ip_mroute::key_t& key)
715 {
716   os << "[" << key.first << ", " << key.second.to_string() << "]";
717
718   return (os);
719 }
720
721 std::ostream&
722 operator<<(std::ostream& os, const path_list_t& key)
723 {
724   os << "[";
725   for (auto k : key) {
726     os << k.to_string() << " ";
727   }
728   os << "]";
729
730   return (os);
731 }
732
733 std::ostream&
734 operator<<(std::ostream& os, const mpath_list_t& key)
735 {
736   os << "[";
737   for (auto k : key) {
738     os << "[" << k.first.to_string() << ", " << k.second.to_string() << "]";
739   }
740   os << "]";
741
742   return (os);
743 }
744
745 }; // namespace route
746 }; // namespace VOM
747
748 /*
749  * fd.io coding-style-patch-verification: ON
750  *
751  * Local Variables:
752  * eval: (c-set-style "mozilla")
753  * End:
754  */