fib: fib api updates
[vpp.git] / extras / vom / vom / interface.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/interface.hpp"
17 #include "vom/api_types.hpp"
18 #include "vom/bond_group_binding.hpp"
19 #include "vom/bond_group_binding_cmds.hpp"
20 #include "vom/bond_interface_cmds.hpp"
21 #include "vom/interface_cmds.hpp"
22 #include "vom/interface_factory.hpp"
23 #include "vom/l3_binding_cmds.hpp"
24 #include "vom/logger.hpp"
25 #include "vom/prefix.hpp"
26 #include "vom/singular_db_funcs.hpp"
27 #include "vom/stat_reader.hpp"
28 #include "vom/tap_interface_cmds.hpp"
29
30 namespace VOM {
31 /**
32  * A DB of all the interfaces, key on the name
33  */
34 singular_db<interface::key_t, interface> interface::m_db;
35
36 /**
37  * A DB of all the interfaces, key on VPP's handle
38  */
39 std::map<handle_t, std::weak_ptr<interface>> interface::m_hdl_db;
40
41 interface::event_handler interface::m_evh;
42
43 /**
44  * the event enable command.
45  */
46 std::shared_ptr<interface_cmds::events_cmd> interface::m_events_cmd;
47
48 /**
49  * Construct a new object matching the desried state
50  */
51 interface::interface(const std::string& name,
52                      interface::type_t itf_type,
53                      interface::admin_state_t itf_state,
54                      const std::string& tag)
55   : m_hdl(handle_t::INVALID)
56   , m_name(name)
57   , m_type(itf_type)
58   , m_state(itf_state)
59   , m_table_id(route::DEFAULT_TABLE)
60   , m_l2_address(l2_address_t::ZERO, rc_t::UNSET)
61   , m_stats_type(stats_type_t::NORMAL)
62   , m_stats({})
63   , m_listener(nullptr)
64   , m_oper(oper_state_t::DOWN)
65   , m_tag(tag)
66 {
67 }
68
69 interface::interface(const std::string& name,
70                      interface::type_t itf_type,
71                      interface::admin_state_t itf_state,
72                      const route_domain& rd,
73                      const std::string& tag)
74   : m_hdl(handle_t::INVALID)
75   , m_name(name)
76   , m_type(itf_type)
77   , m_rd(rd.singular())
78   , m_state(itf_state)
79   , m_table_id(m_rd->table_id())
80   , m_l2_address(l2_address_t::ZERO, rc_t::UNSET)
81   , m_stats_type(stats_type_t::NORMAL)
82   , m_stats({})
83   , m_listener(nullptr)
84   , m_oper(oper_state_t::DOWN)
85   , m_tag(tag)
86 {
87 }
88
89 interface::interface(const interface& o)
90   : m_hdl(o.m_hdl)
91   , m_name(o.m_name)
92   , m_type(o.m_type)
93   , m_rd(o.m_rd)
94   , m_state(o.m_state)
95   , m_table_id(o.m_table_id)
96   , m_l2_address(o.m_l2_address)
97   , m_stats_type(o.m_stats_type)
98   , m_stats(o.m_stats)
99   , m_listener(o.m_listener)
100   , m_oper(o.m_oper)
101   , m_tag(o.m_tag)
102 {
103 }
104
105 bool
106 interface::operator==(const interface& i) const
107 {
108   return ((key() == i.key()) &&
109           (m_l2_address.data() == i.m_l2_address.data()) &&
110           (m_state == i.m_state) && (m_rd == i.m_rd) && (m_type == i.m_type) &&
111           (m_oper == i.m_oper));
112 }
113
114 interface::event_listener::event_listener()
115   : m_status(rc_t::NOOP)
116 {
117 }
118
119 HW::item<bool>&
120 interface::event_listener::status()
121 {
122   return (m_status);
123 }
124
125 interface::stat_listener::stat_listener()
126   : m_status(rc_t::NOOP)
127 {
128 }
129
130 HW::item<bool>&
131 interface::stat_listener::status()
132 {
133   return (m_status);
134 }
135
136 /**
137  * Return the interface type
138  */
139 const interface::type_t&
140 interface::type() const
141 {
142   return (m_type);
143 }
144
145 const handle_t&
146 interface::handle() const
147 {
148   return (singular()->handle_i());
149 }
150
151 const handle_t&
152 interface::handle_i() const
153 {
154   return (m_hdl.data());
155 }
156
157 const l2_address_t&
158 interface::l2_address() const
159 {
160   return (m_l2_address.data());
161 }
162
163 interface::const_iterator_t
164 interface::cbegin()
165 {
166   return m_db.begin();
167 }
168
169 interface::const_iterator_t
170 interface::cend()
171 {
172   return m_db.end();
173 }
174
175 void
176 interface::sweep()
177 {
178   if (m_table_id && (m_table_id.data() != route::DEFAULT_TABLE)) {
179     m_table_id.data() = route::DEFAULT_TABLE;
180     HW::enqueue(
181       new interface_cmds::set_table_cmd(m_table_id, l3_proto_t::IPV4, m_hdl));
182     HW::enqueue(
183       new interface_cmds::set_table_cmd(m_table_id, l3_proto_t::IPV6, m_hdl));
184   }
185
186   if (m_listener) {
187     disable_stats_i();
188   }
189
190   // If the interface is up, bring it down
191   if (m_state && interface::admin_state_t::UP == m_state.data()) {
192     m_state.data() = interface::admin_state_t::DOWN;
193     HW::enqueue(new interface_cmds::state_change_cmd(m_state, m_hdl));
194   }
195
196   if (m_hdl) {
197     std::queue<cmd*> cmds;
198     HW::enqueue(mk_delete_cmd(cmds));
199   }
200   HW::write();
201 }
202
203 void
204 interface::replay()
205 {
206   if (m_hdl) {
207     std::queue<cmd*> cmds;
208     HW::enqueue(mk_create_cmd(cmds));
209   }
210
211   if (m_state && interface::admin_state_t::UP == m_state.data()) {
212     HW::enqueue(new interface_cmds::state_change_cmd(m_state, m_hdl));
213   }
214
215   if (m_listener) {
216     enable_stats(m_listener, m_stats_type.data());
217   }
218
219   if (m_table_id && (m_table_id.data() != route::DEFAULT_TABLE)) {
220     HW::enqueue(
221       new interface_cmds::set_table_cmd(m_table_id, l3_proto_t::IPV4, m_hdl));
222     HW::enqueue(
223       new interface_cmds::set_table_cmd(m_table_id, l3_proto_t::IPV6, m_hdl));
224   }
225 }
226
227 interface::~interface()
228 {
229   sweep();
230   release();
231 }
232
233 void
234 interface::release()
235 {
236   // not in the DB anymore.
237   m_db.release(m_name, this);
238 }
239
240 std::string
241 interface::to_string() const
242 {
243   std::ostringstream s;
244   s << "interface:[" << m_name << " type:" << m_type.to_string()
245     << " hdl:" << m_hdl.to_string() << " l2-address:["
246     << m_l2_address.to_string() << "]";
247
248   if (m_rd) {
249     s << " rd:" << m_rd->to_string();
250   }
251
252   s << " admin-state:" << m_state.to_string()
253     << " oper-state:" << m_oper.to_string();
254
255   if (!m_tag.empty()) {
256     s << " tag:[" << m_tag << "]";
257   }
258
259   s << "]";
260
261   return (s.str());
262 }
263
264 const std::string&
265 interface::name() const
266 {
267   return (m_name);
268 }
269
270 const interface::key_t&
271 interface::key() const
272 {
273   return (name());
274 }
275
276 std::queue<cmd*>&
277 interface::mk_create_cmd(std::queue<cmd*>& q)
278 {
279   if (type_t::LOOPBACK == m_type) {
280     q.push(new interface_cmds::loopback_create_cmd(m_hdl, m_name));
281     q.push(new interface_cmds::set_tag(m_hdl, m_name));
282     /*
283      * set the m_tag for pretty-print
284      */
285     m_tag = m_name;
286   } else if (type_t::BVI == m_type) {
287     q.push(new interface_cmds::bvi_create_cmd(m_hdl, m_name));
288     q.push(new interface_cmds::set_tag(m_hdl, m_name));
289     m_tag = m_name;
290   } else if (type_t::AFPACKET == m_type) {
291     q.push(new interface_cmds::af_packet_create_cmd(m_hdl, m_name));
292     if (!m_tag.empty())
293       q.push(new interface_cmds::set_tag(m_hdl, m_tag));
294   } else if (type_t::TAPV2 == m_type) {
295     if (!m_tag.empty())
296       q.push(new interface_cmds::set_tag(m_hdl, m_tag));
297   } else if (type_t::VHOST == m_type) {
298     q.push(new interface_cmds::vhost_create_cmd(m_hdl, m_name, m_tag));
299   } else {
300     m_hdl.set(rc_t::OK);
301   }
302
303   return (q);
304 }
305
306 std::queue<cmd*>&
307 interface::mk_delete_cmd(std::queue<cmd*>& q)
308 {
309   if (type_t::LOOPBACK == m_type) {
310     q.push(new interface_cmds::loopback_delete_cmd(m_hdl));
311   } else if (type_t::BVI == m_type) {
312     q.push(new interface_cmds::bvi_delete_cmd(m_hdl));
313   } else if (type_t::AFPACKET == m_type) {
314     q.push(new interface_cmds::af_packet_delete_cmd(m_hdl, m_name));
315   } else if (type_t::VHOST == m_type) {
316     q.push(new interface_cmds::vhost_delete_cmd(m_hdl, m_name));
317   }
318
319   return (q);
320 }
321
322 void
323 interface::update(const interface& desired)
324 {
325   /*
326    * the desired state is always that the interface should be created
327    */
328   if (rc_t::OK != m_hdl.rc()) {
329     std::queue<cmd*> cmds;
330     HW::enqueue(mk_create_cmd(cmds));
331     /*
332      * interface create now, so we can barf early if it fails
333      */
334     HW::write();
335   }
336
337   /*
338    * If the interface is not created do other commands should be issued
339    */
340   if (rc_t::OK != m_hdl.rc())
341     return;
342
343   /*
344    * change the interface state to that which is deisred
345    */
346   if (m_state.update(desired.m_state)) {
347     HW::enqueue(new interface_cmds::state_change_cmd(m_state, m_hdl));
348   }
349
350   /*
351    * change the interface state to that which is deisred
352    */
353   if (m_l2_address.update(desired.m_l2_address)) {
354     HW::enqueue(new interface_cmds::set_mac_cmd(m_l2_address, m_hdl));
355   }
356
357   /*
358    * If the interface is mapped into a route domain, set VPP's
359    * table ID
360    */
361   if (m_rd != desired.m_rd) {
362     /*
363      * changing route domains. need to remove all L3 bindings, swap the table
364      * then reapply the bindings.
365      */
366     auto it = l3_binding::cbegin();
367
368     while (it != l3_binding::cend()) {
369       if (it->second.lock()->itf().key() == key())
370         it->second.lock()->sweep();
371       ++it;
372     }
373     m_rd = desired.m_rd;
374     m_table_id.update(m_rd ? m_rd->table_id() : route::DEFAULT_TABLE);
375     HW::enqueue(
376       new interface_cmds::set_table_cmd(m_table_id, l3_proto_t::IPV4, m_hdl));
377     HW::enqueue(
378       new interface_cmds::set_table_cmd(m_table_id, l3_proto_t::IPV6, m_hdl));
379     HW::write();
380
381     it = l3_binding::cbegin();
382     while (it != l3_binding::cend()) {
383       if (it->second.lock()->itf().key() == key())
384         it->second.lock()->replay(); //(*it->second.lock());
385       ++it;
386     }
387   } else if (!m_table_id && m_rd) {
388     HW::enqueue(
389       new interface_cmds::set_table_cmd(m_table_id, l3_proto_t::IPV4, m_hdl));
390     HW::enqueue(
391       new interface_cmds::set_table_cmd(m_table_id, l3_proto_t::IPV6, m_hdl));
392   }
393 }
394
395 void
396 interface::set(const admin_state_t& state)
397 {
398   m_state = state;
399 }
400
401 void
402 interface::set(const l2_address_t& addr)
403 {
404   m_l2_address = { addr, rc_t::NOOP };
405 }
406
407 void
408 interface::set(const handle_t& hdl)
409 {
410   m_hdl = hdl;
411 }
412
413 void
414 interface::set(const oper_state_t& state)
415 {
416   m_oper = state;
417 }
418
419 void
420 interface::set(const std::string& tag)
421 {
422   m_tag = tag;
423 }
424
425 void
426 interface::set(const counter_t& count, const std::string& stat_type)
427 {
428   if ("rx" == stat_type)
429     m_stats.m_rx = count;
430   else if ("tx" == stat_type)
431     m_stats.m_tx = count;
432   else if ("drops" == stat_type)
433     m_stats.m_drop = count;
434   else if ("rx-unicast" == stat_type)
435     m_stats.m_rx_unicast = count;
436   else if ("tx-unicast" == stat_type)
437     m_stats.m_tx_unicast = count;
438   else if ("rx-multicast" == stat_type)
439     m_stats.m_rx_multicast = count;
440   else if ("tx-multicast" == stat_type)
441     m_stats.m_tx_multicast = count;
442   else if ("rx-broadcast" == stat_type)
443     m_stats.m_rx_broadcast = count;
444   else if ("tx-broadcast" == stat_type)
445     m_stats.m_rx_broadcast = count;
446 }
447
448 const interface::stats_t&
449 interface::get_stats(void) const
450 {
451   return m_stats;
452 }
453
454 void
455 interface::publish_stats()
456 {
457   m_listener->handle_interface_stat(*this);
458 }
459
460 std::ostream&
461 operator<<(std::ostream& os, const interface::stats_t& stats)
462 {
463   os << "["
464      << "rx " << stats.m_rx << " rx-unicast " << stats.m_rx_unicast
465      << " rx-multicast " << stats.m_rx_multicast << " rx-broadcast "
466      << stats.m_rx_broadcast << " tx " << stats.m_tx << " tx-unicast "
467      << stats.m_tx_unicast << " tx-multicast " << stats.m_tx_multicast
468      << " tx-broadcast " << stats.m_tx_broadcast << " drops " << stats.m_drop
469      << "]" << std::endl;
470
471   return (os);
472 }
473
474 void
475 interface::enable_stats_i(interface::stat_listener* el, const stats_type_t& st)
476 {
477   if (el != NULL) {
478     if (stats_type_t::DETAILED == st) {
479       m_stats_type.set(rc_t::NOOP);
480       HW::enqueue(new interface_cmds::collect_detail_stats_change_cmd(
481         m_stats_type, handle_i(), true));
482     }
483     stat_reader::registers(*this);
484     m_listener = el;
485   }
486 }
487
488 void
489 interface::enable_stats(interface::stat_listener* el, const stats_type_t& st)
490 {
491   singular()->enable_stats_i(el, st);
492 }
493
494 void
495 interface::disable_stats_i()
496 {
497   if (m_listener != NULL) {
498     if (stats_type_t::DETAILED == m_stats_type) {
499       HW::enqueue(new interface_cmds::collect_detail_stats_change_cmd(
500         m_stats_type, handle_i(), false));
501     }
502     stat_reader::unregisters(*this);
503     m_listener = NULL;
504   }
505 }
506
507 void
508 interface::disable_stats()
509 {
510   singular()->disable_stats_i();
511 }
512
513 std::shared_ptr<interface>
514 interface::singular_i() const
515 {
516   return (m_db.find_or_add(key(), *this));
517 }
518
519 std::shared_ptr<interface>
520 interface::singular() const
521 {
522   return singular_i();
523 }
524
525 std::shared_ptr<interface>
526 interface::find(const key_t& k)
527 {
528   return (m_db.find(k));
529 }
530
531 std::shared_ptr<interface>
532 interface::find(const handle_t& handle)
533 {
534   return (m_hdl_db[handle].lock());
535 }
536
537 void
538 interface::add(const key_t& key, const HW::item<handle_t>& item)
539 {
540   std::shared_ptr<interface> sp = find(key);
541
542   if (sp && item) {
543     m_hdl_db[item.data()] = sp;
544   }
545 }
546
547 void
548 interface::remove(const HW::item<handle_t>& item)
549 {
550   m_hdl_db.erase(item.data());
551 }
552
553 void
554 interface::dump(std::ostream& os)
555 {
556   db_dump(m_db, os);
557 }
558
559 void
560 interface::enable_events(interface::event_listener& el)
561 {
562   m_events_cmd = std::make_shared<interface_cmds::events_cmd>(el);
563   HW::enqueue(m_events_cmd);
564   HW::write();
565 }
566
567 void
568 interface::disable_events()
569 {
570   m_events_cmd.reset();
571 }
572
573 void
574 interface::event_handler::handle_populate(const client_db::key_t& key)
575 {
576   /*
577    * dump VPP vhost-user interfaces
578    */
579   std::shared_ptr<interface_cmds::vhost_dump_cmd> vcmd =
580     std::make_shared<interface_cmds::vhost_dump_cmd>();
581
582   HW::enqueue(vcmd);
583   HW::write();
584
585   for (auto& vhost_itf_record : *vcmd) {
586     std::shared_ptr<interface> vitf =
587       interface_factory::new_vhost_user_interface(
588         vhost_itf_record.get_payload());
589     VOM_LOG(log_level_t::DEBUG) << " vhost-dump: " << vitf->to_string();
590     OM::commit(key, *vitf);
591   }
592
593   /*
594    * dump VPP af-packet interfaces
595    */
596   std::shared_ptr<interface_cmds::af_packet_dump_cmd> afcmd =
597     std::make_shared<interface_cmds::af_packet_dump_cmd>();
598
599   HW::enqueue(afcmd);
600   HW::write();
601
602   for (auto& af_packet_itf_record : *afcmd) {
603     std::shared_ptr<interface> afitf =
604       interface_factory::new_af_packet_interface(
605         af_packet_itf_record.get_payload());
606     VOM_LOG(log_level_t::DEBUG) << " af_packet-dump: " << afitf->to_string();
607     OM::commit(key, *afitf);
608   }
609
610   /*
611    * dump VPP tapv2 interfaces
612    */
613   std::shared_ptr<tap_interface_cmds::tapv2_dump_cmd> tapv2cmd =
614     std::make_shared<tap_interface_cmds::tapv2_dump_cmd>();
615
616   HW::enqueue(tapv2cmd);
617   HW::write();
618
619   for (auto& tapv2_record : *tapv2cmd) {
620     std::shared_ptr<tap_interface> tapv2itf =
621       interface_factory::new_tap_interface(tapv2_record.get_payload());
622     VOM_LOG(log_level_t::DEBUG) << "tapv2-dump: " << tapv2itf->to_string();
623
624     /*
625      * Write each of the discovered interfaces into the OM,
626      * but disable the HW Command q whilst we do, so that no
627      * commands are sent to VPP
628      */
629     OM::commit(key, *tapv2itf);
630   }
631
632   /*
633    * dump VPP interfaces
634    */
635   std::shared_ptr<interface_cmds::dump_cmd> cmd =
636     std::make_shared<interface_cmds::dump_cmd>();
637
638   HW::enqueue(cmd);
639   HW::write();
640
641   for (auto& itf_record : *cmd) {
642     auto payload = itf_record.get_payload();
643     VOM_LOG(log_level_t::DEBUG) << "dump: [" << payload.sw_if_index
644                                 << " name:" << (char*)payload.interface_name
645                                 << " tag:" << (char*)payload.tag << "]";
646
647     std::shared_ptr<interface> itf = interface_factory::new_interface(payload);
648
649     if (itf && interface::type_t::LOCAL != itf->type()) {
650       VOM_LOG(log_level_t::DEBUG) << "dump: " << itf->to_string();
651       /*
652        * Write each of the discovered interfaces into the OM,
653        * but disable the HW Command q whilst we do, so that no
654        * commands are sent to VPP
655        */
656       OM::commit(key, *itf);
657
658       /**
659        * Get the address configured on the interface
660        */
661       std::shared_ptr<l3_binding_cmds::dump_v4_cmd> dcmd =
662         std::make_shared<l3_binding_cmds::dump_v4_cmd>(
663           l3_binding_cmds::dump_v4_cmd(itf->handle()));
664
665       HW::enqueue(dcmd);
666       HW::write();
667
668       for (auto& l3_record : *dcmd) {
669         auto& payload = l3_record.get_payload();
670         const route::prefix_t pfx = from_api(payload.prefix);
671
672         VOM_LOG(log_level_t::DEBUG) << "dump: " << pfx.to_string();
673
674         l3_binding l3(*itf, pfx);
675         OM::commit(key, l3);
676       }
677     }
678   }
679
680   /*
681    * dump VPP bond interfaces
682    */
683   std::shared_ptr<bond_interface_cmds::dump_cmd> bcmd =
684     std::make_shared<bond_interface_cmds::dump_cmd>();
685
686   HW::enqueue(bcmd);
687   HW::write();
688
689   for (auto& bond_itf_record : *bcmd) {
690     std::shared_ptr<bond_interface> bond_itf =
691       interface_factory::new_bond_interface(bond_itf_record.get_payload());
692
693     VOM_LOG(log_level_t::DEBUG) << " bond-dump:" << bond_itf->to_string();
694
695     /*
696      * Write each of the discovered interfaces into the OM,
697      * but disable the HW Command q whilst we do, so that no
698      * commands are sent to VPP
699      */
700     OM::commit(key, *bond_itf);
701
702     std::shared_ptr<bond_group_binding_cmds::dump_cmd> scmd =
703       std::make_shared<bond_group_binding_cmds::dump_cmd>(
704         bond_group_binding_cmds::dump_cmd(bond_itf->handle()));
705
706     HW::enqueue(scmd);
707     HW::write();
708
709     bond_group_binding::enslaved_itf_t enslaved_itfs;
710
711     for (auto& slave_itf_record : *scmd) {
712       bond_member slave_itf = interface_factory::new_bond_member_interface(
713         slave_itf_record.get_payload());
714
715       VOM_LOG(log_level_t::DEBUG) << " slave-dump:" << slave_itf.to_string();
716
717       /*
718        * Write each of the discovered interfaces into the OM,
719        * but disable the HW Command q whilst we do, so that no
720        * commands are sent to VPP
721        */
722       //      OM::commit(slave_itf->key(), *slave_itf);
723       enslaved_itfs.insert(slave_itf);
724     }
725
726     if (!enslaved_itfs.empty()) {
727       bond_group_binding bid(*bond_itf, enslaved_itfs);
728       /*
729        * Write each of the discovered interfaces into the OM,
730        * but disable the HW Command q whilst we do, so that no
731        * commands are sent to VPP
732        */
733       OM::commit(key, bid);
734     }
735   }
736 }
737
738 interface::event_handler::event_handler()
739 {
740   OM::register_listener(this);
741   inspect::register_handler({ "interface", "intf" }, "interfaces", this);
742 }
743
744 void
745 interface::event_handler::handle_replay()
746 {
747   m_db.replay();
748 }
749
750 dependency_t
751 interface::event_handler::order() const
752 {
753   return (dependency_t::INTERFACE);
754 }
755
756 void
757 interface::event_handler::show(std::ostream& os)
758 {
759   db_dump(m_db, os);
760 }
761
762 } // namespace VOM
763
764 /*
765  * fd.io coding-style-patch-verification: ON
766  *
767  * Local Variables:
768  * eval: (c-set-style "mozilla")
769  * End:
770  */