VPP Object Model (VOM)
[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/cmd.hpp"
18 #include "vom/l3_binding.hpp"
19 #include "vom/logger.hpp"
20 #include "vom/prefix.hpp"
21
22 namespace VOM {
23 /**
24  * A DB of all the interfaces, key on the name
25  */
26 singular_db<const std::string, interface> interface::m_db;
27
28 /**
29  * A DB of all the interfaces, key on VPP's handle
30  */
31 std::map<handle_t, std::weak_ptr<interface>> interface::m_hdl_db;
32
33 interface::event_handler interface::m_evh;
34
35 /**
36  * Construct a new object matching the desried state
37  */
38 interface::interface(const std::string& name,
39                      interface::type_t itf_type,
40                      interface::admin_state_t itf_state)
41   : m_hdl(handle_t::INVALID)
42   , m_name(name)
43   , m_type(itf_type)
44   , m_state(itf_state)
45   , m_table_id(route::DEFAULT_TABLE)
46   , m_l2_address(l2_address_t::ZERO, rc_t::UNSET)
47   , m_oper(oper_state_t::DOWN)
48 {
49 }
50
51 interface::interface(const handle_t& handle,
52                      const l2_address_t& l2_address,
53                      const std::string& name,
54                      interface::type_t type,
55                      interface::admin_state_t state)
56   : m_hdl(handle)
57   , m_name(name)
58   , m_type(type)
59   , m_state(state)
60   , m_table_id(route::DEFAULT_TABLE)
61   , m_l2_address(l2_address)
62   , m_oper(oper_state_t::DOWN)
63 {
64 }
65
66 interface::interface(const std::string& name,
67                      interface::type_t itf_type,
68                      interface::admin_state_t itf_state,
69                      const route_domain& rd)
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_oper(oper_state_t::DOWN)
78 {
79 }
80
81 interface::interface(const interface& o)
82   : m_hdl(o.m_hdl)
83   , m_name(o.m_name)
84   , m_type(o.m_type)
85   , m_rd(o.m_rd)
86   , m_state(o.m_state)
87   , m_table_id(o.m_table_id)
88   , m_l2_address(o.m_l2_address)
89   , m_oper(o.m_oper)
90 {
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 (m_hdl.data());
128 }
129
130 const l2_address_t&
131 interface::l2_address() const
132 {
133   return (m_l2_address.data());
134 }
135
136 interface::const_iterator_t
137 interface::cbegin()
138 {
139   return m_db.cbegin();
140 }
141
142 interface::const_iterator_t
143 interface::cend()
144 {
145   return m_db.cend();
146 }
147
148 void
149 interface::sweep()
150 {
151   if (m_table_id) {
152     m_table_id.data() = route::DEFAULT_TABLE;
153     HW::enqueue(new set_table_cmd(m_table_id, l3_proto_t::IPV4, m_hdl));
154     HW::enqueue(new set_table_cmd(m_table_id, l3_proto_t::IPV6, m_hdl));
155   }
156
157   // If the interface is up, bring it down
158   if (m_state && interface::admin_state_t::UP == m_state.data()) {
159     m_state.data() = interface::admin_state_t::DOWN;
160     HW::enqueue(new state_change_cmd(m_state, m_hdl));
161   }
162   if (m_hdl) {
163     std::queue<cmd*> cmds;
164     HW::enqueue(mk_delete_cmd(cmds));
165   }
166   HW::write();
167 }
168
169 void
170 interface::replay()
171 {
172   if (m_hdl) {
173     std::queue<cmd*> cmds;
174     HW::enqueue(mk_create_cmd(cmds));
175   }
176
177   if (m_state && interface::admin_state_t::UP == m_state.data()) {
178     HW::enqueue(new state_change_cmd(m_state, m_hdl));
179   }
180
181   if (m_table_id) {
182     HW::enqueue(new set_table_cmd(m_table_id, l3_proto_t::IPV4, m_hdl));
183     HW::enqueue(new set_table_cmd(m_table_id, l3_proto_t::IPV6, m_hdl));
184   }
185 }
186
187 interface::~interface()
188 {
189   sweep();
190   release();
191 }
192
193 void
194 interface::release()
195 {
196   // not in the DB anymore.
197   m_db.release(m_name, this);
198 }
199
200 std::string
201 interface::to_string() const
202 {
203   std::ostringstream s;
204   s << "interface:[" << m_name << " type:" << m_type.to_string()
205     << " hdl:" << m_hdl.to_string()
206     << " l2-address:" << m_l2_address.to_string();
207
208   if (m_rd) {
209     s << " rd:" << m_rd->to_string();
210   }
211
212   s << " admin-state:" << m_state.to_string()
213     << " oper-state:" << m_oper.to_string() << "]";
214
215   return (s.str());
216 }
217
218 const std::string&
219 interface::name() const
220 {
221   return (m_name);
222 }
223
224 const interface::key_type&
225 interface::key() const
226 {
227   return (name());
228 }
229
230 std::queue<cmd*>&
231 interface::mk_create_cmd(std::queue<cmd*>& q)
232 {
233   if (type_t::LOOPBACK == m_type) {
234     q.push(new loopback_create_cmd(m_hdl, m_name));
235   } else if (type_t::BVI == m_type) {
236     q.push(new loopback_create_cmd(m_hdl, m_name));
237     q.push(new set_tag(m_hdl, m_name));
238   } else if (type_t::AFPACKET == m_type) {
239     q.push(new af_packet_create_cmd(m_hdl, m_name));
240   } else if (type_t::TAP == m_type) {
241     q.push(new tap_create_cmd(m_hdl, m_name));
242   }
243
244   return (q);
245 }
246
247 std::queue<cmd*>&
248 interface::mk_delete_cmd(std::queue<cmd*>& q)
249 {
250   if ((type_t::LOOPBACK == m_type) || (type_t::BVI == m_type)) {
251     q.push(new loopback_delete_cmd(m_hdl));
252   } else if (type_t::AFPACKET == m_type) {
253     q.push(new af_packet_delete_cmd(m_hdl, m_name));
254   } else if (type_t::TAP == m_type) {
255     q.push(new tap_delete_cmd(m_hdl));
256   }
257
258   return (q);
259 }
260
261 void
262 interface::update(const interface& desired)
263 {
264   /*
265  * the desired state is always that the interface should be created
266  */
267   if (rc_t::OK != m_hdl.rc()) {
268     std::queue<cmd*> cmds;
269     HW::enqueue(mk_create_cmd(cmds));
270   }
271
272   /*
273  * change the interface state to that which is deisred
274  */
275   if (m_state.update(desired.m_state)) {
276     HW::enqueue(new state_change_cmd(m_state, m_hdl));
277   }
278
279   /*
280  * change the interface state to that which is deisred
281  */
282   if (m_l2_address.update(desired.m_l2_address)) {
283     HW::enqueue(new set_mac_cmd(m_l2_address, m_hdl));
284   }
285
286   /*
287  * If the interface is mapped into a route domain, set VPP's
288  * table ID
289  */
290   if (!m_table_id && m_rd) {
291     HW::enqueue(new set_table_cmd(m_table_id, l3_proto_t::IPV4, m_hdl));
292     HW::enqueue(new set_table_cmd(m_table_id, l3_proto_t::IPV6, m_hdl));
293   }
294 }
295
296 void
297 interface::set(const l2_address_t& addr)
298 {
299   assert(rc_t::UNSET == m_l2_address.rc());
300   m_l2_address.set(rc_t::NOOP);
301   m_l2_address.update(addr);
302 }
303
304 void
305 interface::set(const oper_state_t& state)
306 {
307   m_oper = state;
308 }
309
310 std::shared_ptr<interface>
311 interface::singular_i() const
312 {
313   return (m_db.find_or_add(name(), *this));
314 }
315
316 std::shared_ptr<interface>
317 interface::singular() const
318 {
319   return singular_i();
320 }
321
322 std::shared_ptr<interface>
323 interface::find(const std::string& name)
324 {
325   return (m_db.find(name));
326 }
327
328 std::shared_ptr<interface>
329 interface::find(const handle_t& handle)
330 {
331   return (m_hdl_db[handle].lock());
332 }
333
334 void
335 interface::add(const std::string& name, const HW::item<handle_t>& item)
336 {
337   std::shared_ptr<interface> sp = find(name);
338
339   if (sp && item) {
340     m_hdl_db[item.data()] = sp;
341   }
342 }
343
344 void
345 interface::remove(const HW::item<handle_t>& item)
346 {
347   m_hdl_db.erase(item.data());
348 }
349
350 void
351 interface::dump(std::ostream& os)
352 {
353   m_db.dump(os);
354 }
355
356 void
357 interface::event_handler::handle_populate(const client_db::key_t& key)
358 {
359   /*
360  * dump VPP current states
361  */
362   std::shared_ptr<interface::dump_cmd> cmd(new interface::dump_cmd());
363
364   HW::enqueue(cmd);
365   HW::write();
366
367   for (auto& itf_record : *cmd) {
368     std::unique_ptr<interface> itf =
369       interface::new_interface(itf_record.get_payload());
370
371     if (itf && interface::type_t::LOCAL != itf->type()) {
372       VOM_LOG(log_level_t::DEBUG) << "dump: " << itf->to_string();
373       /*
374  * Write each of the discovered interfaces into the OM,
375  * but disable the HW Command q whilst we do, so that no
376  * commands are sent to VPP
377  */
378       OM::commit(key, *itf);
379
380       /**
381  * Get the address configured on the interface
382  */
383       std::shared_ptr<l3_binding::dump_v4_cmd> dcmd =
384         std::make_shared<l3_binding::dump_v4_cmd>(
385           l3_binding::dump_v4_cmd(itf->handle()));
386
387       HW::enqueue(dcmd);
388       HW::write();
389
390       for (auto& l3_record : *dcmd) {
391         auto& payload = l3_record.get_payload();
392         const route::prefix_t pfx(payload.is_ipv6, payload.ip,
393                                   payload.prefix_length);
394
395         VOM_LOG(log_level_t::DEBUG) << "dump: " << pfx.to_string();
396
397         l3_binding l3(*itf, pfx);
398         OM::commit(key, l3);
399       }
400     }
401   }
402 }
403
404 interface::event_handler::event_handler()
405 {
406   OM::register_listener(this);
407   inspect::register_handler({ "interface", "intf" }, "interfaces", this);
408 }
409
410 void
411 interface::event_handler::handle_replay()
412 {
413   m_db.replay();
414 }
415
416 dependency_t
417 interface::event_handler::order() const
418 {
419   return (dependency_t::INTERFACE);
420 }
421
422 void
423 interface::event_handler::show(std::ostream& os)
424 {
425   m_db.dump(os);
426 }
427 }
428 /*
429  * fd.io coding-style-patch-verification: ON
430  *
431  * Local Variables:
432  * eval: (c-set-style "mozilla")
433  * End:
434  */