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