b136c251b94d30e633d936960e46645bd362860d
[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 << 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 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, const nh_proto_t& proto)
67   : m_type(special)
68   , m_nh_proto(proto)
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     for (auto& p : m_paths)
343       HW::enqueue(
344         new ip_route_cmds::delete_cmd(m_hw, m_rd->table_id(), m_prefix, p));
345   }
346   HW::write();
347 }
348
349 void
350 ip_route::replay()
351 {
352   if (m_hw) {
353     for (auto& p : m_paths)
354       HW::enqueue(
355         new ip_route_cmds::update_cmd(m_hw, m_rd->table_id(), m_prefix, p));
356   }
357 }
358 std::string
359 ip_route::to_string() const
360 {
361   std::ostringstream s;
362   s << "route:[" << m_rd->to_string() << ", " << m_prefix.to_string() << " ["
363     << m_paths << "]"
364     << "]";
365
366   return (s.str());
367 }
368
369 void
370 ip_route::update(const ip_route& r)
371 {
372   if (rc_t::OK != m_hw.rc()) {
373     /*
374      * route not yet installed. install each of the desired paths
375      */
376     m_paths = r.m_paths;
377
378     for (auto& p : m_paths)
379       HW::enqueue(
380         new ip_route_cmds::update_cmd(m_hw, m_rd->table_id(), m_prefix, p));
381   } else {
382     /*
383      * add each path that is not installed yet and remove each that is no longer
384      * wanted
385      */
386     path_list_t to_add;
387     set_difference(r.m_paths.begin(), r.m_paths.end(), m_paths.begin(),
388                    m_paths.end(), std::inserter(to_add, to_add.begin()));
389
390     for (auto& p : to_add)
391       HW::enqueue(
392         new ip_route_cmds::update_cmd(m_hw, m_rd->table_id(), m_prefix, p));
393
394     path_list_t to_del;
395     set_difference(m_paths.begin(), m_paths.end(), r.m_paths.begin(),
396                    r.m_paths.end(), std::inserter(to_del, to_del.begin()));
397
398     for (auto& p : to_del)
399       HW::enqueue(
400         new ip_route_cmds::delete_cmd(m_hw, m_rd->table_id(), m_prefix, p));
401
402     m_paths = r.m_paths;
403   }
404 }
405
406 std::shared_ptr<ip_route>
407 ip_route::find_or_add(const ip_route& temp)
408 {
409   return (m_db.find_or_add(temp.key(), temp));
410 }
411
412 std::shared_ptr<ip_route>
413 ip_route::find(const key_t& k)
414 {
415   return (m_db.find(k));
416 }
417
418 std::shared_ptr<ip_route>
419 ip_route::singular() const
420 {
421   return find_or_add(*this);
422 }
423
424 void
425 ip_route::dump(std::ostream& os)
426 {
427   db_dump(m_db, os);
428 }
429
430 ip_route::event_handler::event_handler()
431 {
432   OM::register_listener(this);
433   inspect::register_handler({ "ip-route" }, "ip route configurations", this);
434 }
435
436 void
437 ip_route::event_handler::handle_replay()
438 {
439   m_db.replay();
440 }
441
442 void
443 ip_route::event_handler::handle_populate(const client_db::key_t& key)
444 {
445   std::shared_ptr<ip_route_cmds::dump_v4_cmd> cmd_v4 =
446     std::make_shared<ip_route_cmds::dump_v4_cmd>();
447   std::shared_ptr<ip_route_cmds::dump_v6_cmd> cmd_v6 =
448     std::make_shared<ip_route_cmds::dump_v6_cmd>();
449
450   HW::enqueue(cmd_v4);
451   HW::enqueue(cmd_v6);
452   HW::write();
453
454   for (auto& record : *cmd_v4) {
455     auto& payload = record.get_payload();
456
457     prefix_t pfx(0, payload.address, payload.address_length);
458
459     /**
460      * populating the route domain here
461      */
462     route_domain rd_temp(payload.table_id);
463     std::shared_ptr<route_domain> rd = route_domain::find(payload.table_id);
464     if (!rd) {
465       OM::commit(key, rd_temp);
466     }
467     ip_route ip_r(rd_temp, pfx);
468
469     for (unsigned int i = 0; i < payload.count; i++) {
470       ip_r.add(from_vpp(payload.path[i], nh_proto_t::IPV4));
471     }
472     VOM_LOG(log_level_t::DEBUG) << "ip-route-dump: " << ip_r.to_string();
473
474     /*
475      * Write each of the discovered interfaces into the OM,
476      * but disable the HW Command q whilst we do, so that no
477      * commands are sent to VPP
478      */
479     OM::commit(key, ip_r);
480   }
481
482   for (auto& record : *cmd_v6) {
483     auto& payload = record.get_payload();
484
485     prefix_t pfx(1, payload.address, payload.address_length);
486     route_domain rd_temp(payload.table_id);
487     std::shared_ptr<route_domain> rd = route_domain::find(payload.table_id);
488     if (!rd) {
489       OM::commit(key, rd_temp);
490     }
491     ip_route ip_r(rd_temp, pfx);
492
493     for (unsigned int i = 0; i < payload.count; i++) {
494       ip_r.add(from_vpp(payload.path[i], nh_proto_t::IPV6));
495     }
496     VOM_LOG(log_level_t::DEBUG) << "ip-route-dump: " << ip_r.to_string();
497
498     /*
499      * Write each of the discovered interfaces into the OM,
500      * but disable the HW Command q whilst we do, so that no
501      * commands are sent to VPP
502      */
503     OM::commit(key, ip_r);
504   }
505 }
506
507 dependency_t
508 ip_route::event_handler::order() const
509 {
510   return (dependency_t::TABLE);
511 }
512
513 void
514 ip_route::event_handler::show(std::ostream& os)
515 {
516   db_dump(m_db, os);
517 }
518
519 ip_mroute::ip_mroute(const mprefix_t& mprefix)
520   : m_hw(false)
521   , m_rd(route_domain::get_default())
522   , m_mprefix(mprefix)
523   , m_paths()
524 {
525 }
526
527 ip_mroute::ip_mroute(const ip_mroute& r)
528   : m_hw(r.m_hw)
529   , m_rd(r.m_rd)
530   , m_mprefix(r.m_mprefix)
531   , m_paths(r.m_paths)
532 {
533 }
534
535 ip_mroute::ip_mroute(const route_domain& rd, const mprefix_t& mprefix)
536   : m_hw(false)
537   , m_rd(rd.singular())
538   , m_mprefix(mprefix)
539   , m_paths()
540 {
541 }
542
543 void
544 ip_mroute::add(const path& path, const itf_flags_t& flag)
545 {
546   m_paths.insert(std::make_pair(path, flag));
547 }
548
549 ip_mroute::~ip_mroute()
550 {
551   sweep();
552   m_db.release(key(), this);
553 }
554
555 const ip_mroute::key_t
556 ip_mroute::key() const
557 {
558   return (std::make_pair(m_rd->table_id(), m_mprefix));
559 }
560
561 bool
562 ip_mroute::operator==(const ip_mroute& i) const
563 {
564   return ((key() == i.key()) && (m_paths == i.m_paths));
565 }
566
567 void
568 ip_mroute::sweep()
569 {
570   if (m_hw) {
571     for (auto& p : m_paths)
572       HW::enqueue(new ip_mroute_cmds::delete_cmd(m_hw, m_rd->table_id(),
573                                                  m_mprefix, p.first, p.second));
574   }
575   HW::write();
576 }
577
578 void
579 ip_mroute::replay()
580 {
581   if (m_hw) {
582     for (auto& p : m_paths)
583       HW::enqueue(new ip_mroute_cmds::update_cmd(m_hw, m_rd->table_id(),
584                                                  m_mprefix, p.first, p.second));
585   }
586 }
587 std::string
588 ip_mroute::to_string() const
589 {
590   std::ostringstream s;
591   s << "route:[" << m_rd->to_string() << ", " << m_mprefix.to_string() << " ["
592     << m_paths << "]"
593     << "]";
594
595   return (s.str());
596 }
597
598 void
599 ip_mroute::update(const ip_mroute& r)
600 {
601   if (rc_t::OK != m_hw.rc()) {
602     for (auto& p : m_paths)
603       HW::enqueue(new ip_mroute_cmds::update_cmd(m_hw, m_rd->table_id(),
604                                                  m_mprefix, p.first, p.second));
605   }
606 }
607
608 std::shared_ptr<ip_mroute>
609 ip_mroute::find_or_add(const ip_mroute& temp)
610 {
611   return (m_db.find_or_add(temp.key(), temp));
612 }
613
614 std::shared_ptr<ip_mroute>
615 ip_mroute::find(const key_t& k)
616 {
617   return (m_db.find(k));
618 }
619
620 std::shared_ptr<ip_mroute>
621 ip_mroute::singular() const
622 {
623   return find_or_add(*this);
624 }
625
626 void
627 ip_mroute::dump(std::ostream& os)
628 {
629   db_dump(m_db, os);
630 }
631
632 ip_mroute::event_handler::event_handler()
633 {
634   OM::register_listener(this);
635   inspect::register_handler({ "ip-mroute" },
636                             "ip multicast route configurations", this);
637 }
638
639 void
640 ip_mroute::event_handler::handle_replay()
641 {
642   m_db.replay();
643 }
644
645 void
646 ip_mroute::event_handler::handle_populate(const client_db::key_t& key)
647 {
648   std::shared_ptr<ip_mroute_cmds::dump_v4_cmd> cmd_v4 =
649     std::make_shared<ip_mroute_cmds::dump_v4_cmd>();
650   std::shared_ptr<ip_mroute_cmds::dump_v6_cmd> cmd_v6 =
651     std::make_shared<ip_mroute_cmds::dump_v6_cmd>();
652
653   HW::enqueue(cmd_v4);
654   HW::enqueue(cmd_v6);
655   HW::write();
656
657   VOM_LOG(log_level_t::INFO) << "ip-mroute-dump: ";
658
659   for (auto& record : *cmd_v4) {
660     auto& payload = record.get_payload();
661
662     ip_address_t gaddr = from_bytes(0, payload.grp_address);
663     ip_address_t saddr = from_bytes(0, payload.src_address);
664     mprefix_t pfx(saddr, gaddr, payload.address_length);
665
666     /**
667      * populating the route domain here
668      */
669     route_domain rd_temp(payload.table_id);
670     std::shared_ptr<route_domain> rd = route_domain::find(payload.table_id);
671     if (!rd) {
672       OM::commit(key, rd_temp);
673     }
674     ip_mroute ip_r(rd_temp, pfx);
675
676     for (unsigned int i = 0; i < payload.count; i++) {
677       vapi_type_mfib_path& p = payload.path[i];
678       ip_r.add(from_vpp(p.path, nh_proto_t::IPV4),
679                itf_flags_t::from_vpp(p.itf_flags));
680     }
681     VOM_LOG(log_level_t::DEBUG) << "ip-mroute-dump: " << ip_r.to_string();
682
683     /*
684      * Write each of the discovered interfaces into the OM,
685      * but disable the HW Command q whilst we do, so that no
686      * commands are sent to VPP
687      */
688     OM::commit(key, ip_r);
689   }
690
691   for (auto& record : *cmd_v6) {
692     auto& payload = record.get_payload();
693
694     ip_address_t gaddr = from_bytes(1, payload.grp_address);
695     ip_address_t saddr = from_bytes(1, payload.src_address);
696     mprefix_t pfx(saddr, gaddr, payload.address_length);
697
698     route_domain rd_temp(payload.table_id);
699     std::shared_ptr<route_domain> rd = route_domain::find(payload.table_id);
700     if (!rd) {
701       OM::commit(key, rd_temp);
702     }
703     ip_mroute ip_r(rd_temp, pfx);
704
705     for (unsigned int i = 0; i < payload.count; i++) {
706       vapi_type_mfib_path& p = payload.path[i];
707       ip_r.add(from_vpp(p.path, nh_proto_t::IPV6),
708                itf_flags_t::from_vpp(p.itf_flags));
709     }
710     VOM_LOG(log_level_t::DEBUG) << "ip-mroute-dump: " << ip_r.to_string();
711
712     /*
713      * Write each of the discovered interfaces into the OM,
714      * but disable the HW Command q whilst we do, so that no
715      * commands are sent to VPP
716      */
717     OM::commit(key, ip_r);
718   }
719 }
720
721 dependency_t
722 ip_mroute::event_handler::order() const
723 {
724   return (dependency_t::TABLE);
725 }
726
727 void
728 ip_mroute::event_handler::show(std::ostream& os)
729 {
730   db_dump(m_db, os);
731 }
732
733 std::ostream&
734 operator<<(std::ostream& os, const ip_route::key_t& key)
735 {
736   os << "[" << key.first << ", " << key.second.to_string() << "]";
737
738   return (os);
739 }
740
741 std::ostream&
742 operator<<(std::ostream& os, const ip_mroute::key_t& key)
743 {
744   os << "[" << key.first << ", " << key.second.to_string() << "]";
745
746   return (os);
747 }
748
749 std::ostream&
750 operator<<(std::ostream& os, const path_list_t& key)
751 {
752   os << "[";
753   for (auto k : key) {
754     os << k.to_string() << " ";
755   }
756   os << "]";
757
758   return (os);
759 }
760
761 std::ostream&
762 operator<<(std::ostream& os, const mpath_list_t& key)
763 {
764   os << "[";
765   for (auto k : key) {
766     os << "[" << k.first.to_string() << ", " << k.second.to_string() << "]";
767   }
768   os << "]";
769
770   return (os);
771 }
772
773 }; // namespace route
774 }; // namespace VOM
775
776 /*
777  * fd.io coding-style-patch-verification: ON
778  *
779  * Local Variables:
780  * eval: (c-set-style "mozilla")
781  * End:
782  */