interface.cpp revision e5ff5a36
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/api_types.hpp"
18#include "vom/bond_group_binding.hpp"
19#include "vom/bond_group_binding_cmds.hpp"
20#include "vom/bond_interface_cmds.hpp"
21#include "vom/interface_cmds.hpp"
22#include "vom/interface_factory.hpp"
23#include "vom/l3_binding_cmds.hpp"
24#include "vom/logger.hpp"
25#include "vom/prefix.hpp"
26#include "vom/singular_db_funcs.hpp"
27#include "vom/stat_reader.hpp"
28#include "vom/tap_interface_cmds.hpp"
29
30namespace VOM {
31/**
32 * A DB of all the interfaces, key on the name
33 */
34singular_db<interface::key_t, interface> interface::m_db;
35
36/**
37 * A DB of all the interfaces, key on VPP's handle
38 */
39std::map<handle_t, std::weak_ptr<interface>> interface::m_hdl_db;
40
41interface::event_handler interface::m_evh;
42
43/**
44 * the event enable command.
45 */
46std::shared_ptr<interface_cmds::events_cmd> interface::m_events_cmd;
47
48/**
49 * Construct a new object matching the desried state
50 */
51interface::interface(const std::string& name,
52                     interface::type_t itf_type,
53                     interface::admin_state_t itf_state,
54                     const std::string& tag)
55  : m_hdl(handle_t::INVALID)
56  , m_name(name)
57  , m_type(itf_type)
58  , m_state(itf_state)
59  , m_table_id(route::DEFAULT_TABLE)
60  , m_l2_address(l2_address_t::ZERO, rc_t::UNSET)
61  , m_stats_type(stats_type_t::NORMAL)
62  , m_stats({})
63  , m_listener(nullptr)
64  , m_oper(oper_state_t::DOWN)
65  , m_tag(tag)
66{
67}
68
69interface::interface(const std::string& name,
70                     interface::type_t itf_type,
71                     interface::admin_state_t itf_state,
72                     const route_domain& rd,
73                     const std::string& tag)
74  : m_hdl(handle_t::INVALID)
75  , m_name(name)
76  , m_type(itf_type)
77  , m_rd(rd.singular())
78  , m_state(itf_state)
79  , m_table_id(m_rd->table_id())
80  , m_l2_address(l2_address_t::ZERO, rc_t::UNSET)
81  , m_stats_type(stats_type_t::NORMAL)
82  , m_stats({})
83  , m_listener(nullptr)
84  , m_oper(oper_state_t::DOWN)
85  , m_tag(tag)
86{
87}
88
89interface::interface(const interface& o)
90  : m_hdl(o.m_hdl)
91  , m_name(o.m_name)
92  , m_type(o.m_type)
93  , m_rd(o.m_rd)
94  , m_state(o.m_state)
95  , m_table_id(o.m_table_id)
96  , m_l2_address(o.m_l2_address)
97  , m_stats_type(o.m_stats_type)
98  , m_stats(o.m_stats)
99  , m_listener(o.m_listener)
100  , m_oper(o.m_oper)
101  , m_tag(o.m_tag)
102{
103}
104
105bool
106interface::operator==(const interface& i) const
107{
108  return ((key() == i.key()) &&
109          (m_l2_address.data() == i.m_l2_address.data()) &&
110          (m_state == i.m_state) && (m_rd == i.m_rd) && (m_type == i.m_type) &&
111          (m_oper == i.m_oper));
112}
113
114interface::event_listener::event_listener()
115  : m_status(rc_t::NOOP)
116{
117}
118
119HW::item<bool>&
120interface::event_listener::status()
121{
122  return (m_status);
123}
124
125interface::stat_listener::stat_listener()
126  : m_status(rc_t::NOOP)
127{
128}
129
130HW::item<bool>&
131interface::stat_listener::status()
132{
133  return (m_status);
134}
135
136/**
137 * Return the interface type
138 */
139const interface::type_t&
140interface::type() const
141{
142  return (m_type);
143}
144
145const handle_t&
146interface::handle() const
147{
148  return (singular()->handle_i());
149}
150
151const handle_t&
152interface::handle_i() const
153{
154  return (m_hdl.data());
155}
156
157const l2_address_t&
158interface::l2_address() const
159{
160  return (m_l2_address.data());
161}
162
163const interface::admin_state_t&
164interface::admin_state() const
165{
166  return (m_state.data());
167}
168
169interface::const_iterator_t
170interface::cbegin()
171{
172  return m_db.begin();
173}
174
175interface::const_iterator_t
176interface::cend()
177{
178  return m_db.end();
179}
180
181void
182interface::sweep()
183{
184  if (m_table_id && (m_table_id.data() != route::DEFAULT_TABLE)) {
185    m_table_id.data() = route::DEFAULT_TABLE;
186    HW::enqueue(
187      new interface_cmds::set_table_cmd(m_table_id, l3_proto_t::IPV4, m_hdl));
188    HW::enqueue(
189      new interface_cmds::set_table_cmd(m_table_id, l3_proto_t::IPV6, m_hdl));
190  }
191
192  if (m_listener) {
193    disable_stats_i();
194  }
195
196  // If the interface is up, bring it down
197  if (m_state && interface::admin_state_t::UP == m_state.data()) {
198    m_state.data() = interface::admin_state_t::DOWN;
199    HW::enqueue(new interface_cmds::state_change_cmd(m_state, m_hdl));
200  }
201
202  if (m_hdl) {
203    std::queue<cmd*> cmds;
204    HW::enqueue(mk_delete_cmd(cmds));
205  }
206  HW::write();
207}
208
209void
210interface::replay()
211{
212  if (m_hdl) {
213    std::queue<cmd*> cmds;
214    HW::enqueue(mk_create_cmd(cmds));
215  }
216
217  if (m_state && interface::admin_state_t::UP == m_state.data()) {
218    HW::enqueue(new interface_cmds::state_change_cmd(m_state, m_hdl));
219  }
220
221  if (m_listener) {
222    enable_stats(m_listener, m_stats_type.data());
223  }
224
225  if (m_table_id && (m_table_id.data() != route::DEFAULT_TABLE)) {
226    HW::enqueue(
227      new interface_cmds::set_table_cmd(m_table_id, l3_proto_t::IPV4, m_hdl));
228    HW::enqueue(
229      new interface_cmds::set_table_cmd(m_table_id, l3_proto_t::IPV6, m_hdl));
230  }
231}
232
233interface::~interface()
234{
235  sweep();
236  release();
237}
238
239void
240interface::release()
241{
242  // not in the DB anymore.
243  m_db.release(m_name, this);
244}
245
246std::string
247interface::to_string() const
248{
249  std::ostringstream s;
250  s << "interface:[" << m_name << " type:" << m_type.to_string()
251    << " hdl:" << m_hdl.to_string() << " l2-address:["
252    << m_l2_address.to_string() << "]";
253
254  if (m_rd) {
255    s << " rd:" << m_rd->to_string();
256  }
257
258  s << " admin-state:" << m_state.to_string()
259    << " oper-state:" << m_oper.to_string();
260
261  if (!m_tag.empty()) {
262    s << " tag:[" << m_tag << "]";
263  }
264
265  s << "]";
266
267  return (s.str());
268}
269
270const std::string&
271interface::name() const
272{
273  return (m_name);
274}
275
276const interface::key_t&
277interface::key() const
278{
279  return (name());
280}
281
282std::queue<cmd*>&
283interface::mk_create_cmd(std::queue<cmd*>& q)
284{
285  if (type_t::LOOPBACK == m_type) {
286    q.push(new interface_cmds::loopback_create_cmd(m_hdl, m_name));
287    q.push(new interface_cmds::set_tag(m_hdl, m_name));
288    /*
289     * set the m_tag for pretty-print
290     */
291    m_tag = m_name;
292  } else if (type_t::BVI == m_type) {
293    q.push(new interface_cmds::bvi_create_cmd(m_hdl, m_name));
294    q.push(new interface_cmds::set_tag(m_hdl, m_name));
295    m_tag = m_name;
296  } else if (type_t::AFPACKET == m_type) {
297    q.push(new interface_cmds::af_packet_create_cmd(m_hdl, m_name));
298    if (!m_tag.empty())
299      q.push(new interface_cmds::set_tag(m_hdl, m_tag));
300  } else if (type_t::TAPV2 == m_type) {
301    if (!m_tag.empty())
302      q.push(new interface_cmds::set_tag(m_hdl, m_tag));
303  } else if (type_t::VHOST == m_type) {
304    q.push(new interface_cmds::vhost_create_cmd(m_hdl, m_name, m_tag));
305  } else {
306    m_hdl.set(rc_t::OK);
307  }
308
309  return (q);
310}
311
312std::queue<cmd*>&
313interface::mk_delete_cmd(std::queue<cmd*>& q)
314{
315  if (type_t::LOOPBACK == m_type) {
316    q.push(new interface_cmds::loopback_delete_cmd(m_hdl));
317  } else if (type_t::BVI == m_type) {
318    q.push(new interface_cmds::bvi_delete_cmd(m_hdl));
319  } else if (type_t::AFPACKET == m_type) {
320    q.push(new interface_cmds::af_packet_delete_cmd(m_hdl, m_name));
321  } else if (type_t::VHOST == m_type) {
322    q.push(new interface_cmds::vhost_delete_cmd(m_hdl, m_name));
323  }
324
325  return (q);
326}
327
328void
329interface::update(const interface& desired)
330{
331  /*
332   * the desired state is always that the interface should be created
333   */
334  if (rc_t::OK != m_hdl.rc()) {
335    std::queue<cmd*> cmds;
336    HW::enqueue(mk_create_cmd(cmds));
337    /*
338     * interface create now, so we can barf early if it fails
339     */
340    HW::write();
341  }
342
343  /*
344   * If the interface is not created do other commands should be issued
345   */
346  if (rc_t::OK != m_hdl.rc())
347    return;
348
349  /*
350   * change the interface state to that which is deisred
351   */
352  if (m_state.update(desired.m_state)) {
353    HW::enqueue(new interface_cmds::state_change_cmd(m_state, m_hdl));
354  }
355
356  /*
357   * change the interface state to that which is deisred
358   */
359  if (m_l2_address.update(desired.m_l2_address)) {
360    HW::enqueue(new interface_cmds::set_mac_cmd(m_l2_address, m_hdl));
361  }
362
363  /*
364   * If the interface is mapped into a route domain, set VPP's
365   * table ID
366   */
367  if (m_rd != desired.m_rd) {
368    /*
369     * changing route domains. need to remove all L3 bindings, swap the table
370     * then reapply the bindings.
371     */
372    auto it = l3_binding::cbegin();
373
374    while (it != l3_binding::cend()) {
375      if (it->second.lock()->itf().key() == key())
376        it->second.lock()->sweep();
377      ++it;
378    }
379    m_rd = desired.m_rd;
380    m_table_id.update(m_rd ? m_rd->table_id() : route::DEFAULT_TABLE);
381    HW::enqueue(
382      new interface_cmds::set_table_cmd(m_table_id, l3_proto_t::IPV4, m_hdl));
383    HW::enqueue(
384      new interface_cmds::set_table_cmd(m_table_id, l3_proto_t::IPV6, m_hdl));
385    HW::write();
386
387    it = l3_binding::cbegin();
388    while (it != l3_binding::cend()) {
389      if (it->second.lock()->itf().key() == key())
390        it->second.lock()->replay(); //(*it->second.lock());
391      ++it;
392    }
393  } else if (!m_table_id && m_rd) {
394    HW::enqueue(
395      new interface_cmds::set_table_cmd(m_table_id, l3_proto_t::IPV4, m_hdl));
396    HW::enqueue(
397      new interface_cmds::set_table_cmd(m_table_id, l3_proto_t::IPV6, m_hdl));
398  }
399}
400
401void
402interface::set(const admin_state_t& state)
403{
404  m_state = state;
405}
406
407void
408interface::set(const l2_address_t& addr)
409{
410  m_l2_address = { addr, rc_t::NOOP };
411}
412
413void
414interface::set(const handle_t& hdl)
415{
416  m_hdl = hdl;
417}
418
419void
420interface::set(const oper_state_t& state)
421{
422  m_oper = state;
423}
424
425void
426interface::set(const std::string& tag)
427{
428  m_tag = tag;
429}
430
431void
432interface::set(const counter_t& count, const std::string& stat_type)
433{
434  if ("rx" == stat_type)
435    m_stats.m_rx = count;
436  else if ("tx" == stat_type)
437    m_stats.m_tx = count;
438  else if ("drops" == stat_type)
439    m_stats.m_drop = count;
440  else if ("rx-unicast" == stat_type)
441    m_stats.m_rx_unicast = count;
442  else if ("tx-unicast" == stat_type)
443    m_stats.m_tx_unicast = count;
444  else if ("rx-multicast" == stat_type)
445    m_stats.m_rx_multicast = count;
446  else if ("tx-multicast" == stat_type)
447    m_stats.m_tx_multicast = count;
448  else if ("rx-broadcast" == stat_type)
449    m_stats.m_rx_broadcast = count;
450  else if ("tx-broadcast" == stat_type)
451    m_stats.m_rx_broadcast = count;
452}
453
454const interface::stats_t&
455interface::get_stats(void) const
456{
457  return m_stats;
458}
459
460void
461interface::publish_stats()
462{
463  m_listener->handle_interface_stat(*this);
464}
465
466std::ostream&
467operator<<(std::ostream& os, const interface::stats_t& stats)
468{
469  os << "["
470     << "rx " << stats.m_rx << " rx-unicast " << stats.m_rx_unicast
471     << " rx-multicast " << stats.m_rx_multicast << " rx-broadcast "
472     << stats.m_rx_broadcast << " tx " << stats.m_tx << " tx-unicast "
473     << stats.m_tx_unicast << " tx-multicast " << stats.m_tx_multicast
474     << " tx-broadcast " << stats.m_tx_broadcast << " drops " << stats.m_drop
475     << "]" << std::endl;
476
477  return (os);
478}
479
480void
481interface::enable_stats_i(interface::stat_listener* el, const stats_type_t& st)
482{
483  if (el != NULL) {
484    if (stats_type_t::DETAILED == st) {
485      m_stats_type.set(rc_t::NOOP);
486      HW::enqueue(new interface_cmds::collect_detail_stats_change_cmd(
487        m_stats_type, handle_i(), true));
488    }
489    stat_reader::registers(*this);
490    m_listener = el;
491  }
492}
493
494void
495interface::enable_stats(interface::stat_listener* el, const stats_type_t& st)
496{
497  singular()->enable_stats_i(el, st);
498}
499
500void
501interface::disable_stats_i()
502{
503  if (m_listener != NULL) {
504    if (stats_type_t::DETAILED == m_stats_type) {
505      HW::enqueue(new interface_cmds::collect_detail_stats_change_cmd(
506        m_stats_type, handle_i(), false));
507    }
508    stat_reader::unregisters(*this);
509    m_listener = NULL;
510  }
511}
512
513void
514interface::disable_stats()
515{
516  singular()->disable_stats_i();
517}
518
519std::shared_ptr<interface>
520interface::singular_i() const
521{
522  return (m_db.find_or_add(key(), *this));
523}
524
525std::shared_ptr<interface>
526interface::singular() const
527{
528  return singular_i();
529}
530
531std::shared_ptr<interface>
532interface::find(const key_t& k)
533{
534  return (m_db.find(k));
535}
536
537std::shared_ptr<interface>
538interface::find(const handle_t& handle)
539{
540  return (m_hdl_db[handle].lock());
541}
542
543void
544interface::add(const key_t& key, const HW::item<handle_t>& item)
545{
546  std::shared_ptr<interface> sp = find(key);
547
548  if (sp && item) {
549    m_hdl_db[item.data()] = sp;
550  }
551}
552
553void
554interface::remove(const HW::item<handle_t>& item)
555{
556  m_hdl_db.erase(item.data());
557}
558
559void
560interface::dump(std::ostream& os)
561{
562  db_dump(m_db, os);
563}
564
565void
566interface::enable_events(interface::event_listener& el)
567{
568  m_events_cmd = std::make_shared<interface_cmds::events_cmd>(el);
569  HW::enqueue(m_events_cmd);
570  HW::write();
571}
572
573void
574interface::disable_events()
575{
576  m_events_cmd.reset();
577}
578
579void
580interface::event_handler::handle_populate(const client_db::key_t& key)
581{
582  /*
583   * dump VPP vhost-user interfaces
584   */
585  std::shared_ptr<interface_cmds::vhost_dump_cmd> vcmd =
586    std::make_shared<interface_cmds::vhost_dump_cmd>();
587
588  HW::enqueue(vcmd);
589  HW::write();
590
591  for (auto& vhost_itf_record : *vcmd) {
592    std::shared_ptr<interface> vitf =
593      interface_factory::new_vhost_user_interface(
594        vhost_itf_record.get_payload());
595    VOM_LOG(log_level_t::DEBUG) << " vhost-dump: " << vitf->to_string();
596    OM::commit(key, *vitf);
597  }
598
599  /*
600   * dump VPP af-packet interfaces
601   */
602  std::shared_ptr<interface_cmds::af_packet_dump_cmd> afcmd =
603    std::make_shared<interface_cmds::af_packet_dump_cmd>();
604
605  HW::enqueue(afcmd);
606  HW::write();
607
608  for (auto& af_packet_itf_record : *afcmd) {
609    std::shared_ptr<interface> afitf =
610      interface_factory::new_af_packet_interface(
611        af_packet_itf_record.get_payload());
612    VOM_LOG(log_level_t::DEBUG) << " af_packet-dump: " << afitf->to_string();
613    OM::commit(key, *afitf);
614  }
615
616  /*
617   * dump VPP tapv2 interfaces
618   */
619  std::shared_ptr<tap_interface_cmds::tapv2_dump_cmd> tapv2cmd =
620    std::make_shared<tap_interface_cmds::tapv2_dump_cmd>();
621
622  HW::enqueue(tapv2cmd);
623  HW::write();
624
625  for (auto& tapv2_record : *tapv2cmd) {
626    std::shared_ptr<tap_interface> tapv2itf =
627      interface_factory::new_tap_interface(tapv2_record.get_payload());
628    VOM_LOG(log_level_t::DEBUG) << "tapv2-dump: " << tapv2itf->to_string();
629
630    /*
631     * Write each of the discovered interfaces into the OM,
632     * but disable the HW Command q whilst we do, so that no
633     * commands are sent to VPP
634     */
635    OM::commit(key, *tapv2itf);
636  }
637
638  /*
639   * dump VPP interfaces
640   */
641  std::shared_ptr<interface_cmds::dump_cmd> cmd =
642    std::make_shared<interface_cmds::dump_cmd>();
643
644  HW::enqueue(cmd);
645  HW::write();
646
647  for (auto& itf_record : *cmd) {
648    auto payload = itf_record.get_payload();
649    VOM_LOG(log_level_t::DEBUG) << "dump: [" << payload.sw_if_index
650                                << " name:" << (char*)payload.interface_name
651                                << " tag:" << (char*)payload.tag << "]";
652
653    std::shared_ptr<interface> itf = interface_factory::new_interface(payload);
654
655    if (itf && interface::type_t::LOCAL != itf->type()) {
656      VOM_LOG(log_level_t::DEBUG) << "dump: " << itf->to_string();
657      /*
658       * Write each of the discovered interfaces into the OM,
659       * but disable the HW Command q whilst we do, so that no
660       * commands are sent to VPP
661       */
662      OM::commit(key, *itf);
663
664      /**
665       * Get the address configured on the interface
666       */
667      std::shared_ptr<l3_binding_cmds::dump_v4_cmd> dcmd =
668        std::make_shared<l3_binding_cmds::dump_v4_cmd>(
669          l3_binding_cmds::dump_v4_cmd(itf->handle()));
670
671      HW::enqueue(dcmd);
672      HW::write();
673
674      for (auto& l3_record : *dcmd) {
675        auto& payload = l3_record.get_payload();
676        const route::prefix_t pfx = from_api(payload.prefix);
677
678        VOM_LOG(log_level_t::DEBUG) << "dump: " << pfx.to_string();
679
680        l3_binding l3(*itf, pfx);
681        OM::commit(key, l3);
682      }
683    }
684  }
685
686  /*
687   * dump VPP bond interfaces
688   */
689  std::shared_ptr<bond_interface_cmds::dump_cmd> bcmd =
690    std::make_shared<bond_interface_cmds::dump_cmd>();
691
692  HW::enqueue(bcmd);
693  HW::write();
694
695  for (auto& bond_itf_record : *bcmd) {
696    std::shared_ptr<bond_interface> bond_itf =
697      interface_factory::new_bond_interface(bond_itf_record.get_payload());
698
699    VOM_LOG(log_level_t::DEBUG) << " bond-dump:" << bond_itf->to_string();
700
701    /*
702     * Write each of the discovered interfaces into the OM,
703     * but disable the HW Command q whilst we do, so that no
704     * commands are sent to VPP
705     */
706    OM::commit(key, *bond_itf);
707
708    std::shared_ptr<bond_group_binding_cmds::dump_cmd> scmd =
709      std::make_shared<bond_group_binding_cmds::dump_cmd>(
710        bond_group_binding_cmds::dump_cmd(bond_itf->handle()));
711
712    HW::enqueue(scmd);
713    HW::write();
714
715    bond_group_binding::enslaved_itf_t enslaved_itfs;
716
717    for (auto& slave_itf_record : *scmd) {
718      bond_member slave_itf = interface_factory::new_bond_member_interface(
719        slave_itf_record.get_payload());
720
721      VOM_LOG(log_level_t::DEBUG) << " slave-dump:" << slave_itf.to_string();
722
723      /*
724       * Write each of the discovered interfaces into the OM,
725       * but disable the HW Command q whilst we do, so that no
726       * commands are sent to VPP
727       */
728      //      OM::commit(slave_itf->key(), *slave_itf);
729      enslaved_itfs.insert(slave_itf);
730    }
731
732    if (!enslaved_itfs.empty()) {
733      bond_group_binding bid(*bond_itf, enslaved_itfs);
734      /*
735       * Write each of the discovered interfaces into the OM,
736       * but disable the HW Command q whilst we do, so that no
737       * commands are sent to VPP
738       */
739      OM::commit(key, bid);
740    }
741  }
742}
743
744interface::event_handler::event_handler()
745{
746  OM::register_listener(this);
747  inspect::register_handler({ "interface", "intf" }, "interfaces", this);
748}
749
750void
751interface::event_handler::handle_replay()
752{
753  m_db.replay();
754}
755
756dependency_t
757interface::event_handler::order() const
758{
759  return (dependency_t::INTERFACE);
760}
761
762void
763interface::event_handler::show(std::ostream& os)
764{
765  db_dump(m_db, os);
766}
767
768} // namespace VOM
769
770/*
771 * fd.io coding-style-patch-verification: ON
772 *
773 * Local Variables:
774 * eval: (c-set-style "mozilla")
775 * End:
776 */
777