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