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