VOM: favour make_shared
[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/interface_cmds.hpp"
18 #include "vom/interface_factory.hpp"
19 #include "vom/l3_binding_cmds.hpp"
20 #include "vom/logger.hpp"
21 #include "vom/prefix.hpp"
22
23 namespace VOM {
24 /**
25  * A DB of all the interfaces, key on the name
26  */
27 singular_db<interface::key_t, interface> interface::m_db;
28
29 /**
30  * A DB of all the interfaces, key on VPP's handle
31  */
32 std::map<handle_t, std::weak_ptr<interface>> interface::m_hdl_db;
33
34 interface::event_handler interface::m_evh;
35
36 /**
37  * Construct a new object matching the desried state
38  */
39 interface::interface(const std::string& name,
40                      interface::type_t itf_type,
41                      interface::admin_state_t itf_state)
42   : m_hdl(handle_t::INVALID)
43   , m_name(name)
44   , m_type(itf_type)
45   , m_state(itf_state)
46   , m_table_id(route::DEFAULT_TABLE)
47   , m_l2_address(l2_address_t::ZERO, rc_t::UNSET)
48   , m_oper(oper_state_t::DOWN)
49 {
50 }
51
52 interface::interface(const handle_t& handle,
53                      const l2_address_t& l2_address,
54                      const std::string& name,
55                      interface::type_t type,
56                      interface::admin_state_t state)
57   : m_hdl(handle, rc_t::OK)
58   , m_name(name)
59   , m_type(type)
60   , m_state(state, rc_t::OK)
61   , m_table_id(route::DEFAULT_TABLE)
62   , m_l2_address(l2_address)
63   , m_oper(oper_state_t::DOWN)
64 {
65 }
66
67 interface::interface(const std::string& name,
68                      interface::type_t itf_type,
69                      interface::admin_state_t itf_state,
70                      const route_domain& rd)
71   : m_hdl(handle_t::INVALID)
72   , m_name(name)
73   , m_type(itf_type)
74   , m_rd(rd.singular())
75   , m_state(itf_state)
76   , m_table_id(m_rd->table_id())
77   , m_l2_address(l2_address_t::ZERO, rc_t::UNSET)
78   , m_oper(oper_state_t::DOWN)
79 {
80 }
81
82 interface::interface(const interface& o)
83   : m_hdl(o.m_hdl)
84   , m_name(o.m_name)
85   , m_type(o.m_type)
86   , m_rd(o.m_rd)
87   , m_state(o.m_state)
88   , m_table_id(o.m_table_id)
89   , m_l2_address(o.m_l2_address)
90   , m_oper(o.m_oper)
91 {
92 }
93
94 bool
95 interface::operator==(const interface& i) const
96 {
97   return ((key() == i.key()) &&
98           (m_l2_address.data() == i.m_l2_address.data()) &&
99           (m_state == i.m_state) && (m_rd == i.m_rd) && (m_type == i.m_type) &&
100           (m_oper == i.m_oper));
101 }
102
103 interface::event_listener::event_listener()
104   : m_status(rc_t::NOOP)
105 {
106 }
107
108 HW::item<bool>&
109 interface::event_listener::status()
110 {
111   return (m_status);
112 }
113
114 interface::stat_listener::stat_listener()
115   : m_status(rc_t::NOOP)
116 {
117 }
118
119 HW::item<bool>&
120 interface::stat_listener::status()
121 {
122   return (m_status);
123 }
124
125 /**
126  * Return the interface type
127  */
128 const interface::type_t&
129 interface::type() const
130 {
131   return (m_type);
132 }
133
134 const handle_t&
135 interface::handle() const
136 {
137   return (singular()->handle_i());
138 }
139
140 const handle_t&
141 interface::handle_i() const
142 {
143   return (m_hdl.data());
144 }
145
146 const l2_address_t&
147 interface::l2_address() const
148 {
149   return (m_l2_address.data());
150 }
151
152 interface::const_iterator_t
153 interface::cbegin()
154 {
155   return m_db.cbegin();
156 }
157
158 interface::const_iterator_t
159 interface::cend()
160 {
161   return m_db.cend();
162 }
163
164 void
165 interface::sweep()
166 {
167   if (m_table_id && (m_table_id.data() != route::DEFAULT_TABLE)) {
168     m_table_id.data() = route::DEFAULT_TABLE;
169     HW::enqueue(
170       new interface_cmds::set_table_cmd(m_table_id, l3_proto_t::IPV4, m_hdl));
171     HW::enqueue(
172       new interface_cmds::set_table_cmd(m_table_id, l3_proto_t::IPV6, m_hdl));
173   }
174
175   if (m_stats)
176     HW::dequeue(m_stats);
177
178   // If the interface is up, bring it down
179   if (m_state && interface::admin_state_t::UP == m_state.data()) {
180     m_state.data() = interface::admin_state_t::DOWN;
181     HW::enqueue(new interface_cmds::state_change_cmd(m_state, m_hdl));
182   }
183
184   if (m_hdl) {
185     std::queue<cmd*> cmds;
186     HW::enqueue(mk_delete_cmd(cmds));
187   }
188   HW::write();
189 }
190
191 void
192 interface::replay()
193 {
194   if (m_hdl) {
195     std::queue<cmd*> cmds;
196     HW::enqueue(mk_create_cmd(cmds));
197   }
198
199   if (m_state && interface::admin_state_t::UP == m_state.data()) {
200     HW::enqueue(new interface_cmds::state_change_cmd(m_state, m_hdl));
201   }
202
203   if (m_table_id && (m_table_id.data() != route::DEFAULT_TABLE)) {
204     HW::enqueue(
205       new interface_cmds::set_table_cmd(m_table_id, l3_proto_t::IPV4, m_hdl));
206     HW::enqueue(
207       new interface_cmds::set_table_cmd(m_table_id, l3_proto_t::IPV6, m_hdl));
208   }
209 }
210
211 interface::~interface()
212 {
213   sweep();
214   release();
215 }
216
217 void
218 interface::release()
219 {
220   // not in the DB anymore.
221   m_db.release(m_name, this);
222 }
223
224 std::string
225 interface::to_string() const
226 {
227   std::ostringstream s;
228   s << "interface:[" << m_name << " type:" << m_type.to_string()
229     << " hdl:" << m_hdl.to_string() << " l2-address:["
230     << m_l2_address.to_string() << "]";
231
232   if (m_rd) {
233     s << " rd:" << m_rd->to_string();
234   }
235
236   s << " admin-state:" << m_state.to_string()
237     << " oper-state:" << m_oper.to_string() << "]";
238
239   return (s.str());
240 }
241
242 const std::string&
243 interface::name() const
244 {
245   return (m_name);
246 }
247
248 const interface::key_t&
249 interface::key() const
250 {
251   return (name());
252 }
253
254 std::queue<cmd*>&
255 interface::mk_create_cmd(std::queue<cmd*>& q)
256 {
257   if (type_t::LOOPBACK == m_type) {
258     q.push(new interface_cmds::loopback_create_cmd(m_hdl, m_name));
259   } else if (type_t::BVI == m_type) {
260     q.push(new interface_cmds::loopback_create_cmd(m_hdl, m_name));
261     q.push(new interface_cmds::set_tag(m_hdl, m_name));
262   } else if (type_t::AFPACKET == m_type) {
263     q.push(new interface_cmds::af_packet_create_cmd(m_hdl, m_name));
264   } else if (type_t::TAP == m_type) {
265     q.push(new interface_cmds::tap_create_cmd(m_hdl, m_name));
266   }
267
268   return (q);
269 }
270
271 std::queue<cmd*>&
272 interface::mk_delete_cmd(std::queue<cmd*>& q)
273 {
274   if ((type_t::LOOPBACK == m_type) || (type_t::BVI == m_type)) {
275     q.push(new interface_cmds::loopback_delete_cmd(m_hdl));
276   } else if (type_t::AFPACKET == m_type) {
277     q.push(new interface_cmds::af_packet_delete_cmd(m_hdl, m_name));
278   } else if (type_t::TAP == m_type) {
279     q.push(new interface_cmds::tap_delete_cmd(m_hdl));
280   }
281
282   return (q);
283 }
284
285 void
286 interface::update(const interface& desired)
287 {
288   /*
289    * the desired state is always that the interface should be created
290    */
291   if (rc_t::OK != m_hdl.rc()) {
292     std::queue<cmd*> cmds;
293     HW::enqueue(mk_create_cmd(cmds));
294     /*
295      * interface create now, so we can barf early if it fails
296      */
297     HW::write();
298   }
299
300   /*
301    * If the interface is not created do other commands should be issued
302    */
303   if (rc_t::OK != m_hdl.rc())
304     return;
305
306   /*
307    * change the interface state to that which is deisred
308    */
309   if (m_state.update(desired.m_state)) {
310     HW::enqueue(new interface_cmds::state_change_cmd(m_state, m_hdl));
311   }
312
313   /*
314    * change the interface state to that which is deisred
315    */
316   if (m_l2_address.update(desired.m_l2_address)) {
317     HW::enqueue(new interface_cmds::set_mac_cmd(m_l2_address, m_hdl));
318   }
319
320   /*
321    * If the interface is mapped into a route domain, set VPP's
322    * table ID
323    */
324   if (m_rd != desired.m_rd) {
325     /*
326      * changing route domains. need to remove all L3 bindings, swap the table
327      * then reapply the bindings.
328      */
329     auto it = l3_binding::cbegin();
330
331     while (it != l3_binding::cend()) {
332       if (it->second.lock()->itf().key() == key())
333         it->second.lock()->sweep();
334       ++it;
335     }
336     m_rd = desired.m_rd;
337     m_table_id.update(m_rd ? m_rd->table_id() : route::DEFAULT_TABLE);
338     HW::enqueue(
339       new interface_cmds::set_table_cmd(m_table_id, l3_proto_t::IPV4, m_hdl));
340     HW::enqueue(
341       new interface_cmds::set_table_cmd(m_table_id, l3_proto_t::IPV6, m_hdl));
342     HW::write();
343
344     it = l3_binding::cbegin();
345     while (it != l3_binding::cend()) {
346       if (it->second.lock()->itf().key() == key())
347         it->second.lock()->replay(); //(*it->second.lock());
348       ++it;
349     }
350   } else if (!m_table_id && m_rd) {
351     HW::enqueue(
352       new interface_cmds::set_table_cmd(m_table_id, l3_proto_t::IPV4, m_hdl));
353     HW::enqueue(
354       new interface_cmds::set_table_cmd(m_table_id, l3_proto_t::IPV6, m_hdl));
355   }
356 }
357
358 void
359 interface::set(const l2_address_t& addr)
360 {
361   assert(rc_t::UNSET == m_l2_address.rc());
362   m_l2_address.set(rc_t::NOOP);
363   m_l2_address.update(addr);
364 }
365
366 void
367 interface::set(const oper_state_t& state)
368 {
369   m_oper = state;
370 }
371
372 void
373 interface::enable_stats_i(interface::stat_listener& el)
374 {
375   m_stats.reset(new interface_cmds::stats_cmd(el, handle_i()));
376   HW::enqueue(m_stats);
377   HW::write();
378 }
379
380 void
381 interface::enable_stats(interface::stat_listener& el)
382 {
383   singular()->enable_stats_i(el);
384 }
385
386 std::shared_ptr<interface>
387 interface::singular_i() const
388 {
389   return (m_db.find_or_add(key(), *this));
390 }
391
392 std::shared_ptr<interface>
393 interface::singular() const
394 {
395   return singular_i();
396 }
397
398 std::shared_ptr<interface>
399 interface::find(const key_t& k)
400 {
401   return (m_db.find(k));
402 }
403
404 std::shared_ptr<interface>
405 interface::find(const handle_t& handle)
406 {
407   return (m_hdl_db[handle].lock());
408 }
409
410 void
411 interface::add(const key_t& key, const HW::item<handle_t>& item)
412 {
413   std::shared_ptr<interface> sp = find(key);
414
415   if (sp && item) {
416     m_hdl_db[item.data()] = sp;
417   }
418 }
419
420 void
421 interface::remove(const HW::item<handle_t>& item)
422 {
423   m_hdl_db.erase(item.data());
424 }
425
426 void
427 interface::dump(std::ostream& os)
428 {
429   m_db.dump(os);
430 }
431
432 void
433 interface::event_handler::handle_populate(const client_db::key_t& key)
434 {
435   /*
436    * dump VPP current states
437    */
438   std::shared_ptr<interface_cmds::dump_cmd> cmd =
439     std::make_shared<interface_cmds::dump_cmd>();
440
441   HW::enqueue(cmd);
442   HW::write();
443
444   for (auto& itf_record : *cmd) {
445     std::unique_ptr<interface> itf =
446       interface_factory::new_interface(itf_record.get_payload());
447
448     if (itf && interface::type_t::LOCAL != itf->type()) {
449       VOM_LOG(log_level_t::DEBUG) << "dump: " << itf->to_string();
450       /*
451        * Write each of the discovered interfaces into the OM,
452        * but disable the HW Command q whilst we do, so that no
453        * commands are sent to VPP
454        */
455       OM::commit(key, *itf);
456
457       /**
458        * Get the address configured on the interface
459        */
460       std::shared_ptr<l3_binding_cmds::dump_v4_cmd> dcmd =
461         std::make_shared<l3_binding_cmds::dump_v4_cmd>(
462           l3_binding_cmds::dump_v4_cmd(itf->handle()));
463
464       HW::enqueue(dcmd);
465       HW::write();
466
467       for (auto& l3_record : *dcmd) {
468         auto& payload = l3_record.get_payload();
469         const route::prefix_t pfx(payload.is_ipv6, payload.ip,
470                                   payload.prefix_length);
471
472         VOM_LOG(log_level_t::DEBUG) << "dump: " << pfx.to_string();
473
474         l3_binding l3(*itf, pfx);
475         OM::commit(key, l3);
476       }
477     }
478   }
479 }
480
481 interface::event_handler::event_handler()
482 {
483   OM::register_listener(this);
484   inspect::register_handler({ "interface", "intf" }, "interfaces", this);
485 }
486
487 void
488 interface::event_handler::handle_replay()
489 {
490   m_db.replay();
491 }
492
493 dependency_t
494 interface::event_handler::order() const
495 {
496   return (dependency_t::INTERFACE);
497 }
498
499 void
500 interface::event_handler::show(std::ostream& os)
501 {
502   m_db.dump(os);
503 }
504
505 } // namespace VOM
506
507 /*
508  * fd.io coding-style-patch-verification: ON
509  *
510  * Local Variables:
511  * eval: (c-set-style "mozilla")
512  * End:
513  */