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