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