VOM: interface RD update reconfigures L3 bindings
[vpp.git] / test / ext / vom_test.cpp
1 /*
2  * Test suite for class VppOM
3  *
4  * Copyright (c) 2017 Cisco Systems, Inc. and others.  All rights reserved.
5  *
6  * This program and the accompanying materials are made available under the
7  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
8  * and is available at http://www.eclipse.org/legal/epl-v10.html
9  */
10 #define BOOST_TEST_MODULE "VPP OBJECT MODEL"
11 #define BOOST_TEST_DYN_LINK
12
13 #include <boost/test/unit_test.hpp>
14 #include <boost/assign/list_inserter.hpp>
15
16
17 #include <iostream>
18 #include <deque>
19
20 #include "vom/om.hpp"
21 #include "vom/interface.hpp"
22 #include "vom/interface_cmds.hpp"
23 #include "vom/l2_binding.hpp"
24 #include "vom/l2_binding_cmds.hpp"
25 #include "vom/l3_binding.hpp"
26 #include "vom/l3_binding_cmds.hpp"
27 #include "vom/bridge_domain.hpp"
28 #include "vom/bridge_domain_entry.hpp"
29 #include "vom/bridge_domain_arp_entry.hpp"
30 #include "vom/bridge_domain_cmds.hpp"
31 #include "vom/bridge_domain_entry_cmds.hpp"
32 #include "vom/bridge_domain_arp_entry_cmds.hpp"
33 #include "vom/prefix.hpp"
34 #include "vom/route.hpp"
35 #include "vom/route_cmds.hpp"
36 #include "vom/route_domain.hpp"
37 #include "vom/route_domain_cmds.hpp"
38 #include "vom/vxlan_tunnel.hpp"
39 #include "vom/vxlan_tunnel_cmds.hpp"
40 #include "vom/sub_interface.hpp"
41 #include "vom/sub_interface_cmds.hpp"
42 #include "vom/acl_list.hpp"
43 #include "vom/acl_binding.hpp"
44 #include "vom/acl_list_cmds.hpp"
45 #include "vom/acl_binding_cmds.hpp"
46 #include "vom/acl_l3_rule.hpp"
47 #include "vom/acl_l2_rule.hpp"
48 #include "vom/arp_proxy_config.hpp"
49 #include "vom/arp_proxy_binding.hpp"
50 #include "vom/arp_proxy_config_cmds.hpp"
51 #include "vom/arp_proxy_binding_cmds.hpp"
52 #include "vom/ip_unnumbered.hpp"
53 #include "vom/ip_unnumbered_cmds.hpp"
54 #include "vom/interface_ip6_nd.hpp"
55 #include "vom/interface_span.hpp"
56 #include "vom/interface_span_cmds.hpp"
57 #include "vom/neighbour.hpp"
58 #include "vom/neighbour_cmds.hpp"
59 #include "vom/nat_static.hpp"
60 #include "vom/nat_static_cmds.hpp"
61 #include "vom/nat_binding.hpp"
62 #include "vom/nat_binding_cmds.hpp"
63
64 using namespace boost;
65 using namespace VOM;
66
67 /**
68  * An expectation exception
69  */
70 class ExpException
71 {
72 public:
73     ExpException(unsigned int number)
74     {
75         // a neat place to add a break point
76         std::cout << "  ExpException here: " << number << std::endl;
77     }
78 };
79
80 class MockListener : public interface::event_listener,
81                      public interface::stat_listener
82 {
83     void handle_interface_stat(interface_cmds::stats_cmd *cmd)
84     {
85     }
86     void handle_interface_event(interface_cmds::events_cmd *cmd)
87     {
88     }
89 };
90
91 class MockCmdQ : public HW::cmd_q
92 {
93 public:
94     MockCmdQ():
95         m_strict_order(true)
96     {
97     }
98     virtual ~MockCmdQ()
99     {
100     }
101     void expect(cmd *f)
102     {
103         m_exp_queue.push_back(f);
104     }
105     void enqueue(cmd *f)
106     {
107         m_act_queue.push_back(f);
108     }
109     void enqueue(std::queue<cmd*> &cmds)
110     {
111         while (cmds.size())
112         {
113             m_act_queue.push_back(cmds.front());
114             cmds.pop();
115         }
116     }
117     void enqueue(std::shared_ptr<cmd> f)
118     {
119         m_act_queue.push_back(f.get());
120     }
121
122     void dequeue(cmd *f)
123     {
124     }
125
126     void dequeue(std::shared_ptr<cmd> cmd)
127     {
128     }
129
130     void strict_order(bool on)
131     {
132         m_strict_order = on;
133     }
134
135     bool is_empty()
136     {
137         return ((0 == m_exp_queue.size()) &&
138                 (0 == m_act_queue.size()));
139     }
140
141     rc_t write()
142     {
143         cmd *f_exp, *f_act;
144         rc_t rc = rc_t::OK;
145
146         while (m_act_queue.size())
147         {
148             bool matched = false;
149             auto it_exp = m_exp_queue.begin();
150             auto it_act = m_act_queue.begin();
151
152             f_act = *it_act;
153
154             std::cout << " Act: " << f_act->to_string() << std::endl;
155             while (it_exp != m_exp_queue.end())
156             {
157                 f_exp = *it_exp;
158                 try
159                 {
160                     std::cout << "  Exp: " << f_exp->to_string() << std::endl;
161
162                     if (typeid(*f_exp) != typeid(*f_act))
163                     {
164                         throw ExpException(1);
165                     }
166
167                     if (typeid(*f_exp) == typeid(interface_cmds::af_packet_create_cmd))
168                     {
169                         rc = handle_derived<interface_cmds::af_packet_create_cmd>(f_exp, f_act);
170                     }
171                     else if (typeid(*f_exp) == typeid(interface_cmds::loopback_create_cmd))
172                     {
173                         rc = handle_derived<interface_cmds::loopback_create_cmd>(f_exp, f_act);
174                     }
175                     else if (typeid(*f_exp) == typeid(interface_cmds::loopback_delete_cmd))
176                     {
177                         rc = handle_derived<interface_cmds::loopback_delete_cmd>(f_exp, f_act);
178                     }
179                     else if (typeid(*f_exp) == typeid(interface_cmds::af_packet_delete_cmd))
180                     {
181                         rc = handle_derived<interface_cmds::af_packet_delete_cmd>(f_exp, f_act);
182                     }
183                     else if (typeid(*f_exp) == typeid(interface_cmds::state_change_cmd))
184                     {
185                         rc = handle_derived<interface_cmds::state_change_cmd>(f_exp, f_act);
186                     }
187                     else if (typeid(*f_exp) == typeid(interface_cmds::set_table_cmd))
188                     {
189                         rc = handle_derived<interface_cmds::set_table_cmd>(f_exp, f_act);
190                     }
191                     else if (typeid(*f_exp) == typeid(interface_cmds::set_mac_cmd))
192                     {
193                         rc = handle_derived<interface_cmds::set_mac_cmd>(f_exp, f_act);
194                     }
195                     else if (typeid(*f_exp) == typeid(interface_cmds::set_tag))
196                     {
197                         rc = handle_derived<interface_cmds::set_tag>(f_exp, f_act);
198                     }
199                     else if (typeid(*f_exp) == typeid(route_domain_cmds::create_cmd))
200                     {
201                         rc = handle_derived<route_domain_cmds::create_cmd>(f_exp, f_act);
202                     }
203                     else if (typeid(*f_exp) == typeid(route_domain_cmds::delete_cmd))
204                     {
205                         rc = handle_derived<route_domain_cmds::delete_cmd>(f_exp, f_act);
206                     }
207                     else if (typeid(*f_exp) == typeid(route::ip_route_cmds::update_cmd))
208                     {
209                         rc = handle_derived<route::ip_route_cmds::update_cmd>(f_exp, f_act);
210                     }
211                     else if (typeid(*f_exp) == typeid(route::ip_route_cmds::delete_cmd))
212                     {
213                         rc = handle_derived<route::ip_route_cmds::delete_cmd>(f_exp, f_act);
214                     }
215                     else if (typeid(*f_exp) == typeid(neighbour_cmds::create_cmd))
216                     {
217                         rc = handle_derived<neighbour_cmds::create_cmd>(f_exp, f_act);
218                     }
219                     else if (typeid(*f_exp) == typeid(neighbour_cmds::delete_cmd))
220                     {
221                         rc = handle_derived<neighbour_cmds::delete_cmd>(f_exp, f_act);
222                     }
223                     else if (typeid(*f_exp) == typeid(l3_binding_cmds::bind_cmd))
224                     {
225                         rc = handle_derived<l3_binding_cmds::bind_cmd>(f_exp, f_act);
226                     }
227                     else if (typeid(*f_exp) == typeid(l3_binding_cmds::unbind_cmd))
228                     {
229                         rc = handle_derived<l3_binding_cmds::unbind_cmd>(f_exp, f_act);
230                     }
231                     else if (typeid(*f_exp) == typeid(bridge_domain_cmds::create_cmd))
232                     {
233                         rc = handle_derived<bridge_domain_cmds::create_cmd>(f_exp, f_act);
234                     }
235                     else if (typeid(*f_exp) == typeid(bridge_domain_cmds::delete_cmd))
236                     {
237                         rc = handle_derived<bridge_domain_cmds::delete_cmd>(f_exp, f_act);
238                     }
239                     else if (typeid(*f_exp) == typeid(bridge_domain_entry_cmds::create_cmd))
240                     {
241                         rc = handle_derived<bridge_domain_entry_cmds::create_cmd>(f_exp, f_act);
242                     }
243                     else if (typeid(*f_exp) == typeid(bridge_domain_entry_cmds::delete_cmd))
244                     {
245                         rc = handle_derived<bridge_domain_entry_cmds::delete_cmd>(f_exp, f_act);
246                     }
247                     else if (typeid(*f_exp) == typeid(bridge_domain_arp_entry_cmds::create_cmd))
248                     {
249                         rc = handle_derived<bridge_domain_arp_entry_cmds::create_cmd>(f_exp, f_act);
250                     }
251                     else if (typeid(*f_exp) == typeid(bridge_domain_arp_entry_cmds::delete_cmd))
252                     {
253                         rc = handle_derived<bridge_domain_arp_entry_cmds::delete_cmd>(f_exp, f_act);
254                     }
255                     else if (typeid(*f_exp) == typeid(l2_binding_cmds::bind_cmd))
256                     {
257                         rc = handle_derived<l2_binding_cmds::bind_cmd>(f_exp, f_act);
258                     }
259                     else if (typeid(*f_exp) == typeid(l2_binding_cmds::unbind_cmd))
260                     {
261                         rc = handle_derived<l2_binding_cmds::unbind_cmd>(f_exp, f_act);
262                     }
263                     else if (typeid(*f_exp) == typeid(l2_binding_cmds::set_vtr_op_cmd))
264                     {
265                         rc = handle_derived<l2_binding_cmds::set_vtr_op_cmd>(f_exp, f_act);
266                     }
267                     else if (typeid(*f_exp) == typeid(vxlan_tunnel_cmds::create_cmd))
268                     {
269                         rc = handle_derived<vxlan_tunnel_cmds::create_cmd>(f_exp, f_act);
270                     }
271                     else if (typeid(*f_exp) == typeid(vxlan_tunnel_cmds::delete_cmd))
272                     {
273                         rc = handle_derived<vxlan_tunnel_cmds::delete_cmd>(f_exp, f_act);
274                     }
275                     else if (typeid(*f_exp) == typeid(sub_interface_cmds::create_cmd))
276                     {
277                         rc = handle_derived<sub_interface_cmds::create_cmd>(f_exp, f_act);
278                     }
279                     else if (typeid(*f_exp) == typeid(sub_interface_cmds::delete_cmd))
280                     {
281                         rc = handle_derived<sub_interface_cmds::delete_cmd>(f_exp, f_act);
282                     }
283                     else if (typeid(*f_exp) == typeid(ACL::list_cmds::l3_update_cmd))
284                     {
285                         rc = handle_derived<ACL::list_cmds::l3_update_cmd>(f_exp, f_act);
286                     }
287                     else if (typeid(*f_exp) == typeid(ACL::list_cmds::l3_delete_cmd))
288                     {
289                         rc = handle_derived<ACL::list_cmds::l3_delete_cmd>(f_exp, f_act);
290                     }
291                     else if (typeid(*f_exp) == typeid(ACL::binding_cmds::l3_bind_cmd))
292                     {
293                         rc = handle_derived<ACL::binding_cmds::l3_bind_cmd>(f_exp, f_act);
294                     }
295                     else if (typeid(*f_exp) == typeid(ACL::binding_cmds::l3_unbind_cmd))
296                     {
297                         rc = handle_derived<ACL::binding_cmds::l3_unbind_cmd>(f_exp, f_act);
298                     }
299                     else if (typeid(*f_exp) == typeid(ACL::list_cmds::l2_update_cmd))
300                     {
301                         rc = handle_derived<ACL::list_cmds::l2_update_cmd>(f_exp, f_act);
302                     }
303                     else if (typeid(*f_exp) == typeid(ACL::list_cmds::l2_delete_cmd))
304                     {
305                         rc = handle_derived<ACL::list_cmds::l2_delete_cmd>(f_exp, f_act);
306                     }
307                     else if (typeid(*f_exp) == typeid(ACL::binding_cmds::l2_bind_cmd))
308                     {
309                         rc = handle_derived<ACL::binding_cmds::l2_bind_cmd>(f_exp, f_act);
310                     }
311                     else if (typeid(*f_exp) == typeid(ACL::binding_cmds::l2_unbind_cmd))
312                     {
313                         rc = handle_derived<ACL::binding_cmds::l2_unbind_cmd>(f_exp, f_act);
314                     }
315                     else if (typeid(*f_exp) == typeid(arp_proxy_binding_cmds::bind_cmd))
316                     {
317                         rc = handle_derived<arp_proxy_binding_cmds::bind_cmd>(f_exp, f_act);
318                     }
319                     else if (typeid(*f_exp) == typeid(arp_proxy_binding_cmds::unbind_cmd))
320                     {
321                         rc = handle_derived<arp_proxy_binding_cmds::unbind_cmd>(f_exp, f_act);
322                     }
323                     else if (typeid(*f_exp) == typeid(arp_proxy_config_cmds::config_cmd))
324                     {
325                         rc = handle_derived<arp_proxy_config_cmds::config_cmd>(f_exp, f_act);
326                     }
327                     else if (typeid(*f_exp) == typeid(arp_proxy_config_cmds::unconfig_cmd))
328                     {
329                         rc = handle_derived<arp_proxy_config_cmds::unconfig_cmd>(f_exp, f_act);
330                     }
331                     else if (typeid(*f_exp) == typeid(ip_unnumbered_cmds::config_cmd))
332                     {
333                         rc = handle_derived<ip_unnumbered_cmds::config_cmd>(f_exp, f_act);
334                     }
335                     else if (typeid(*f_exp) == typeid(ip_unnumbered_cmds::unconfig_cmd))
336                     {
337                         rc = handle_derived<ip_unnumbered_cmds::unconfig_cmd>(f_exp, f_act);
338                     }
339                     else if (typeid(*f_exp) == typeid(ip6nd_ra_config::config_cmd))
340                     {
341                         rc = handle_derived<ip6nd_ra_config::config_cmd>(f_exp, f_act);
342                     }
343                     else if (typeid(*f_exp) == typeid(ip6nd_ra_config::unconfig_cmd))
344                     {
345                         rc = handle_derived<ip6nd_ra_config::unconfig_cmd>(f_exp, f_act);
346                     }
347                     else if (typeid(*f_exp) == typeid(ip6nd_ra_prefix::config_cmd))
348                     {
349                         rc = handle_derived<ip6nd_ra_prefix::config_cmd>(f_exp, f_act);
350                     }
351                     else if (typeid(*f_exp) == typeid(ip6nd_ra_prefix::unconfig_cmd))
352                     {
353                         rc = handle_derived<ip6nd_ra_prefix::unconfig_cmd>(f_exp, f_act);
354                     }
355                     else if (typeid(*f_exp) == typeid(interface_span_cmds::config_cmd))
356                     {
357                         rc = handle_derived<interface_span_cmds::config_cmd>(f_exp, f_act);
358                     }
359                     else if (typeid(*f_exp) == typeid(interface_span_cmds::unconfig_cmd))
360                     {
361                         rc = handle_derived<interface_span_cmds::unconfig_cmd>(f_exp, f_act);
362                     }
363                     else if (typeid(*f_exp) == typeid(nat_static_cmds::create_44_cmd))
364                     {
365                         rc = handle_derived<nat_static_cmds::create_44_cmd>(f_exp, f_act);
366                     }
367                     else if (typeid(*f_exp) == typeid(nat_static_cmds::delete_44_cmd))
368                     {
369                         rc = handle_derived<nat_static_cmds::delete_44_cmd>(f_exp, f_act);
370                     }
371                     else if (typeid(*f_exp) == typeid(nat_binding_cmds::bind_44_input_cmd))
372                     {
373                         rc = handle_derived<nat_binding_cmds::bind_44_input_cmd>(f_exp, f_act);
374                     }
375                     else if (typeid(*f_exp) == typeid(nat_binding_cmds::unbind_44_input_cmd))
376                     {
377                         rc = handle_derived<nat_binding_cmds::unbind_44_input_cmd>(f_exp, f_act);
378                     }
379                     else if (typeid(*f_exp) == typeid(interface_cmds::events_cmd))
380                     {
381                         rc = handle_derived<interface_cmds::events_cmd>(f_exp, f_act);
382                     }
383                     else
384                     {
385                         throw ExpException(2);
386                     }
387
388                     // if we get here then we found the match.
389                     m_exp_queue.erase(it_exp);
390                     m_act_queue.erase(it_act);
391                     delete f_exp;
392                     delete f_act;
393
394                     // return any injected failures to the agent
395                     if (rc_t::OK != rc && rc_t::NOOP != rc)
396                     {
397                         return (rc);
398                     }
399
400                     matched = true;
401                     break;
402                 }
403                 catch (ExpException &e)
404                 {
405                     // The expected and actual do not match
406                     if (m_strict_order)
407                     {
408                         // in strict ordering mode this is fatal, so rethrow
409                         throw e;
410                     }
411                     else
412                     {
413                         // move the iterator onto the next in the expected list and
414                         // check for a match
415                         ++it_exp;
416                     }
417                 }
418             }
419
420             if (!matched)
421                 throw ExpException(3);
422         }
423
424         return (rc);
425     }
426 private:
427
428     template <typename T>
429     rc_t handle_derived(const cmd *f_exp, cmd *f_act)
430     {
431         const T *i_exp;
432         T *i_act;
433
434         i_exp = dynamic_cast<const T*>(f_exp);
435         i_act = dynamic_cast<T*>(f_act);
436         if (!(*i_exp == *i_act))
437         {
438             throw ExpException(4);
439         }
440         // pass the data and return code to the agent
441         i_act->item() = i_exp->item();
442
443         return (i_act->item().rc());
444     }
445
446     // The Q to push the expectations on
447     std::deque<cmd*> m_exp_queue;
448
449     // the queue to push the actual events on
450     std::deque<cmd*> m_act_queue;
451
452     // control whether the expected queue is strictly ordered.
453     bool m_strict_order;
454 };
455
456 class VppInit {
457 public:
458     std::string name;
459     MockCmdQ *f;
460
461     VppInit()
462         : name("vpp-ut"),
463           f(new MockCmdQ())
464     {
465         HW::init(f);
466         OM::init();
467         logger().set(log_level_t::DEBUG);
468     }
469
470     ~VppInit() {
471         delete f;
472     }
473 };
474
475 BOOST_AUTO_TEST_SUITE(VppOM_test)
476
477 #define TRY_CHECK_RC(stmt)                    \
478 {                                             \
479     try {                                     \
480         BOOST_CHECK(rc_t::OK == stmt);        \
481     }                                         \
482     catch (ExpException &e)                   \
483     {                                         \
484         BOOST_CHECK(false);                   \
485     }                                         \
486     BOOST_CHECK(vi.f->is_empty());            \
487 }
488
489 #define TRY_CHECK(stmt)                       \
490 {                                             \
491     try {                                     \
492         stmt;                                 \
493     }                                         \
494     catch (ExpException &e)                   \
495     {                                         \
496         BOOST_CHECK(false);                   \
497     }                                         \
498     BOOST_CHECK(vi.f->is_empty());            \
499 }
500
501 #define ADD_EXPECT(stmt)                      \
502     vi.f->expect(new stmt)
503
504 #define STRICT_ORDER_OFF()                        \
505     vi.f->strict_order(false)
506
507 BOOST_AUTO_TEST_CASE(test_interface) {
508     VppInit vi;
509     const std::string go = "GeorgeOrwell";
510     const std::string js = "JohnSteinbeck";
511     rc_t rc = rc_t::OK;
512
513     /*
514      * George creates and deletes the interface
515      */
516     std::string itf1_name = "afpacket1";
517     interface itf1(itf1_name,
518                    interface::type_t::AFPACKET,
519                    interface::admin_state_t::UP);
520
521     /*
522      * set the expectation for a afpacket interface create.
523      *  2 is the interface handle VPP [mock] assigns
524      */
525     HW::item<handle_t> hw_ifh(2, rc_t::OK);
526     ADD_EXPECT(interface_cmds::af_packet_create_cmd(hw_ifh, itf1_name));
527
528     HW::item<interface::admin_state_t> hw_as_up(interface::admin_state_t::UP, rc_t::OK);
529     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_up, hw_ifh));
530
531     TRY_CHECK_RC(OM::write(go, itf1));
532
533     HW::item<interface::admin_state_t> hw_as_down(interface::admin_state_t::DOWN, rc_t::OK);
534     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_down, hw_ifh));
535     ADD_EXPECT(interface_cmds::af_packet_delete_cmd(hw_ifh, itf1_name));
536
537     TRY_CHECK(OM::remove(go));
538
539     /*
540      * George creates the interface, then John brings it down.
541      * George's remove is a no-op, sice John also owns the interface
542      */
543     interface itf1b(itf1_name,
544                     interface::type_t::AFPACKET,
545                     interface::admin_state_t::DOWN);
546
547     ADD_EXPECT(interface_cmds::af_packet_create_cmd(hw_ifh, itf1_name));
548     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_up, hw_ifh));
549     TRY_CHECK_RC(OM::write(go, itf1));
550
551     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_down, hw_ifh));
552     TRY_CHECK_RC(OM::write(js, itf1b));
553
554     TRY_CHECK(OM::remove(go));
555
556     ADD_EXPECT(interface_cmds::af_packet_delete_cmd(hw_ifh, itf1_name));
557     TRY_CHECK(OM::remove(js));
558
559     /*
560      * George adds an interface, then we flush all of Geroge's state
561      */
562     ADD_EXPECT(interface_cmds::af_packet_create_cmd(hw_ifh, itf1_name));
563     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_up, hw_ifh));
564     TRY_CHECK_RC(OM::write(go, itf1));
565
566     TRY_CHECK(OM::mark(go));
567
568     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_down, hw_ifh));
569     ADD_EXPECT(interface_cmds::af_packet_delete_cmd(hw_ifh, itf1_name));
570     TRY_CHECK(OM::sweep(go));
571
572     /*
573      * George adds an interface. mark stale. update the same interface. sweep
574      * and expect no delete
575      */
576     ADD_EXPECT(interface_cmds::af_packet_create_cmd(hw_ifh, itf1_name));
577     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_down, hw_ifh));
578     TRY_CHECK_RC(OM::write(go, itf1b));
579
580     TRY_CHECK(OM::mark(go));
581
582     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_up, hw_ifh));
583     TRY_CHECK_RC(OM::write(go, itf1));
584
585     TRY_CHECK(OM::sweep(go));
586
587     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_down, hw_ifh));
588     ADD_EXPECT(interface_cmds::af_packet_delete_cmd(hw_ifh, itf1_name));
589     TRY_CHECK(OM::remove(go));
590
591     /*
592      * George adds an insterface, then we mark that state. Add a second interface
593      * an flush the first that is now stale.
594      */
595     ADD_EXPECT(interface_cmds::af_packet_create_cmd(hw_ifh, itf1_name));
596     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_up, hw_ifh));
597     TRY_CHECK_RC(OM::write(go, itf1));
598
599     TRY_CHECK(OM::mark(go));
600
601     std::string itf2_name = "afpacket2";
602     interface itf2(itf2_name,
603                    interface::type_t::AFPACKET,
604                    interface::admin_state_t::UP);
605     HW::item<handle_t> hw_ifh2(3, rc_t::OK);
606
607     ADD_EXPECT(interface_cmds::af_packet_create_cmd(hw_ifh2, itf2_name));
608     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_up, hw_ifh2));
609     TRY_CHECK_RC(OM::write(go, itf2));
610
611     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_down, hw_ifh));
612     ADD_EXPECT(interface_cmds::af_packet_delete_cmd(hw_ifh, itf1_name));
613     TRY_CHECK(OM::sweep(go));
614
615     TRY_CHECK(OM::mark(go));
616
617     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_down, hw_ifh2));
618     ADD_EXPECT(interface_cmds::af_packet_delete_cmd(hw_ifh2, itf2_name));
619     TRY_CHECK(OM::sweep(go));
620 }
621
622 BOOST_AUTO_TEST_CASE(test_bvi) {
623     VppInit vi;
624     const std::string ernest = "ErnestHemmingway";
625     const std::string graham = "GrahamGreene";
626     rc_t rc = rc_t::OK;
627     l3_binding *l3;
628
629     HW::item<interface::admin_state_t> hw_as_up(interface::admin_state_t::UP,
630                                                 rc_t::OK);
631     HW::item<interface::admin_state_t> hw_as_down(interface::admin_state_t::DOWN,
632                                                   rc_t::OK);
633
634     /*
635      * Enrest creates a BVI with address 10.10.10.10/24
636      */
637     route::prefix_t pfx_10("10.10.10.10", 24);
638
639     const std::string bvi_name = "bvi1";
640     interface itf(bvi_name,
641                   interface::type_t::BVI,
642                   interface::admin_state_t::UP);
643     HW::item<handle_t> hw_ifh(4, rc_t::OK);
644     HW::item<route::prefix_t> hw_pfx_10(pfx_10, rc_t::OK);
645
646     ADD_EXPECT(interface_cmds::loopback_create_cmd(hw_ifh, bvi_name));
647     ADD_EXPECT(interface_cmds::set_tag(hw_ifh, bvi_name));
648     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_up, hw_ifh));
649     TRY_CHECK_RC(OM::write(ernest, itf));
650
651     l3 = new l3_binding(itf, pfx_10);
652     HW::item<bool> hw_l3_bind(true, rc_t::OK);
653     HW::item<bool> hw_l3_unbind(false, rc_t::OK);
654     ADD_EXPECT(l3_binding_cmds::bind_cmd(hw_l3_bind, hw_ifh.data(), pfx_10));
655     TRY_CHECK_RC(OM::write(ernest, *l3));
656
657     // change the MAC address on the BVI
658     interface itf_new_mac(bvi_name,
659                           interface::type_t::BVI,
660                           interface::admin_state_t::UP);
661     l2_address_t l2_addr({0,1,2,3,4,5});
662     HW::item<l2_address_t> hw_mac(l2_addr, rc_t::OK);
663     itf_new_mac.set(l2_addr);
664     ADD_EXPECT(interface_cmds::set_mac_cmd(hw_mac, hw_ifh));
665     TRY_CHECK_RC(OM::write(ernest, itf_new_mac));
666
667     // create/write the interface to the OM again but with an unset MAC
668     // this should not generate a MAC address update
669     TRY_CHECK_RC(OM::write(ernest, itf));
670
671     // change the MAC address on the BVI - again
672     interface itf_new_mac2(bvi_name,
673                            interface::type_t::BVI,
674                            interface::admin_state_t::UP);
675     l2_address_t l2_addr2({0,1,2,3,4,6});
676     HW::item<l2_address_t> hw_mac2(l2_addr2, rc_t::OK);
677     itf_new_mac2.set(l2_addr2);
678     ADD_EXPECT(interface_cmds::set_mac_cmd(hw_mac2, hw_ifh));
679     TRY_CHECK_RC(OM::write(ernest, itf_new_mac2));
680
681     delete l3;
682     ADD_EXPECT(l3_binding_cmds::unbind_cmd(hw_l3_unbind, hw_ifh.data(), pfx_10));
683     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_down, hw_ifh));
684     ADD_EXPECT(interface_cmds::loopback_delete_cmd(hw_ifh));
685     TRY_CHECK(OM::remove(ernest));
686
687     /*
688      * Graham creates a BVI with address 10.10.10.10/24 in Routing Domain
689      */
690     route_domain rd(1);
691     HW::item<bool> hw_rd4_create(true, rc_t::OK);
692     HW::item<bool> hw_rd4_delete(false, rc_t::OK);
693     HW::item<bool> hw_rd6_create(true, rc_t::OK);
694     HW::item<bool> hw_rd6_delete(false, rc_t::OK);
695     HW::item<route::table_id_t> hw_rd4_bind(1, rc_t::OK);
696     HW::item<route::table_id_t> hw_rd4_unbind(route::DEFAULT_TABLE, rc_t::OK);
697     HW::item<route::table_id_t> hw_rd6_bind(1, rc_t::OK);
698     HW::item<route::table_id_t> hw_rd6_unbind(route::DEFAULT_TABLE, rc_t::OK);
699     ADD_EXPECT(route_domain_cmds::create_cmd(hw_rd4_create, l3_proto_t::IPV4, 1));
700     ADD_EXPECT(route_domain_cmds::create_cmd(hw_rd6_create, l3_proto_t::IPV6, 1));
701     TRY_CHECK_RC(OM::write(graham, rd));
702
703     const std::string bvi2_name = "bvi2";
704     interface *itf2 = new interface(bvi2_name,
705                                     interface::type_t::BVI,
706                                     interface::admin_state_t::UP,
707                                     rd);
708     HW::item<handle_t> hw_ifh2(5, rc_t::OK);
709
710     ADD_EXPECT(interface_cmds::loopback_create_cmd(hw_ifh2, bvi2_name));
711     ADD_EXPECT(interface_cmds::set_tag(hw_ifh2, bvi2_name));
712     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_up, hw_ifh2));
713     ADD_EXPECT(interface_cmds::set_table_cmd(hw_rd4_bind, l3_proto_t::IPV4, hw_ifh2));
714     ADD_EXPECT(interface_cmds::set_table_cmd(hw_rd6_bind, l3_proto_t::IPV6, hw_ifh2));
715
716     TRY_CHECK_RC(OM::write(graham, *itf2));
717
718     l3 = new l3_binding(*itf2, pfx_10);
719     ADD_EXPECT(l3_binding_cmds::bind_cmd(hw_l3_bind, hw_ifh2.data(), pfx_10));
720     TRY_CHECK_RC(OM::write(graham, *l3));
721
722     delete l3;
723     delete itf2;
724
725     ADD_EXPECT(l3_binding_cmds::unbind_cmd(hw_l3_unbind, hw_ifh2.data(), pfx_10));
726     ADD_EXPECT(interface_cmds::set_table_cmd(hw_rd4_unbind, l3_proto_t::IPV4, hw_ifh2));
727     ADD_EXPECT(interface_cmds::set_table_cmd(hw_rd6_unbind, l3_proto_t::IPV6, hw_ifh2));
728     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_down, hw_ifh2));
729     ADD_EXPECT(interface_cmds::loopback_delete_cmd(hw_ifh2));
730     ADD_EXPECT(route_domain_cmds::delete_cmd(hw_rd4_delete, l3_proto_t::IPV4, 1));
731     ADD_EXPECT(route_domain_cmds::delete_cmd(hw_rd6_delete, l3_proto_t::IPV6, 1));
732     TRY_CHECK(OM::remove(graham));
733 }
734
735 BOOST_AUTO_TEST_CASE(test_bridge) {
736     VppInit vi;
737     const std::string franz = "FranzKafka";
738     const std::string dante = "Dante";
739     rc_t rc = rc_t::OK;
740
741     /*
742      * Franz creates an interface, Bridge-domain, then binds the two
743      */
744
745     // interface create
746     std::string itf1_name = "afpacket1";
747     interface itf1(itf1_name,
748                    interface::type_t::AFPACKET,
749                    interface::admin_state_t::UP);
750
751     HW::item<handle_t> hw_ifh(3, rc_t::OK);
752     HW::item<interface::admin_state_t> hw_as_up(interface::admin_state_t::UP,
753                                                 rc_t::OK);
754     ADD_EXPECT(interface_cmds::af_packet_create_cmd(hw_ifh, itf1_name));
755     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_up, hw_ifh));
756
757     TRY_CHECK_RC(OM::write(franz, itf1));
758
759     // bridge-domain create
760     bridge_domain bd1(33);
761
762     HW::item<uint32_t> hw_bd(33, rc_t::OK);
763     ADD_EXPECT(bridge_domain_cmds::create_cmd(hw_bd, bridge_domain::learning_mode_t::ON));
764
765     TRY_CHECK_RC(OM::write(franz, bd1));
766
767     // L2-interface create and bind
768     // this needs to be delete'd before the flush below, since it too maintains
769     // references to the BD and Interface
770     l2_binding *l2itf = new l2_binding(itf1, bd1);
771     HW::item<bool> hw_l2_bind(true, rc_t::OK);
772
773     ADD_EXPECT(l2_binding_cmds::bind_cmd(hw_l2_bind, hw_ifh.data(), hw_bd.data(), false));
774     TRY_CHECK_RC(OM::write(franz, *l2itf));
775
776     /*
777      * Dante adds an interface to the same BD
778      */
779     std::string itf2_name = "afpacket2";
780     interface itf2(itf2_name,
781                    interface::type_t::AFPACKET,
782                    interface::admin_state_t::UP);
783
784     HW::item<handle_t> hw_ifh2(4, rc_t::OK);
785     ADD_EXPECT(interface_cmds::af_packet_create_cmd(hw_ifh2, itf2_name));
786     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_up, hw_ifh2));
787     TRY_CHECK_RC(OM::write(dante, itf2));
788
789     // BD add is a no-op since it exists
790     TRY_CHECK_RC(OM::write(dante, bd1));
791
792     l2_binding *l2itf2 = new l2_binding(itf2, bd1);
793     HW::item<l2_binding::l2_vtr_op_t> hw_set_vtr(l2_binding::l2_vtr_op_t::L2_VTR_POP_1, rc_t::OK);
794     l2itf2->set(l2_binding::l2_vtr_op_t::L2_VTR_POP_1, 68);
795
796     ADD_EXPECT(l2_binding_cmds::bind_cmd(hw_l2_bind, hw_ifh2.data(), hw_bd.data(), false));
797     ADD_EXPECT(l2_binding_cmds::set_vtr_op_cmd(hw_set_vtr, hw_ifh2.data(), 68));
798     TRY_CHECK_RC(OM::write(dante, *l2itf2));
799
800     // Add some static entries to the bridge-domain
801     HW::item<bool> hw_be1(true, rc_t::OK);
802     mac_address_t mac1({0,1,2,3,4,5});
803     bridge_domain_entry *be1 = new bridge_domain_entry(bd1, mac1, itf2);
804     ADD_EXPECT(bridge_domain_entry_cmds::create_cmd(hw_be1, mac1, bd1.id(), hw_ifh2.data()));
805     TRY_CHECK_RC(OM::write(dante, *be1));
806
807     // Add some entries to the bridge-domain ARP termination table
808     HW::item<bool> hw_bea1(true, rc_t::OK);
809     boost::asio::ip::address ip1 = boost::asio::ip::address::from_string("10.10.10.10");
810
811     bridge_domain_arp_entry *bea1 = new bridge_domain_arp_entry(bd1, mac1, ip1);
812     ADD_EXPECT(bridge_domain_arp_entry_cmds::create_cmd(hw_be1, bd1.id(), mac1, ip1));
813     TRY_CHECK_RC(OM::write(dante, *bea1));
814
815     // flush Franz's state
816     delete l2itf;
817     HW::item<interface::admin_state_t> hw_as_down(interface::admin_state_t::DOWN,
818                                                   rc_t::OK);
819     ADD_EXPECT(l2_binding_cmds::unbind_cmd(hw_l2_bind, hw_ifh.data(), hw_bd.data(), false));
820     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_down, hw_ifh));
821     ADD_EXPECT(interface_cmds::af_packet_delete_cmd(hw_ifh, itf1_name));
822     TRY_CHECK(OM::remove(franz));
823
824     // flush Dante's state - the order the interface and BD are deleted
825     // is an uncontrollable artifact of the C++ object destruction.
826     delete l2itf2;
827     delete be1;
828     delete bea1;
829     STRICT_ORDER_OFF();
830     ADD_EXPECT(l2_binding_cmds::unbind_cmd(hw_l2_bind, hw_ifh2.data(), hw_bd.data(), false));
831     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_down, hw_ifh2));
832     ADD_EXPECT(interface_cmds::af_packet_delete_cmd(hw_ifh2, itf2_name));
833     ADD_EXPECT(bridge_domain_entry_cmds::delete_cmd(hw_be1, mac1, bd1.id()));
834     ADD_EXPECT(bridge_domain_arp_entry_cmds::delete_cmd(hw_be1, bd1.id(), mac1, ip1));
835     ADD_EXPECT(bridge_domain_cmds::delete_cmd(hw_bd));
836     TRY_CHECK(OM::remove(dante));
837 }
838
839 BOOST_AUTO_TEST_CASE(test_vxlan) {
840     VppInit vi;
841     const std::string franz = "FranzKafka";
842     rc_t rc = rc_t::OK;
843
844     /*
845      * Franz creates an interface, Bridge-domain, then binds the two
846      */
847
848     // VXLAN create
849     vxlan_tunnel::endpoint_t ep(boost::asio::ip::address::from_string("10.10.10.10"),
850                                boost::asio::ip::address::from_string("10.10.10.11"),
851                                322);
852
853     vxlan_tunnel vxt(ep.src, ep.dst, ep.vni);
854
855     HW::item<handle_t> hw_vxt(3, rc_t::OK);
856     ADD_EXPECT(vxlan_tunnel_cmds::create_cmd(hw_vxt, "don't-care", ep));
857
858     TRY_CHECK_RC(OM::write(franz, vxt));
859
860     // bridge-domain create
861     bridge_domain bd1(33, bridge_domain::learning_mode_t::OFF);
862
863     HW::item<uint32_t> hw_bd(33, rc_t::OK);
864     ADD_EXPECT(bridge_domain_cmds::create_cmd(hw_bd, bridge_domain::learning_mode_t::OFF));
865
866     TRY_CHECK_RC(OM::write(franz, bd1));
867
868     // L2-interface create and bind
869     // this needs to be delete'd before the flush below, since it too maintains
870     // references to the BD and Interface
871     l2_binding *l2itf = new l2_binding(vxt, bd1);
872     HW::item<bool> hw_l2_bind(true, rc_t::OK);
873
874     ADD_EXPECT(l2_binding_cmds::bind_cmd(hw_l2_bind, hw_vxt.data(), hw_bd.data(), false));
875     TRY_CHECK_RC(OM::write(franz, *l2itf));
876
877     // flush Franz's state
878     delete l2itf;
879     HW::item<handle_t> hw_vxtdel(3, rc_t::NOOP);
880     STRICT_ORDER_OFF();
881     ADD_EXPECT(l2_binding_cmds::unbind_cmd(hw_l2_bind, hw_vxt.data(), hw_bd.data(), false));
882     ADD_EXPECT(bridge_domain_cmds::delete_cmd(hw_bd));
883     ADD_EXPECT(vxlan_tunnel_cmds::delete_cmd(hw_vxtdel, ep));
884     TRY_CHECK(OM::remove(franz));
885 }
886
887 BOOST_AUTO_TEST_CASE(test_vlan) {
888     VppInit vi;
889     const std::string noam = "NoamChomsky";
890     rc_t rc = rc_t::OK;
891
892     std::string itf1_name = "host1";
893     interface itf1(itf1_name,
894                    interface::type_t::AFPACKET,
895                    interface::admin_state_t::UP);
896
897     HW::item<handle_t> hw_ifh(2, rc_t::OK);
898     ADD_EXPECT(interface_cmds::af_packet_create_cmd(hw_ifh, itf1_name));
899
900     HW::item<interface::admin_state_t> hw_as_up(interface::admin_state_t::UP, rc_t::OK);
901     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_up, hw_ifh));
902
903     TRY_CHECK_RC(OM::write(noam, itf1));
904
905     sub_interface *vl33 = new sub_interface(itf1,
906                                             interface::admin_state_t::UP,
907                                             33);
908
909     HW::item<handle_t> hw_vl33(3, rc_t::OK);
910     ADD_EXPECT(sub_interface_cmds::create_cmd(hw_vl33, itf1_name+".33", hw_ifh.data(), 33));
911     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_up, hw_vl33));
912
913     TRY_CHECK_RC(OM::write(noam, *vl33));
914
915     delete vl33;
916     HW::item<interface::admin_state_t> hw_as_down(interface::admin_state_t::DOWN, rc_t::OK);
917     HW::item<handle_t> hw_vl33_down(3, rc_t::NOOP);
918     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_down, hw_vl33));
919     ADD_EXPECT(sub_interface_cmds::delete_cmd(hw_vl33_down));
920     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_down, hw_ifh));
921     ADD_EXPECT(interface_cmds::af_packet_delete_cmd(hw_ifh, itf1_name));
922
923     TRY_CHECK(OM::remove(noam));
924 }
925
926 BOOST_AUTO_TEST_CASE(test_acl) {
927     VppInit vi;
928     const std::string fyodor = "FyodorDostoyevsky";
929     const std::string leo = "LeoTolstoy";
930     rc_t rc = rc_t::OK;
931
932     /*
933      * Fyodor adds an ACL in the input direction
934      */
935     std::string itf1_name = "host1";
936     interface itf1(itf1_name,
937                    interface::type_t::AFPACKET,
938                    interface::admin_state_t::UP);
939     HW::item<handle_t> hw_ifh(2, rc_t::OK);
940     HW::item<interface::admin_state_t> hw_as_up(interface::admin_state_t::UP, rc_t::OK);
941     ADD_EXPECT(interface_cmds::af_packet_create_cmd(hw_ifh, itf1_name));
942     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_up, hw_ifh));
943     TRY_CHECK_RC(OM::write(fyodor, itf1));
944
945     route::prefix_t src("10.10.10.10", 32);
946     ACL::l3_rule r1(10, ACL::action_t::PERMIT, src, route::prefix_t::ZERO);
947     ACL::l3_rule r2(20, ACL::action_t::DENY, route::prefix_t::ZERO, route::prefix_t::ZERO);
948
949     std::string acl_name = "acl1";
950     ACL::l3_list acl1(acl_name);
951     acl1.insert(r2);
952     acl1.insert(r1);
953     ACL::l3_list::rules_t rules = {r1, r2};
954
955     HW::item<handle_t> hw_acl(2, rc_t::OK);
956     ADD_EXPECT(ACL::list_cmds::l3_update_cmd(hw_acl, acl_name, rules));
957     TRY_CHECK_RC(OM::write(fyodor, acl1));
958
959     ACL::l3_binding *l3b = new ACL::l3_binding(direction_t::INPUT, itf1, acl1);
960     HW::item<bool> hw_binding(true, rc_t::OK);
961     ADD_EXPECT(ACL::binding_cmds::l3_bind_cmd(hw_binding, direction_t::INPUT,
962                                          hw_ifh.data(), hw_acl.data()));
963     TRY_CHECK_RC(OM::write(fyodor, *l3b));
964
965     /**
966      * Leo adds an L2 ACL in the output direction
967      */
968     TRY_CHECK_RC(OM::write(leo, itf1));
969
970     std::string l2_acl_name = "l2_acl1";
971     mac_address_t mac({0x0, 0x0, 0x1, 0x2, 0x3, 0x4});
972     mac_address_t mac_mask({0xff, 0xff, 0xff, 0x0, 0x0, 0x0});
973     ACL::l2_rule l2_r1(10, ACL::action_t::PERMIT, src, mac, mac_mask);
974     ACL::l2_rule l2_r2(20, ACL::action_t::DENY, src, {}, {});
975
976     ACL::l2_list l2_acl(l2_acl_name);
977     l2_acl.insert(l2_r2);
978     l2_acl.insert(l2_r1);
979
980     ACL::l2_list::rules_t l2_rules = {l2_r1, l2_r2};
981
982     HW::item<handle_t> l2_hw_acl(3, rc_t::OK);
983     ADD_EXPECT(ACL::list_cmds::l2_update_cmd(l2_hw_acl, l2_acl_name, l2_rules));
984     TRY_CHECK_RC(OM::write(leo, l2_acl));
985
986     ACL::l2_binding *l2b = new ACL::l2_binding(direction_t::OUTPUT, itf1, l2_acl);
987     HW::item<bool> l2_hw_binding(true, rc_t::OK);
988     ADD_EXPECT(ACL::binding_cmds::l2_bind_cmd(l2_hw_binding, direction_t::OUTPUT,
989                                        hw_ifh.data(), l2_hw_acl.data()));
990     TRY_CHECK_RC(OM::write(leo, *l2b));
991
992     delete l2b;
993     ADD_EXPECT(ACL::binding_cmds::l2_unbind_cmd(l2_hw_binding, direction_t::OUTPUT,
994                                                 hw_ifh.data(), l2_hw_acl.data()));
995     ADD_EXPECT(ACL::list_cmds::l2_delete_cmd(l2_hw_acl));
996     TRY_CHECK(OM::remove(leo));
997
998     delete l3b;
999     HW::item<interface::admin_state_t> hw_as_down(interface::admin_state_t::DOWN,
1000                                                   rc_t::OK);
1001     STRICT_ORDER_OFF();
1002     ADD_EXPECT(ACL::binding_cmds::l3_unbind_cmd(hw_binding, direction_t::INPUT,
1003                                          hw_ifh.data(), hw_acl.data()));
1004     ADD_EXPECT(ACL::list_cmds::l3_delete_cmd(hw_acl));
1005     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_down, hw_ifh));
1006     ADD_EXPECT(interface_cmds::af_packet_delete_cmd(hw_ifh, itf1_name));
1007
1008     TRY_CHECK(OM::remove(fyodor));
1009 }
1010
1011 BOOST_AUTO_TEST_CASE(test_arp_proxy) {
1012     VppInit vi;
1013     const std::string kurt = "KurtVonnegut";
1014     rc_t rc = rc_t::OK;
1015
1016     asio::ip::address_v4 low  = asio::ip::address_v4::from_string("10.0.0.0");
1017     asio::ip::address_v4 high = asio::ip::address_v4::from_string("10.0.0.255");
1018
1019     arp_proxy_config ap(low, high);
1020     HW::item<bool> hw_ap_cfg(true, rc_t::OK);
1021     ADD_EXPECT(arp_proxy_config_cmds::config_cmd(hw_ap_cfg, low, high));
1022     TRY_CHECK_RC(OM::write(kurt, ap));
1023
1024     std::string itf3_name = "host3";
1025     interface itf3(itf3_name,
1026                    interface::type_t::AFPACKET,
1027                    interface::admin_state_t::UP);
1028     HW::item<handle_t> hw_ifh(2, rc_t::OK);
1029     HW::item<interface::admin_state_t> hw_as_up(interface::admin_state_t::UP, rc_t::OK);
1030     ADD_EXPECT(interface_cmds::af_packet_create_cmd(hw_ifh, itf3_name));
1031     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_up, hw_ifh));
1032     TRY_CHECK_RC(OM::write(kurt, itf3));
1033
1034     arp_proxy_binding *apb = new arp_proxy_binding(itf3, ap);
1035     HW::item<bool> hw_binding(true, rc_t::OK);
1036     ADD_EXPECT(arp_proxy_binding_cmds::bind_cmd(hw_binding, hw_ifh.data()));
1037     TRY_CHECK_RC(OM::write(kurt, *apb));
1038
1039     delete apb;
1040
1041     HW::item<interface::admin_state_t> hw_as_down(interface::admin_state_t::DOWN,
1042                                                   rc_t::OK);
1043     STRICT_ORDER_OFF();
1044     ADD_EXPECT(arp_proxy_binding_cmds::unbind_cmd(hw_binding, hw_ifh.data()));
1045     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_down, hw_ifh));
1046     ADD_EXPECT(interface_cmds::af_packet_delete_cmd(hw_ifh, itf3_name));
1047     ADD_EXPECT(arp_proxy_config_cmds::unconfig_cmd(hw_ap_cfg, low, high));
1048
1049     TRY_CHECK(OM::remove(kurt));
1050 }
1051
1052 BOOST_AUTO_TEST_CASE(test_ip_unnumbered) {
1053     VppInit vi;
1054     const std::string eric = "EricAmbler";
1055     rc_t rc = rc_t::OK;
1056
1057     /*
1058      * Interface 1 has the L3 address
1059      */
1060     std::string itf1_name = "host1";
1061     interface itf1(itf1_name,
1062                    interface::type_t::AFPACKET,
1063                    interface::admin_state_t::UP);
1064     HW::item<handle_t> hw_ifh(2, rc_t::OK);
1065     HW::item<interface::admin_state_t> hw_as_up(interface::admin_state_t::UP, rc_t::OK);
1066     ADD_EXPECT(interface_cmds::af_packet_create_cmd(hw_ifh, itf1_name));
1067     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_up, hw_ifh));
1068     TRY_CHECK_RC(OM::write(eric, itf1));
1069
1070     route::prefix_t pfx_10("10.10.10.10", 24);
1071     l3_binding *l3 = new l3_binding(itf1, pfx_10);
1072     HW::item<bool> hw_l3_bind(true, rc_t::OK);
1073     HW::item<bool> hw_l3_unbind(false, rc_t::OK);
1074     ADD_EXPECT(l3_binding_cmds::bind_cmd(hw_l3_bind, hw_ifh.data(), pfx_10));
1075     TRY_CHECK_RC(OM::write(eric, *l3));
1076
1077     /*
1078      * Interface 2 is unnumbered
1079      */
1080     std::string itf2_name = "host2";
1081     interface itf2(itf2_name,
1082                    interface::type_t::AFPACKET,
1083                    interface::admin_state_t::UP);
1084
1085     HW::item<handle_t> hw_ifh2(4, rc_t::OK);
1086     ADD_EXPECT(interface_cmds::af_packet_create_cmd(hw_ifh2, itf2_name));
1087     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_up, hw_ifh2));
1088     TRY_CHECK_RC(OM::write(eric, itf2));
1089
1090     ip_unnumbered *ipun = new ip_unnumbered(itf2, itf1);
1091     HW::item<bool> hw_ip_cfg(true, rc_t::OK);
1092     HW::item<bool> hw_ip_uncfg(false, rc_t::OK);
1093     ADD_EXPECT(ip_unnumbered_cmds::config_cmd(hw_ip_cfg, hw_ifh2.data(), hw_ifh.data()));
1094     TRY_CHECK_RC(OM::write(eric, *ipun));
1095
1096     delete l3;
1097     delete ipun;
1098
1099     HW::item<interface::admin_state_t> hw_as_down(interface::admin_state_t::DOWN, rc_t::OK);
1100     STRICT_ORDER_OFF();
1101     ADD_EXPECT(ip_unnumbered_cmds::unconfig_cmd(hw_ip_uncfg, hw_ifh2.data(), hw_ifh.data()));
1102     ADD_EXPECT(l3_binding_cmds::unbind_cmd(hw_l3_unbind, hw_ifh.data(), pfx_10));
1103     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_down, hw_ifh2));
1104     ADD_EXPECT(interface_cmds::af_packet_delete_cmd(hw_ifh2, itf2_name));
1105     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_down, hw_ifh));
1106     ADD_EXPECT(interface_cmds::af_packet_delete_cmd(hw_ifh, itf1_name));
1107
1108     TRY_CHECK(OM::remove(eric));
1109 }
1110
1111 BOOST_AUTO_TEST_CASE(test_ip6nd) {
1112     VppInit vi;
1113     const std::string paulo = "PauloCoelho";
1114     rc_t rc = rc_t::OK;
1115
1116     /*
1117      * ra config
1118      */
1119     std::string itf_name = "host_ip6nd";
1120     interface itf(itf_name,
1121                    interface::type_t::AFPACKET,
1122                    interface::admin_state_t::UP);
1123     HW::item<handle_t> hw_ifh(3, rc_t::OK);
1124     HW::item<interface::admin_state_t> hw_as_up(interface::admin_state_t::UP, rc_t::OK);
1125     ADD_EXPECT(interface_cmds::af_packet_create_cmd(hw_ifh, itf_name));
1126     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_up, hw_ifh));
1127     TRY_CHECK_RC(OM::write(paulo, itf));
1128
1129     route::prefix_t pfx_10("fd8f:69d8:c12c:ca62::3", 128);
1130     l3_binding *l3 = new l3_binding(itf, pfx_10);
1131     HW::item<bool> hw_l3_bind(true, rc_t::OK);
1132     HW::item<bool> hw_l3_unbind(false, rc_t::OK);
1133     ADD_EXPECT(l3_binding_cmds::bind_cmd(hw_l3_bind, hw_ifh.data(), pfx_10));
1134     TRY_CHECK_RC(OM::write(paulo, *l3));
1135
1136     ra_config ra(0, 1, 0, 4);
1137     ip6nd_ra_config *ip6ra = new ip6nd_ra_config(itf, ra);
1138     HW::item<bool> hw_ip6nd_ra_config_config(true, rc_t::OK);
1139     HW::item<bool> hw_ip6nd_ra_config_unconfig(false, rc_t::OK);
1140     ADD_EXPECT(ip6nd_ra_config::config_cmd(hw_ip6nd_ra_config_config, hw_ifh.data(), ra));
1141     TRY_CHECK_RC(OM::write(paulo, *ip6ra));
1142
1143     /*
1144      * ra prefix
1145      */
1146     ra_prefix ra_pfx(pfx_10, 0, 0, 2592000, 604800);
1147     ip6nd_ra_prefix *ip6pfx = new ip6nd_ra_prefix(itf, ra_pfx);
1148     HW::item<bool> hw_ip6nd_ra_prefix_config(true, rc_t::OK);
1149     HW::item<bool> hw_ip6nd_ra_prefix_unconfig(false, rc_t::OK);
1150     ADD_EXPECT(ip6nd_ra_prefix::config_cmd(hw_ip6nd_ra_prefix_config, hw_ifh.data(), ra_pfx));
1151     TRY_CHECK_RC(OM::write(paulo, *ip6pfx));
1152
1153     delete ip6pfx;
1154
1155     ADD_EXPECT(ip6nd_ra_prefix::unconfig_cmd(hw_ip6nd_ra_prefix_unconfig, hw_ifh.data(), ra_pfx));
1156
1157     delete ip6ra;
1158     delete l3;
1159
1160     HW::item<interface::admin_state_t> hw_as_down(interface::admin_state_t::DOWN, rc_t::OK);
1161
1162     STRICT_ORDER_OFF();
1163     ADD_EXPECT(ip6nd_ra_config::unconfig_cmd(hw_ip6nd_ra_config_unconfig, hw_ifh.data(), ra));
1164     ADD_EXPECT(l3_binding_cmds::unbind_cmd(hw_l3_unbind, hw_ifh.data(), pfx_10));
1165     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_down, hw_ifh));
1166     ADD_EXPECT(interface_cmds::af_packet_delete_cmd(hw_ifh, itf_name));
1167
1168     TRY_CHECK(OM::remove(paulo));
1169 }
1170
1171 BOOST_AUTO_TEST_CASE(test_interface_span) {
1172     VppInit vi;
1173     const std::string elif = "ElifShafak";
1174     rc_t rc = rc_t::OK;
1175
1176     /*
1177      * Interface 1 to be mirrored
1178      */
1179     std::string itf1_name = "port-from";
1180     interface itf1(itf1_name,
1181                    interface::type_t::AFPACKET,
1182                    interface::admin_state_t::UP);
1183     HW::item<handle_t> hw_ifh(2, rc_t::OK);
1184     HW::item<interface::admin_state_t> hw_as_up(interface::admin_state_t::UP, rc_t::OK);
1185     ADD_EXPECT(interface_cmds::af_packet_create_cmd(hw_ifh, itf1_name));
1186     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_up, hw_ifh));
1187     TRY_CHECK_RC(OM::write(elif, itf1));
1188
1189     /*
1190      * Interface 2 where traffic is mirrored
1191      */
1192     std::string itf2_name = "port-to";
1193     interface itf2(itf2_name,
1194                    interface::type_t::AFPACKET,
1195                    interface::admin_state_t::UP);
1196
1197     HW::item<handle_t> hw_ifh2(4, rc_t::OK);
1198     HW::item<interface::admin_state_t> hw_as_up2(interface::admin_state_t::UP, rc_t::OK);
1199
1200     ADD_EXPECT(interface_cmds::af_packet_create_cmd(hw_ifh2, itf2_name));
1201     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_up2, hw_ifh2));
1202     TRY_CHECK_RC(OM::write(elif, itf2));
1203
1204     interface_span *itf_span = new interface_span(itf1, itf2, interface_span::state_t::TX_RX_ENABLED);
1205     HW::item<bool> hw_is_cfg(true, rc_t::OK);
1206     HW::item<bool> hw_is_uncfg(true, rc_t::OK);
1207     ADD_EXPECT(interface_span_cmds::config_cmd(hw_is_cfg, hw_ifh.data(), hw_ifh2.data(), interface_span::state_t::TX_RX_ENABLED));
1208     TRY_CHECK_RC(OM::write(elif, *itf_span));
1209
1210     HW::item<interface::admin_state_t> hw_as_down(interface::admin_state_t::DOWN, rc_t::OK);
1211     HW::item<interface::admin_state_t> hw_as_down2(interface::admin_state_t::DOWN, rc_t::OK);
1212
1213     delete itf_span;
1214     STRICT_ORDER_OFF();
1215     ADD_EXPECT(interface_span_cmds::unconfig_cmd(hw_is_uncfg, hw_ifh.data(), hw_ifh2.data()));
1216     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_down, hw_ifh));
1217     ADD_EXPECT(interface_cmds::af_packet_delete_cmd(hw_ifh, itf1_name));
1218     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_down2, hw_ifh2));
1219     ADD_EXPECT(interface_cmds::af_packet_delete_cmd(hw_ifh2, itf2_name));
1220
1221     TRY_CHECK(OM::remove(elif));
1222 }
1223
1224 BOOST_AUTO_TEST_CASE(test_routing) {
1225     VppInit vi;
1226     const std::string ian = "IanFleming";
1227     rc_t rc = rc_t::OK;
1228
1229     /*
1230      * non-default route domain
1231      */
1232     route_domain rd4(1);
1233     HW::item<bool> hw_rd4_create(true, rc_t::OK);
1234     HW::item<bool> hw_rd4_delete(false, rc_t::OK);
1235     HW::item<bool> hw_rd6_create(true, rc_t::OK);
1236     HW::item<bool> hw_rd6_delete(false, rc_t::OK);
1237     HW::item<route::table_id_t> hw_rd4_bind(1, rc_t::OK);
1238     HW::item<route::table_id_t> hw_rd4_unbind(route::DEFAULT_TABLE, rc_t::OK);
1239     HW::item<route::table_id_t> hw_rd6_bind(1, rc_t::OK);
1240     HW::item<route::table_id_t> hw_rd7_unbind(route::DEFAULT_TABLE, rc_t::OK);
1241     ADD_EXPECT(route_domain_cmds::create_cmd(hw_rd4_create, l3_proto_t::IPV4, 1));
1242     ADD_EXPECT(route_domain_cmds::create_cmd(hw_rd6_create, l3_proto_t::IPV6, 1));
1243     TRY_CHECK_RC(OM::write(ian, rd4));
1244
1245     /*
1246      * a couple of interfaces
1247      */
1248     std::string itf1_name = "af1";
1249     interface itf1(itf1_name,
1250                    interface::type_t::AFPACKET,
1251                    interface::admin_state_t::UP);
1252     HW::item<handle_t> hw_ifh(2, rc_t::OK);
1253     HW::item<interface::admin_state_t> hw_as_up(interface::admin_state_t::UP, rc_t::OK);
1254     HW::item<interface::admin_state_t> hw_as_down(interface::admin_state_t::DOWN, rc_t::OK);
1255     ADD_EXPECT(interface_cmds::af_packet_create_cmd(hw_ifh, itf1_name));
1256     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_up, hw_ifh));
1257     TRY_CHECK_RC(OM::write(ian, itf1));
1258
1259     std::string itf2_name = "af2";
1260     interface *itf2 = new interface(itf2_name,
1261                                     interface::type_t::AFPACKET,
1262                                     interface::admin_state_t::UP,
1263                                     rd4);
1264
1265     HW::item<handle_t> hw_ifh2(4, rc_t::OK);
1266     HW::item<interface::admin_state_t> hw_as_up2(interface::admin_state_t::UP, rc_t::OK);
1267     HW::item<interface::admin_state_t> hw_as_down2(interface::admin_state_t::DOWN, rc_t::OK);
1268     ADD_EXPECT(interface_cmds::af_packet_create_cmd(hw_ifh2, itf2_name));
1269     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_up2, hw_ifh2));
1270     ADD_EXPECT(interface_cmds::set_table_cmd(hw_rd4_bind, l3_proto_t::IPV4, hw_ifh2));
1271     ADD_EXPECT(interface_cmds::set_table_cmd(hw_rd6_bind, l3_proto_t::IPV6, hw_ifh2));
1272     TRY_CHECK_RC(OM::write(ian, *itf2));
1273
1274     /*
1275      * prefix on each interface
1276      */
1277     route::prefix_t pfx_10("10.10.10.10", 24);
1278     l3_binding *l3_10 = new l3_binding(itf1, pfx_10);
1279     HW::item<bool> hw_l3_10_bind(true, rc_t::OK);
1280     HW::item<bool> hw_l3_10_unbind(false, rc_t::OK);
1281     ADD_EXPECT(l3_binding_cmds::bind_cmd(hw_l3_10_bind, hw_ifh.data(), pfx_10));
1282     TRY_CHECK_RC(OM::write(ian, *l3_10));
1283     route::prefix_t pfx_11("11.11.11.11", 24);
1284     l3_binding *l3_11 = new l3_binding(*itf2, pfx_11);
1285     HW::item<bool> hw_l3_11_bind(true, rc_t::OK);
1286     HW::item<bool> hw_l3_11_unbind(false, rc_t::OK);
1287     ADD_EXPECT(l3_binding_cmds::bind_cmd(hw_l3_11_bind, hw_ifh2.data(), pfx_11));
1288     TRY_CHECK_RC(OM::write(ian, *l3_11));
1289
1290     /*
1291      * A route via interface 1 in the default table
1292      */
1293     route::prefix_t pfx_5("5.5.5.5", 32);
1294     boost::asio::ip::address nh_10 = boost::asio::ip::address::from_string("10.10.10.11");
1295     route::path *path_10 = new route::path(nh_10, itf1);
1296     route::ip_route *route_5 = new route::ip_route(pfx_5);
1297     route_5->add(*path_10);
1298     HW::item<bool> hw_route_5(true, rc_t::OK);
1299     ADD_EXPECT(route::ip_route_cmds::update_cmd(hw_route_5, 0, pfx_5, {*path_10}));
1300     TRY_CHECK_RC(OM::write(ian, *route_5));
1301
1302     /*
1303      * A route via interface 2 in the non-default table
1304      */
1305     boost::asio::ip::address nh_11 = boost::asio::ip::address::from_string("11.11.11.10");
1306     route::path *path_11 = new route::path(nh_11, *itf2);
1307     route::ip_route *route_5_2 = new route::ip_route(rd4, pfx_5);
1308     route_5_2->add(*path_11);
1309     HW::item<bool> hw_route_5_2(true, rc_t::OK);
1310     ADD_EXPECT(route::ip_route_cmds::update_cmd(hw_route_5_2, 1, pfx_5, {*path_11}));
1311     TRY_CHECK_RC(OM::write(ian, *route_5_2));
1312
1313     /*
1314      * An ARP entry for the neighbour on itf1
1315      */
1316     HW::item<bool> hw_neighbour(true, rc_t::OK);
1317     mac_address_t mac_n({0,1,2,4,5,6});
1318     neighbour *ne = new neighbour(itf1, mac_n, nh_10);
1319     ADD_EXPECT(neighbour_cmds::create_cmd(hw_neighbour, hw_ifh.data(), mac_n, nh_10));
1320     TRY_CHECK_RC(OM::write(ian, *ne));
1321
1322     /*
1323      * A DVR route
1324      */
1325     route::prefix_t pfx_6("6.6.6.6", 32);
1326     route::path *path_l2 = new route::path(*itf2, nh_proto_t::ETHERNET);
1327     route::ip_route *route_dvr = new route::ip_route(pfx_6);
1328     route_dvr->add(*path_l2);
1329     HW::item<bool> hw_route_dvr(true, rc_t::OK);
1330     ADD_EXPECT(route::ip_route_cmds::update_cmd(hw_route_dvr, 0, pfx_6, {*path_l2}));
1331     TRY_CHECK_RC(OM::write(ian, *route_dvr));
1332
1333     STRICT_ORDER_OFF();
1334     // delete the stack objects that hold references to others
1335     // so the OM::remove is the call that removes the last reference
1336     delete l3_11;
1337     delete l3_10;
1338     delete itf2;
1339     delete route_5;
1340     delete path_10;
1341     delete route_5_2;
1342     delete path_11;
1343     delete route_dvr;
1344     delete path_l2;
1345     delete ne;
1346     ADD_EXPECT(neighbour_cmds::delete_cmd(hw_neighbour, hw_ifh.data(), mac_n, nh_10));
1347     ADD_EXPECT(route::ip_route_cmds::delete_cmd(hw_route_dvr, 0, pfx_6));
1348     ADD_EXPECT(route::ip_route_cmds::delete_cmd(hw_route_5_2, 1, pfx_5));
1349     ADD_EXPECT(route::ip_route_cmds::delete_cmd(hw_route_5, 0, pfx_5));
1350     ADD_EXPECT(l3_binding_cmds::unbind_cmd(hw_l3_10_unbind, hw_ifh.data(), pfx_10));
1351     ADD_EXPECT(l3_binding_cmds::unbind_cmd(hw_l3_11_unbind, hw_ifh2.data(), pfx_11));
1352     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_down, hw_ifh));
1353     ADD_EXPECT(interface_cmds::af_packet_delete_cmd(hw_ifh, itf1_name));
1354     ADD_EXPECT(interface_cmds::set_table_cmd(hw_rd4_unbind, l3_proto_t::IPV4, hw_ifh2));
1355     ADD_EXPECT(interface_cmds::set_table_cmd(hw_rd4_unbind, l3_proto_t::IPV6, hw_ifh2));
1356     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_down2, hw_ifh2));
1357     ADD_EXPECT(interface_cmds::af_packet_delete_cmd(hw_ifh2, itf2_name));
1358     ADD_EXPECT(route_domain_cmds::delete_cmd(hw_rd4_delete, l3_proto_t::IPV4, 1));
1359     ADD_EXPECT(route_domain_cmds::delete_cmd(hw_rd6_delete, l3_proto_t::IPV6, 1));
1360
1361     TRY_CHECK(OM::remove(ian));
1362 }
1363
1364 BOOST_AUTO_TEST_CASE(test_nat) {
1365     VppInit vi;
1366     const std::string gs = "GeorgeSimenon";
1367     rc_t rc = rc_t::OK;
1368
1369     /*
1370      * Inside Interface
1371      */
1372     std::string itf_in_name = "inside";
1373     interface itf_in(itf_in_name,
1374                      interface::type_t::AFPACKET,
1375                      interface::admin_state_t::UP);
1376     HW::item<handle_t> hw_ifh(2, rc_t::OK);
1377     HW::item<interface::admin_state_t> hw_as_up(interface::admin_state_t::UP, rc_t::OK);
1378     HW::item<interface::admin_state_t> hw_as_down(interface::admin_state_t::DOWN, rc_t::OK);
1379     ADD_EXPECT(interface_cmds::af_packet_create_cmd(hw_ifh, itf_in_name));
1380     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_up, hw_ifh));
1381     TRY_CHECK_RC(OM::write(gs, itf_in));
1382
1383     /*
1384      * outside
1385      */
1386     std::string itf_out_name = "port-to";
1387     interface itf_out(itf_out_name,
1388                    interface::type_t::AFPACKET,
1389                    interface::admin_state_t::UP);
1390
1391     HW::item<handle_t> hw_ifh2(4, rc_t::OK);
1392     HW::item<interface::admin_state_t> hw_as_up2(interface::admin_state_t::UP, rc_t::OK);
1393     HW::item<interface::admin_state_t> hw_as_down2(interface::admin_state_t::DOWN, rc_t::OK);
1394
1395     ADD_EXPECT(interface_cmds::af_packet_create_cmd(hw_ifh2, itf_out_name));
1396     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_up2, hw_ifh2));
1397     TRY_CHECK_RC(OM::write(gs, itf_out));
1398
1399     /*
1400      * A NAT static mapping
1401      */
1402     boost::asio::ip::address in_addr = boost::asio::ip::address::from_string("10.0.0.1");
1403     boost::asio::ip::address_v4 out_addr = boost::asio::ip::address_v4::from_string("1.1.1.1");
1404
1405     nat_static ns(in_addr, out_addr);
1406     HW::item<bool> hw_ns(true, rc_t::OK);
1407
1408     ADD_EXPECT(nat_static_cmds::create_44_cmd(hw_ns, 0, in_addr.to_v4(), out_addr));
1409     TRY_CHECK_RC(OM::write(gs, ns));
1410
1411     /*
1412      * bind nat inside and out
1413      */
1414     nat_binding *nb_in = new nat_binding(itf_in,
1415                                          direction_t::INPUT,
1416                                          l3_proto_t::IPV4,
1417                                          nat_binding::zone_t::INSIDE);
1418     HW::item<bool> hw_nb_in(true, rc_t::OK);
1419
1420     ADD_EXPECT(nat_binding_cmds::bind_44_input_cmd(hw_nb_in,
1421                                                    hw_ifh.data().value(),
1422                                                    nat_binding::zone_t::INSIDE));
1423     TRY_CHECK_RC(OM::write(gs, *nb_in));
1424
1425     nat_binding *nb_out = new nat_binding(itf_out,
1426                                           direction_t::INPUT,
1427                                           l3_proto_t::IPV4,
1428                                           nat_binding::zone_t::OUTSIDE);
1429     HW::item<bool> hw_nb_out(true, rc_t::OK);
1430
1431     ADD_EXPECT(nat_binding_cmds::bind_44_input_cmd(hw_nb_out,
1432                                                    hw_ifh2.data().value(),
1433                                                    nat_binding::zone_t::OUTSIDE));
1434     TRY_CHECK_RC(OM::write(gs, *nb_out));
1435
1436
1437     STRICT_ORDER_OFF();
1438     delete nb_in;
1439     delete nb_out;
1440     ADD_EXPECT(nat_binding_cmds::unbind_44_input_cmd(hw_nb_in,
1441                                                      hw_ifh.data().value(),
1442                                                      nat_binding::zone_t::INSIDE));
1443     ADD_EXPECT(nat_binding_cmds::unbind_44_input_cmd(hw_nb_out,
1444                                                      hw_ifh2.data().value(),
1445                                                      nat_binding::zone_t::OUTSIDE));
1446     ADD_EXPECT(nat_static_cmds::delete_44_cmd(hw_ns, 0, in_addr.to_v4(), out_addr));
1447     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_down, hw_ifh));
1448     ADD_EXPECT(interface_cmds::af_packet_delete_cmd(hw_ifh, itf_in_name));
1449     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_down2, hw_ifh2));
1450     ADD_EXPECT(interface_cmds::af_packet_delete_cmd(hw_ifh2, itf_out_name));
1451
1452     TRY_CHECK(OM::remove(gs));
1453 }
1454
1455 BOOST_AUTO_TEST_CASE(test_interface_events) {
1456     VppInit vi;
1457     MockListener ml;
1458
1459     HW::item<bool> hw_want(true, rc_t::OK);
1460
1461     ADD_EXPECT(interface_cmds::events_cmd(ml));
1462     cmd* itf = new interface_cmds::events_cmd(ml);
1463
1464     HW::enqueue(itf);
1465     HW::write();
1466
1467     HW::dequeue(itf);
1468 }
1469
1470 BOOST_AUTO_TEST_CASE(test_interface_route_domain_change) {
1471     VppInit vi;
1472     const std::string rene = "ReneGoscinny";
1473     rc_t rc = rc_t::OK;
1474
1475     /*
1476      * Create an interface with two IP addresses
1477      */
1478     std::string itf1_name = "host1";
1479     interface itf1(itf1_name,
1480                    interface::type_t::AFPACKET,
1481                    interface::admin_state_t::UP);
1482     HW::item<handle_t> hw_ifh1(2, rc_t::OK);
1483     HW::item<interface::admin_state_t> hw_as_up(interface::admin_state_t::UP, rc_t::OK);
1484     HW::item<interface::admin_state_t> hw_as_down(interface::admin_state_t::DOWN, rc_t::OK);
1485     ADD_EXPECT(interface_cmds::af_packet_create_cmd(hw_ifh1, itf1_name));
1486     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_up, hw_ifh1));
1487     TRY_CHECK_RC(OM::write(rene, itf1));
1488
1489     route::prefix_t pfx_10("10.10.10.10", 24);
1490     l3_binding *l3_1 = new l3_binding(itf1, pfx_10);
1491     HW::item<bool> hw_l3_bind1(true, rc_t::OK);
1492     HW::item<bool> hw_l3_unbind1(false, rc_t::OK);
1493     ADD_EXPECT(l3_binding_cmds::bind_cmd(hw_l3_bind1, hw_ifh1.data(), pfx_10));
1494     TRY_CHECK_RC(OM::write(rene, *l3_1));
1495
1496     route::prefix_t pfx_11("10.10.11.11", 24);
1497     l3_binding *l3_2 = new l3_binding(itf1, pfx_11);
1498     HW::item<bool> hw_l3_bind2(true, rc_t::OK);
1499     HW::item<bool> hw_l3_unbind2(false, rc_t::OK);
1500     ADD_EXPECT(l3_binding_cmds::bind_cmd(hw_l3_bind2, hw_ifh1.data(), pfx_11));
1501     TRY_CHECK_RC(OM::write(rene, *l3_2));
1502
1503     route_domain rd(1);
1504     HW::item<bool> hw_rd_create(true, rc_t::OK);
1505     HW::item<bool> hw_rd_delete(false, rc_t::OK);
1506     HW::item<route::table_id_t> hw_rd_bind(1, rc_t::OK);
1507     HW::item<route::table_id_t> hw_rd_unbind(route::DEFAULT_TABLE, rc_t::OK);
1508     ADD_EXPECT(route_domain_cmds::create_cmd(hw_rd_create, l3_proto_t::IPV4, 1));
1509     ADD_EXPECT(route_domain_cmds::create_cmd(hw_rd_create, l3_proto_t::IPV6, 1));
1510     TRY_CHECK_RC(OM::write(rene, rd));
1511
1512     /*
1513      * update the interface to change to a new route-domain
1514      * expect that the l3-bindings are removed and readded.
1515      */
1516     interface *itf2 = new interface(itf1_name,
1517                                     interface::type_t::AFPACKET,
1518                                     interface::admin_state_t::UP,
1519                                     rd);
1520     ADD_EXPECT(l3_binding_cmds::unbind_cmd(hw_l3_unbind1, hw_ifh1.data(), pfx_10));
1521     ADD_EXPECT(l3_binding_cmds::unbind_cmd(hw_l3_unbind2, hw_ifh1.data(), pfx_11));
1522     ADD_EXPECT(interface_cmds::set_table_cmd(hw_rd_bind, l3_proto_t::IPV4, hw_ifh1));
1523     ADD_EXPECT(interface_cmds::set_table_cmd(hw_rd_bind, l3_proto_t::IPV6, hw_ifh1));
1524     ADD_EXPECT(l3_binding_cmds::bind_cmd(hw_l3_bind1, hw_ifh1.data(), pfx_10));
1525     ADD_EXPECT(l3_binding_cmds::bind_cmd(hw_l3_bind2, hw_ifh1.data(), pfx_11));
1526     TRY_CHECK_RC(OM::write(rene, *itf2));
1527
1528     /*
1529      * mve the interface back to the default route-domain
1530      */
1531     interface itf3(itf1_name,
1532                    interface::type_t::AFPACKET,
1533                    interface::admin_state_t::UP);
1534     ADD_EXPECT(l3_binding_cmds::unbind_cmd(hw_l3_unbind1, hw_ifh1.data(), pfx_10));
1535     ADD_EXPECT(l3_binding_cmds::unbind_cmd(hw_l3_unbind2, hw_ifh1.data(), pfx_11));
1536     ADD_EXPECT(interface_cmds::set_table_cmd(hw_rd_unbind, l3_proto_t::IPV4, hw_ifh1));
1537     ADD_EXPECT(interface_cmds::set_table_cmd(hw_rd_unbind, l3_proto_t::IPV6, hw_ifh1));
1538     ADD_EXPECT(l3_binding_cmds::bind_cmd(hw_l3_bind1, hw_ifh1.data(), pfx_10));
1539     ADD_EXPECT(l3_binding_cmds::bind_cmd(hw_l3_bind2, hw_ifh1.data(), pfx_11));
1540     TRY_CHECK_RC(OM::write(rene, itf3));
1541
1542     delete l3_1;
1543     delete l3_2;
1544     delete itf2;
1545
1546     STRICT_ORDER_OFF();
1547     ADD_EXPECT(l3_binding_cmds::unbind_cmd(hw_l3_unbind1, hw_ifh1.data(), pfx_10));
1548     ADD_EXPECT(l3_binding_cmds::unbind_cmd(hw_l3_unbind2, hw_ifh1.data(), pfx_11));
1549     ADD_EXPECT(interface_cmds::state_change_cmd(hw_as_down, hw_ifh1));
1550     ADD_EXPECT(interface_cmds::af_packet_delete_cmd(hw_ifh1, itf1_name));
1551     ADD_EXPECT(route_domain_cmds::delete_cmd(hw_rd_delete, l3_proto_t::IPV4, 1));
1552     ADD_EXPECT(route_domain_cmds::delete_cmd(hw_rd_delete, l3_proto_t::IPV6, 1));
1553
1554     TRY_CHECK(OM::remove(rene));
1555 }
1556
1557 BOOST_AUTO_TEST_SUITE_END()