summaryrefslogtreecommitdiffstats
path: root/test/test_interface_crud.py
blob: 2f08f33c704d2709fb0dee37c8fb99679ea4f0a4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#!/usr/bin/env python3
"""CRUD tests of APIs (Create, Read, Update, Delete) HLD:

- interface up/down/add/delete - interface type:
    - pg (TBD)
    - loopback
    - vhostuser (TBD)
    - af_packet (TBD)
    - netmap (TBD)
    - tuntap (root privileges needed)
    - vxlan (TBD)
"""

import unittest

from scapy.layers.inet import IP, ICMP
from scapy.layers.l2 import Ether

from framework import VppTestCase, VppTestRunner


class TestLoopbackInterfaceCRUD(VppTestCase):
    """CRUD Loopback

    """

    @classmethod
    def setUpClass(cls):
        super(TestLoopbackInterfaceCRUD, cls).setUpClass()
        try:
            cls.create_pg_interfaces(range(1))
            for i in cls.pg_interfaces:
                i.config_ip4().resolve_arp()
        except:
            cls.tearDownClass()
            raise

    @classmethod
    def tearDownClass(cls):
        super(TestLoopbackInterfaceCRUD, cls).tearDownClass()

    @staticmethod
    def create_icmp_stream(src_if, dst_ifs):
        """

        :param VppInterface src_if: Packets are send to this interface,
            using this interfaces remote host.
        :param list dst_ifs: IPv4 ICMP requests are send to interfaces
            addresses.
        :return: List of generated packets.
        """
        pkts = []
        for i in dst_ifs:
            p = (Ether(dst=src_if.local_mac, src=src_if.remote_mac) /
                 IP(src=src_if.remote_ip4, dst=i.local_ip4) /
                 ICMP(id=i.sw_if_index, type='echo-request'))
            pkts.append(p)
        return pkts

    def verify_icmp(self, capture, request_src_if, dst_ifs):
        """

        :param capture: Capture to verify.
        :param VppInterface request_src_if: Interface where was send packets.
        :param list dst_ifs: Interfaces where was generated IPv4 ICMP requests.
        """
        rcvd_icmp_pkts = []
        for pkt in capture:
            try:
                ip = pkt[IP]
                icmp = pkt[ICMP]
            except IndexError:
                pass
            else:
                info = (ip.src, ip.dst, icmp.type, icmp.id)
                rcvd_icmp_pkts.append(info)

        for i in dst_ifs:
            # 0 - icmp echo response
            info = (i.local_ip4, request_src_if.remote_ip4, 0, i.sw_if_index)
            self.assertIn(info, rcvd_icmp_pkts)

    def test_crud(self):
        # create
        loopbacks = self.create_loopback_interfaces(20)
        for i in loopbacks:
            i.local_ip4_prefix_len = 32
            i.config_ip4().admin_up()

        # read (check sw if dump, ip4 fib, ip6 fib)
        if_dump = self.vapi.sw_interface_dump(name_filter_valid=True,
                                              name_filter='loop')
        fib4_dump = self.vapi.ip_route_dump(0)
        for i in loopbacks:
            self.assertTrue(i.is_interface_config_in_dump(if_dump))
            self.assertTrue(i.is_ip4_entry_in_fib_dump(fib4_dump))

        if_dump = self.vapi.sw_interface_dump(name_filter_valid=True,
                                              name_filter='loopXYZ')
        self.assertEqual(len(if_dump), 0)

        # check ping
        stream = self.create_icmp_stream(self.pg0, loopbacks)
        self.pg0.add_stream(stream)
        self.pg_enable_capture(self.pg_interfaces)
        self.pg_start()
        capture = self.pg0.get_capture(expected_count=len(stream))

        self.verify_icmp(capture, self.pg0, loopbacks)

        # delete
        for i in loopbacks:
            i.remove_vpp_config()

        # read (check not in sw if dump, ip4 fib, ip6 fib)
        if_dump = self.vapi.sw_interface_dump()
        fib4_dump = self.vapi.ip_route_dump(0)
        for i in loopbacks:
            self.assertFalse(i.is_interface_config_in_dump(if_dump))
            self.assertFalse(i.is_ip4_entry_in_fib_dump(fib4_dump))

        #  check not ping
        stream = self.create_icmp_stream(self.pg0, loopbacks)
        self.pg0.add_stream(stream)
        self.pg_enable_capture(self.pg_interfaces)
        self.pg_start()
        self.pg0.assert_nothing_captured()

    def test_down(self):
        # create
        loopbacks = self.create_loopback_interfaces(20)
        for i in loopbacks:
            i.local_ip4_prefix_len = 32
            i.config_ip4().admin_up()

        # disable
        for i in loopbacks:
            i.admin_down().unconfig_ip4()

        # read (check not in sw if dump, ip4 fib, ip6 fib)
        if_dump = self.vapi.sw_interface_dump()
        fib4_dump = self.vapi.ip_route_dump(0)
        for i in loopbacks:
            self.assertTrue(i.is_interface_config_in_dump(if_dump))
            self.assertFalse(i.is_ip4_entry_in_fib_dump(fib4_dump))

        #  check not ping
        stream = self.create_icmp_stream(self.pg0, loopbacks)
        self.pg0.add_stream(stream)
        self.pg_enable_capture(self.pg_interfaces)
        self.pg_start()
        self.pg0.assert_nothing_captured()


class TestInterfaceDumpApiLocalOnly(VppTestCase):
    """test_interface_crud.TestInterfaceDumpApiLocalOnly"""

    def test_sw_if_index_0(self):
        rv = self.vapi.sw_interface_dump(sw_if_index=0)
        self.assertEqual(rv[0].sw_if_index, 0)

    def test_sw_if_index_twiddle0(self):
        rv = self.vapi.sw_interface_dump(sw_if_index=0xffffffff)
        self.assertEqual(rv[0].sw_if_index, 0)

    def test_sw_if_index_1_not_existing(self):
        rv = self.vapi.sw_interface_dump(sw_if_index=1)
        self.assertEqual(len(rv), 0, 'expected no records.')


class TestInterfaceDumpApi(VppTestCase):
    """test_interface_crud.TestInterfaceDumpApi"""

    def test_sw_if_index_1(self):
        self.vapi.create_loopback_instance(is_specified=1,
                                           user_instance=10)
        self.vapi.create_loopback_instance(is_specified=1,
                                           user_instance=5)

        # Can I get back the specified record?
        rv = self.vapi.sw_interface_dump(sw_if_index=1)
        self.assertEqual(rv[0].sw_if_index, 1, rv)

        # verify 3 interfaces
        rv = self.vapi.sw_interface_dump(sw_if_index=0xffffffff)
        self.assertEqual(len(rv), 3, 'Expected 3 interfaces.')


if __name__ == '__main__':
    unittest.main(testRunner=VppTestRunner)
.Variable */ .highlight .ow { color: #f92672 } /* Operator.Word */ .highlight .w { color: #f8f8f2 } /* Text.Whitespace */ .highlight .mb { color: #ae81ff } /* Literal.Number.Bin */ .highlight .mf { color: #ae81ff } /* Literal.Number.Float */ .highlight .mh { color: #ae81ff } /* Literal.Number.Hex */ .highlight .mi { color: #ae81ff } /* Literal.Number.Integer */ .highlight .mo { color: #ae81ff } /* Literal.Number.Oct */ .highlight .sa { color: #e6db74 } /* Literal.String.Affix */ .highlight .sb { color: #e6db74 } /* Literal.String.Backtick */ .highlight .sc { color: #e6db74 } /* Literal.String.Char */ .highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */ .highlight .sd { color: #e6db74 } /* Literal.String.Doc */ .highlight .s2 { color: #e6db74 } /* Literal.String.Double */ .highlight .se { color: #ae81ff } /* Literal.String.Escape */ .highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */ .highlight .si { color: #e6db74 } /* Literal.String.Interpol */ .highlight .sx { color: #e6db74 } /* Literal.String.Other */ .highlight .sr { color: #e6db74 } /* Literal.String.Regex */ .highlight .s1 { color: #e6db74 } /* Literal.String.Single */ .highlight .ss { color: #e6db74 } /* Literal.String.Symbol */ .highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #a6e22e } /* Name.Function.Magic */ .highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */ .highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */ .highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */ .highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */ .highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */ } @media (prefers-color-scheme: light) { .highlight .hll { background-color: #ffffcc } .highlight .c { color: #888888 } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { color: #008800; font-weight: bold } /* Keyword */ .highlight .ch { color: #888888 } /* Comment.Hashbang */ .highlight .cm { color: #888888 } /* Comment.Multiline */ .highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */ .highlight .cpf { color: #888888 } /* Comment.PreprocFile */ .highlight .c1 { color: #888888 } /* Comment.Single */ .highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */ }
/*
 * nat.c - skeleton vpp-api-test plug-in
 *
 * Copyright (c) <current-year> <your-organization>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <vat/vat.h>
#include <vlibapi/api.h>
#include <vlibmemory/api.h>

#include <vppinfra/error.h>
#include <vnet/ip/ip.h>
#include <nat/nat.h>

#define __plugin_msg_base snat_test_main.msg_id_base
#include <vlibapi/vat_helper_macros.h>

uword unformat_sw_if_index (unformat_input_t * input, va_list * args);

/* Declare message IDs */
#include <nat/nat_msg_enum.h>

/* define message structures */
#define vl_typedefs
#include <nat/nat_all_api_h.h>
#undef vl_typedefs

/* declare message handlers for each api */

#define vl_endianfun             /* define message structures */
#include <nat/nat_all_api_h.h>
#undef vl_endianfun

/* instantiate all the print functions we know about */
#define vl_print(handle, ...)
#define vl_printfun
#include <nat/nat_all_api_h.h>
#undef vl_printfun

/* Get the API version number. */
#define vl_api_version(n,v) static u32 api_version=(v);
#include <nat/nat_all_api_h.h>
#undef vl_api_version

typedef struct {
    /* API message ID base */
    u16 msg_id_base;
    vat_main_t *vat_main;
} snat_test_main_t;

snat_test_main_t snat_test_main;

#define foreach_standard_reply_retval_handler    \
_(nat44_add_del_address_range_reply)             \
_(nat44_interface_add_del_feature_reply)         \
_(nat44_add_del_static_mapping_reply)            \
_(nat_set_workers_reply)                         \
_(nat44_add_del_interface_addr_reply)            \
_(nat_ipfix_enable_disable_reply)                \
_(nat_det_add_del_map_reply)                     \
_(nat_set_timeouts_reply)                        \
_(nat_det_close_session_out_reply)               \
_(nat_det_close_session_in_reply)

#define _(n)                                            \
    static void vl_api_##n##_t_handler                  \
    (vl_api_##n##_t * mp)                               \
    {                                                   \
        vat_main_t * vam = snat_test_main.vat_main;   \
        i32 retval = ntohl(mp->retval);                 \
        if (vam->async_mode) {                          \
            vam->async_errors += (retval < 0);          \
        } else {                                        \
            vam->retval = retval;                       \
            vam->result_ready = 1;                      \
        }                                               \
    }
foreach_standard_reply_retval_handler;
#undef _

/*
 * Table of message reply handlers, must include boilerplate handlers
 * we just generated
 */
#define foreach_vpe_api_reply_msg                               \
_(NAT44_ADD_DEL_ADDRESS_RANGE_REPLY,                            \
  nat44_add_del_address_range_reply)                            \
_(NAT44_INTERFACE_ADD_DEL_FEATURE_REPLY,                        \
  nat44_interface_add_del_feature_reply)                        \
_(NAT44_ADD_DEL_STATIC_MAPPING_REPLY,                           \
  nat44_add_del_static_mapping_reply)                           \
_(NAT_CONTROL_PING_REPLY, nat_control_ping_reply)               \
_(NAT44_STATIC_MAPPING_DETAILS, nat44_static_mapping_details)   \
_(NAT_SHOW_CONFIG_REPLY, nat_show_config_reply)                 \
_(NAT44_ADDRESS_DETAILS, nat44_address_details)                 \
_(NAT44_INTERFACE_DETAILS, nat44_interface_details)             \
_(NAT_SET_WORKERS_REPLY, nat_set_workers_reply)                 \
_(NAT_WORKER_DETAILS, nat_worker_details)                       \
_(NAT44_ADD_DEL_INTERFACE_ADDR_REPLY,                           \
  nat44_add_del_interface_addr_reply)                           \
_(NAT44_INTERFACE_ADDR_DETAILS, nat44_interface_addr_details)   \
_(NAT_IPFIX_ENABLE_DISABLE_REPLY,                               \
  nat_ipfix_enable_disable_reply)                               \
_(NAT44_USER_DETAILS, nat44_user_details)                       \
_(NAT44_USER_SESSION_DETAILS, nat44_user_session_details)       \
_(NAT_DET_ADD_DEL_MAP_REPLY, nat_det_add_del_map_reply)         \
_(NAT_DET_FORWARD_REPLY, nat_det_forward_reply)                 \
_(NAT_DET_REVERSE_REPLY, nat_det_reverse_reply)                 \
_(NAT_DET_MAP_DETAILS, nat_det_map_details)                     \
_(NAT_SET_TIMEOUTS_REPLY, nat_set_timeouts_reply)               \
_(NAT_GET_TIMEOUTS_REPLY, nat_get_timeouts_reply)               \
_(NAT_DET_CLOSE_SESSION_OUT_REPLY,                              \
  nat_det_close_session_out_reply)                              \
_(NAT_DET_CLOSE_SESSION_IN_REPLY,                               \
  nat_det_close_session_in_reply)                               \
_(NAT_DET_SESSION_DETAILS, nat_det_session_details)

static int api_nat44_add_del_address_range (vat_main_t * vam)
{
  unformat_input_t * i = vam->input;
  ip4_address_t start_addr, end_addr;
  u32 start_host_order, end_host_order;
  vl_api_nat44_add_del_address_range_t * mp;
  u8 is_add = 1;
  int count;
  int ret;

  while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (i, "%U - %U",
                    unformat_ip4_address, &start_addr,
                    unformat_ip4_address, &end_addr))
        ;
      else if (unformat (i, "%U", unformat_ip4_address, &start_addr))
        end_addr = start_addr;
      else if (unformat (i, "del"))
        is_add = 0;
      else
        {
          clib_warning("unknown input '%U'", format_unformat_error, i);
          return -99;
        }
    }

  start_host_order = clib_host_to_net_u32 (start_addr.as_u32);
  end_host_order = clib_host_to_net_u32 (end_addr.as_u32);

  if (end_host_order < start_host_order)
    {
      errmsg ("end address less than start address\n");
      return -99;
    }

  count = (end_host_order - start_host_order) + 1;

  if (count > 1024)
    {
    errmsg ("%U - %U, %d addresses...\n",
           format_ip4_address, &start_addr,
           format_ip4_address, &end_addr,
           count);
    }

  M(NAT44_ADD_DEL_ADDRESS_RANGE, mp);

  memcpy (mp->first_ip_address, &start_addr, 4);
  memcpy (mp->last_ip_address, &end_addr, 4);
  mp->is_add = is_add;

  S(mp);
  W (ret);
  return ret;
}

static int api_nat44_interface_add_del_feature (vat_main_t * vam)
{
  unformat_input_t * i = vam->input;
  vl_api_nat44_interface_add_del_feature_t * mp;
  u32 sw_if_index;
  u8 sw_if_index_set = 0;
  u8 is_inside = 1;
  u8 is_add = 1;
  int ret;

  while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (i, "%U", unformat_sw_if_index, vam, &sw_if_index))
        sw_if_index_set = 1;
      else if (unformat (i, "sw_if_index %d", &sw_if_index))
        sw_if_index_set = 1;
      else if (unformat (i, "out"))
        is_inside = 0;
      else if (unformat (i, "in"))
        is_inside = 1;
      else if (unformat (i, "del"))
        is_add = 0;
      else
        {
          clib_warning("unknown input '%U'", format_unformat_error, i);
          return -99;
        }
    }

  if (sw_if_index_set == 0)
    {
      errmsg ("interface / sw_if_index required\n");
      return -99;
    }

  M(NAT44_INTERFACE_ADD_DEL_FEATURE, mp);
  mp->sw_if_index = ntohl(sw_if_index);
  mp->is_add = is_add;
  mp->is_inside = is_inside;

  S(mp);
  W (ret);
  return ret;
}

static int api_nat44_add_del_static_mapping(vat_main_t * vam)
{
  unformat_input_t * i = vam->input;
  vl_api_nat44_add_del_static_mapping_t * mp;
  u8 external_addr_set = 0;
  u8 local_addr_set = 0;
  u8 is_add = 1;
  u8 addr_only = 1;
  ip4_address_t local_addr, external_addr;
  u32 local_port = 0, external_port = 0, vrf_id = ~0;
  u32 sw_if_index = ~0;
  u8 sw_if_index_set = 0;
  u32 proto = ~0;
  u8 proto_set = 0;
  int ret;

  while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (i, "local_addr %U", unformat_ip4_address, &local_addr))
        local_addr_set = 1;
      else if (unformat (i, "external_addr %U", unformat_ip4_address,
                         &external_addr))
        external_addr_set = 1;
      else if (unformat (i, "local_port %u", &local_port))
        addr_only = 0;
      else if (unformat (i, "external_port %u", &external_port))
        addr_only = 0;
      else if (unformat (i, "external_if %U", unformat_sw_if_index, vam,
                         &sw_if_index))
        sw_if_index_set = 1;
      else if (unformat (i, "external_sw_if_index %d", &sw_if_index))
        sw_if_index_set = 1;
      else if (unformat (i, "vrf %u", &vrf_id))
        ;
      else if (unformat (i, "protocol %u", &proto))
        proto_set = 1;
      else if (unformat (i, "del"))
        is_add = 0;
      else
        {
          clib_warning("unknown input '%U'", format_unformat_error, i);
          return -99;
        }
    }

  if (!addr_only && !proto_set)
    {
      errmsg ("protocol required\n");
      return -99;
    }

  if (!local_addr_set)
    {
      errmsg ("local addr required\n");
      return -99;
    }
  if (!external_addr_set && !sw_if_index_set)
    {
      errmsg ("external addr or interface required\n");
      return -99;
    }

  M(NAT44_ADD_DEL_STATIC_MAPPING, mp);
  mp->is_add = is_add;
  mp->addr_only = addr_only;
  mp->local_port = ntohs ((u16) local_port);
  mp->external_port = ntohs ((u16) external_port);
  mp->external_sw_if_index = ntohl (sw_if_index);
  mp->vrf_id = ntohl (vrf_id);
  mp->protocol = (u8) proto;
  memcpy (mp->local_ip_address, &local_addr, 4);
  memcpy (mp->external_ip_address, &external_addr, 4);

  S(mp);
  W (ret);
  return ret;
}

static void vl_api_nat_control_ping_reply_t_handler
  (vl_api_nat_control_ping_reply_t * mp)
{
  vat_main_t *vam = &vat_main;
  i32 retval = ntohl (mp->retval);
  if (vam->async_mode)
    {
      vam->async_errors += (retval < 0);
    }
  else
    {
      vam->retval = retval;
      vam->result_ready = 1;
    }
}

static void vl_api_nat44_static_mapping_details_t_handler
  (vl_api_nat44_static_mapping_details_t *mp)
{
  snat_test_main_t * sm = &snat_test_main;
  vat_main_t *vam = sm->vat_main;

  if (mp->addr_only && mp->external_sw_if_index != ~0)
      fformat (vam->ofp, "%15U%6s%15d%6s%11d%6d\n",
               format_ip4_address, &mp->local_ip_address, "",
               ntohl (mp->external_sw_if_index), "",
               ntohl (mp->vrf_id),
               mp->protocol);
  else if (mp->addr_only && mp->external_sw_if_index == ~0)
      fformat (vam->ofp, "%15U%6s%15U%6s%11d%6d\n",
               format_ip4_address, &mp->local_ip_address, "",
               format_ip4_address, &mp->external_ip_address, "",
               ntohl (mp->vrf_id),
               mp->protocol);
  else if (!mp->addr_only && mp->external_sw_if_index != ~0)
      fformat (vam->ofp, "%15U%6d%15d%6d%11d%6d\n",
               format_ip4_address, &mp->local_ip_address,
               ntohs (mp->local_port),
               ntohl (mp->external_sw_if_index),
               ntohs (mp->external_port),
               ntohl (mp->vrf_id),
               mp->protocol);
  else
      fformat (vam->ofp, "%15U%6d%15U%6d%11d%6d\n",
               format_ip4_address, &mp->local_ip_address,
               ntohs (mp->local_port),
               format_ip4_address, &mp->external_ip_address,
               ntohs (mp->external_port),
               ntohl (mp->vrf_id),
               mp->protocol);

}

static int api_nat44_static_mapping_dump(vat_main_t * vam)
{
  vl_api_nat44_static_mapping_dump_t * mp;
  vl_api_nat_control_ping_t *mp_ping;
  int ret;

  if (vam->json_output)
    {
      clib_warning ("JSON output not supported for nat44_static_mapping_dump");
      return -99;
    }

  fformat (vam->ofp, "%21s%21s\n", "local", "external");
  fformat (vam->ofp, "%15s%6s%15s%6s%11s%6s\n", "address", "port",
           "address/if_idx", "port", "vrf", "proto");

  M(NAT44_STATIC_MAPPING_DUMP, mp);
  S(mp);

  /* Use a control ping for synchronization */
  M(NAT_CONTROL_PING, mp_ping);
  S(mp_ping);

  W (ret);
  return ret;
}

static void vl_api_nat_show_config_reply_t_handler
  (vl_api_nat_show_config_reply_t *mp)
{
  snat_test_main_t * sm = &snat_test_main;
  vat_main_t *vam = sm->vat_main;
  i32 retval = ntohl (mp->retval);

  if (retval >= 0)
    {
      fformat (vam->ofp, "translation hash buckets %d\n",
               ntohl (mp->translation_buckets));
      fformat (vam->ofp, "translation hash memory %d\n",
               ntohl (mp->translation_memory_size));
      fformat (vam->ofp, "user hash buckets %d\n", ntohl (mp->user_buckets));
      fformat (vam->ofp, "user hash memory %d\n", ntohl (mp->user_memory_size));
      fformat (vam->ofp, "max translations per user %d\n",
               ntohl (mp->max_translations_per_user));
      fformat (vam->ofp, "outside VRF id %d\n", ntohl (mp->outside_vrf_id));
      fformat (vam->ofp, "inside VRF id %d\n", ntohl (mp->inside_vrf_id));
      if (mp->static_mapping_only)
        {
          fformat (vam->ofp, "static mapping only");
          if (mp->static_mapping_connection_tracking)
            fformat (vam->ofp, " connection tracking");
          fformat (vam->ofp, "\n");
        }
    }
  vam->retval = retval;
  vam->result_ready = 1;
}

static int api_nat_show_config(vat_main_t * vam)
{
  vl_api_nat_show_config_t * mp;
  int ret;

  if (vam->json_output)
    {
      clib_warning ("JSON output not supported for nat_show_config");
      return -99;
    }

  M(NAT_SHOW_CONFIG, mp);
  S(mp);
  W (ret);
  return ret;
}

static void vl_api_nat44_address_details_t_handler
  (vl_api_nat44_address_details_t *mp)
{
  snat_test_main_t * sm = &snat_test_main;
  vat_main_t *vam = sm->vat_main;

  fformat (vam->ofp, "%U\n", format_ip4_address, &mp->ip_address);
}

static int api_nat44_address_dump(vat_main_t * vam)
{
  vl_api_nat44_address_dump_t * mp;
  vl_api_nat_control_ping_t *mp_ping;
  int ret;

  if (vam->json_output)
    {
      clib_warning ("JSON output not supported for nat44_address_dump");
      return -99;
    }

  M(NAT44_ADDRESS_DUMP, mp);
  S(mp);

  /* Use a control ping for synchronization */
  M(NAT_CONTROL_PING, mp_ping);
  S(mp_ping);

  W (ret);
  return ret;
}

static void vl_api_nat44_interface_details_t_handler
  (vl_api_nat44_interface_details_t *mp)
{
  snat_test_main_t * sm = &snat_test_main;
  vat_main_t *vam = sm->vat_main;

  fformat (vam->ofp, "sw_if_index %d %s\n", ntohl (mp->sw_if_index),
           mp->is_inside ? "in" : "out");
}

static int api_nat44_interface_dump(vat_main_t * vam)
{
  vl_api_nat44_interface_dump_t * mp;
  vl_api_nat_control_ping_t *mp_ping;
  int ret;

  if (vam->json_output)
    {
      clib_warning ("JSON output not supported for nat44_address_dump");
      return -99;
    }

  M(NAT44_INTERFACE_DUMP, mp);
  S(mp);

  /* Use a control ping for synchronization */
  M(NAT_CONTROL_PING, mp_ping);
  S(mp_ping);

  W (ret);
  return ret;
}

static int api_nat_set_workers (vat_main_t * vam)
{
  unformat_input_t * i = vam->input;
  vl_api_nat_set_workers_t * mp;
  uword *bitmap;
  int ret;

  while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (i, "%U", unformat_bitmap_list, &bitmap))
        ;
      else
        {
          clib_warning("unknown input '%U'", format_unformat_error, i);
          return -99;
        }
    }

  M(NAT_SET_WORKERS, mp);
  mp->worker_mask = clib_host_to_net_u64 (bitmap[0]);

  S(mp);
  W (ret);
  return ret;
}

static void vl_api_nat_worker_details_t_handler
  (vl_api_nat_worker_details_t *mp)
{
  snat_test_main_t * sm = &snat_test_main;
  vat_main_t *vam = sm->vat_main;

  fformat (vam->ofp, "worker_index %d (%s at lcore %u)\n",
           ntohl (mp->worker_index), mp->name, ntohl (mp->lcore_id));
}

static int api_nat_worker_dump(vat_main_t * vam)
{
  vl_api_nat_worker_dump_t * mp;
  vl_api_nat_control_ping_t *mp_ping;
  int ret;

  if (vam->json_output)
    {
      clib_warning ("JSON output not supported for nat_address_dump");
      return -99;
    }

  M(NAT_WORKER_DUMP, mp);
  S(mp);

  /* Use a control ping for synchronization */
  M(NAT_CONTROL_PING, mp_ping);
  S(mp_ping);

  W (ret);
  return ret;
}

static int api_nat44_add_del_interface_addr (vat_main_t * vam)
{
  unformat_input_t * i = vam->input;
  vl_api_nat44_add_del_interface_addr_t * mp;
  u32 sw_if_index;
  u8 sw_if_index_set = 0;
  u8 is_add = 1;
  int ret;

  while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (i, "%U", unformat_sw_if_index, vam, &sw_if_index))
        sw_if_index_set = 1;
      else if (unformat (i, "sw_if_index %d", &sw_if_index))
        sw_if_index_set = 1;
      else if (unformat (i, "del"))
        is_add = 0;
      else
        {
          clib_warning("unknown input '%U'", format_unformat_error, i);
          return -99;
        }
    }

  if (sw_if_index_set == 0)
    {
      errmsg ("interface / sw_if_index required\n");
      return -99;
    }

  M(NAT44_ADD_DEL_INTERFACE_ADDR, mp);
  mp->sw_if_index = ntohl(sw_if_index);
  mp->is_add = is_add;

  S(mp);
  W (ret);
  return ret;
}

static void vl_api_nat44_interface_addr_details_t_handler
  (vl_api_nat44_interface_addr_details_t *mp)
{
  snat_test_main_t * sm = &snat_test_main;
  vat_main_t *vam = sm->vat_main;

  fformat (vam->ofp, "sw_if_index %d\n", ntohl (mp->sw_if_index));
}

static int api_nat44_interface_addr_dump(vat_main_t * vam)
{
  vl_api_nat44_interface_addr_dump_t * mp;
  vl_api_nat_control_ping_t *mp_ping;
  int ret;

  if (vam->json_output)
    {
      clib_warning ("JSON output not supported for nat44_address_dump");
      return -99;
    }

  M(NAT44_INTERFACE_ADDR_DUMP, mp);
  S(mp);

  /* Use a control ping for synchronization */
  M(NAT_CONTROL_PING, mp_ping);
  S(mp_ping);

  W (ret);
  return ret;
}

static int api_nat_ipfix_enable_disable (vat_main_t * vam)
{
  unformat_input_t * i = vam->input;
  vl_api_nat_ipfix_enable_disable_t * mp;
  u32 domain_id = 0;
  u32 src_port = 0;
  u8 enable = 1;
  int ret;

  while (unformat_check_input (i) != UNFORMAT_END_OF_INPUT)
    {
      if (unformat (i, "domain %d", &domain_id))
        ;
      else if (unformat (i, "src_port %d", &src_port))
        ;
      else if (unformat (i, "disable"))
        enable = 0;
      else
        {
          clib_warning("unknown input '%U'", format_unformat_error, i);
          return -99;
        }
    }

  M(NAT_IPFIX_ENABLE_DISABLE, mp);
  mp->domain_id = htonl(domain_id);
  mp->src_port = htons((u16) src_port);
  mp->enable = enable;

  S(mp);
  W (ret);
  return ret;
}

static void vl_api_nat44_user_session_details_t_handler
  (vl_api_nat44_user_session_details_t *mp)
{
  snat_test_main_t * sm = &snat_test_main;
  vat_main_t *vam = sm->vat_main;

  fformat(vam->ofp, "%s session %U:%d to %U:%d protocol id %d "
                    "total packets %d total bytes %lld\n",
          mp->is_static ? "static" : "dynamic",
          format_ip4_address, mp->inside_ip_address, ntohs(mp->inside_port),
          format_ip4_address, mp->outside_ip_address, ntohs(mp->outside_port),
          ntohs(mp->protocol), ntohl(mp->total_pkts),
          clib_net_to_host_u64(mp->total_bytes));
}

static int api_nat44_user_session_dump(vat_main_t * vam)
{
  unformat_input_t* i = vam->input;
  vl_api_nat44_user_session_dump_t * mp;
  vl_api_nat_control_ping_t *mp_ping;
  ip4_address_t addr;
  u32 vrf_id = ~0;
  int ret;

  if (vam->json_output)
    {
      clib_warning ("JSON output not supported for nat44_address_dump");
      return -99;
    }

  if (unformat (i, "ip_address %U vrf_id %d",
                unformat_ip4_address, &addr, &vrf_id))
    ;
  else
    {
      clib_warning("unknown input '%U'", format_unformat_error, i);
      return -99;
    }

  M(NAT44_USER_SESSION_DUMP, mp);
  S(mp);

  /* Use a control ping for synchronization */
  M(NAT_CONTROL_PING, mp_ping);
  memset(mp->ip_address, 0, 16);
  clib_memcpy(mp->ip_address, &addr, 4);
  mp->vrf_id = htonl(vrf_id);
  S(mp_ping);

  W (ret);
  return ret;
}

static void vl_api_nat44_user_details_t_handler
  (vl_api_nat44_user_details_t *mp)
{
  snat_test_main_t * sm = &snat_test_main;
  vat_main_t *vam = sm->vat_main;

  fformat(vam->ofp, "user with ip %U with vrf_id %d "
                    "with %d sessions and %d static sessions\n",
          format_ip4_address, mp->ip_address, ntohl(mp->vrf_id),
          ntohl(mp->nsessions), ntohl(mp->nstaticsessions));
}

static int api_nat44_user_dump(vat_main_t * vam)
{
  vl_api_nat44_user_dump_t * mp;
  vl_api_nat_control_ping_t *mp_ping;
  int ret;

  if (vam->json_output)
    {
      clib_warning ("JSON output not supported for nat44_address_dump");
      return -99;
    }

  M(NAT44_USER_DUMP, mp);
  S(mp);

  /* Use a control ping for synchronization */
  M(NAT_CONTROL_PING, mp_ping);
  S(mp_ping);

  W (ret);
  return ret;
}

static int api_nat_det_add_del_map (vat_main_t * vam)
{
  unformat_input_t * i = vam->input;
  vl_api_nat_det_add_del_map_t * mp;
  ip4_address_t in_addr, out_addr;
  u32 in_plen, out_plen;
  u8 is_add = 1;
  int ret;

  if (unformat (i, "in %U/%d out %U/%d",
                unformat_ip4_address, &in_addr, &in_plen,
                unformat_ip4_address, &out_addr, &out_plen))
    ;
  else if (unformat (i, "del"))
    is_add = 0;
  else
    {
      clib_warning("unknown input '%U'", format_unformat_error, i);
      return -99;
    }

  M(NAT_DET_ADD_DEL_MAP, mp);
  clib_memcpy(mp->in_addr, &in_addr, 4);
  mp->in_plen = in_plen;
  clib_memcpy(mp->out_addr, &out_addr, 4);
  mp->out_plen = out_plen;
  mp->is_add = is_add;

  S(mp);
  W (ret);
  return ret;
}

static void vl_api_nat_det_forward_reply_t_handler
  (vl_api_nat_det_forward_reply_t *mp)
{
  snat_test_main_t * sm = &snat_test_main;
  vat_main_t *vam = sm->vat_main;
  i32 retval = ntohl(mp->retval);

  if (retval >= 0)
  {
    fformat (vam->ofp, "outside address %U", format_ip4_address, &mp->out_addr);
    fformat (vam->ofp, " outside port range start %d", ntohs(mp->out_port_lo));
    fformat (vam->ofp, " outside port range end %d\n", ntohs(mp->out_port_hi));
  }

  vam->retval = retval;
  vam->result_ready = 1;
}

static int api_nat_det_forward (vat_main_t * vam)
{
  unformat_input_t * i = vam->input;
  vl_api_nat_det_forward_t * mp;
  ip4_address_t in_addr;
  int ret;

  if (unformat (i, "%U", unformat_ip4_address, &in_addr))
    ;
  else
    {
      clib_warning("unknown input '%U'", format_unformat_error, i);
      return -99;
    }

  M(NAT_DET_FORWARD, mp);
  clib_memcpy(mp->in_addr, &in_addr, 4);

  S(mp);
  W(ret);
  return ret;
}

static void vl_api_nat_det_reverse_reply_t_handler
  (vl_api_nat_det_reverse_reply_t *mp)
{
  snat_test_main_t * sm = &snat_test_main;
  vat_main_t *vam = sm->vat_main;
  i32 retval = ntohl(mp->retval);

  if (retval >= 0)
  {
    fformat (vam->ofp, "inside address %U\n", format_ip4_address, &mp->in_addr);
  }

  vam->retval = retval;
  vam->result_ready = 1;
}

static int api_nat_det_reverse (vat_main_t * vam)
{
  unformat_input_t * i = vam->input;
  vl_api_nat_det_reverse_t * mp;
  ip4_address_t out_addr;
  u32 out_port;
  int ret;

  if (unformat (i, "%U %d", unformat_ip4_address, &out_addr, &out_port))
    ;
  else
    {
      clib_warning("unknown input '%U'", format_unformat_error, i);
      return -99;
    }

  M(NAT_DET_REVERSE, mp);
  clib_memcpy(mp->out_addr, &out_addr, 4);
  mp->out_port = htons((u16)out_port);

  S(mp);
  W(ret);
  return ret;
}

static void vl_api_nat_det_map_details_t_handler
  (vl_api_nat_det_map_details_t *mp)
{
  snat_test_main_t * sm = &snat_test_main;
  vat_main_t *vam = sm->vat_main;

  fformat (vam->ofp, "Deterministic S-NAT mapping in %U/%d out %U/%d "
                     "ports per host %d sharing ratio %d "
                     "number of sessions %d",
           format_ip4_address, mp->in_addr, mp->in_plen,
           format_ip4_address, mp->out_addr, mp->out_plen,
           ntohs(mp->ports_per_host), ntohl(mp->sharing_ratio),
           ntohl(mp->ses_num));
}

static int api_nat_det_map_dump(vat_main_t * vam)
{
  vl_api_nat_det_map_dump_t * mp;
  vl_api_nat_control_ping_t *mp_ping;
  int ret;

  if (vam->json_output)
    {
      clib_warning ("JSON output not supported for nat_det_map_dump");
      return -99;
    }

  M(NAT_DET_MAP_DUMP, mp);
  S(mp);

  /* Use a control ping for synchronization */
  M(NAT_CONTROL_PING, mp_ping);
  S(mp_ping);

  W (ret);
  return ret;
}

static int api_nat_set_timeouts (vat_main_t * vam)
{
  unformat_input_t * i = vam->input;
  vl_api_nat_set_timeouts_t * mp;
  u32 udp = SNAT_UDP_TIMEOUT;
  u32 tcp_established = SNAT_TCP_ESTABLISHED_TIMEOUT;
  u32 tcp_transitory = SNAT_TCP_TRANSITORY_TIMEOUT;
  u32 icmp = SNAT_ICMP_TIMEOUT;
  int ret;

  if (unformat (i, "udp %d", &udp))
    ;
  else if (unformat (i, "tcp_established %d", &tcp_established))
    ;
  else if (unformat (i, "tcp_transitory %d", &tcp_transitory))
    ;
  else if (unformat (i, "icmp %d", &icmp))
    ;
  else
    {
      clib_warning("unknown input '%U'", format_unformat_error, i);
      return -99;
    }

  M(NAT_SET_TIMEOUTS, mp);
  mp->udp = htonl(udp);
  mp->tcp_established = htonl(tcp_established);
  mp->tcp_transitory = htonl(tcp_transitory);
  mp->icmp = htonl(icmp);

  S(mp);
  W (ret);
  return ret;
}

static void vl_api_nat_get_timeouts_reply_t_handler
  (vl_api_nat_get_timeouts_reply_t *mp)
{
  snat_test_main_t * sm = &snat_test_main;
  vat_main_t *vam = sm->vat_main;
  i32 retval = ntohl (mp->retval);

  if (retval >= 0)
    {
      fformat (vam->ofp, "udp timeout: %dsec\n", ntohl (mp->udp));
      fformat (vam->ofp, "tcp-established timeout: %dsec",
               ntohl (mp->tcp_established));
      fformat (vam->ofp, "tcp-transitory timeout: %dsec",
               ntohl (mp->tcp_transitory));
      fformat (vam->ofp, "icmp timeout: %dsec", ntohl (mp->icmp));
    }
  vam->retval = retval;
  vam->result_ready = 1;
}

static int api_nat_get_timeouts(vat_main_t * vam)
{
  vl_api_nat_get_timeouts_t * mp;
  int ret;

  if (vam->json_output)
    {
      clib_warning ("JSON output not supported for nat_get_timeouts");
      return -99;
    }

  M(NAT_GET_TIMEOUTS, mp);
  S(mp);
  W (ret);
  return ret;
}

static int api_nat_det_close_session_out (vat_main_t * vam)
{
  unformat_input_t * i = vam->input;
  vl_api_nat_det_close_session_out_t * mp;
  ip4_address_t out_addr, ext_addr;
  u32 out_port, ext_port;
  int ret;

  if (unformat (i, "%U:%d %U:%d",
                unformat_ip4_address, &out_addr, &out_port,
                unformat_ip4_address, &ext_addr, &ext_port))
    ;
  else
    {
      clib_warning("unknown input '%U'", format_unformat_error, i);
      return -99;
    }

  M(NAT_DET_CLOSE_SESSION_OUT, mp);
  clib_memcpy(mp->out_addr, &out_addr, 4);
  mp->out_port = ntohs((u16)out_port);
  clib_memcpy(mp->ext_addr, &ext_addr, 4);
  mp->ext_port = ntohs((u16)ext_port);

  S(mp);
  W (ret);
  return ret;
}

static int api_nat_det_close_session_in (vat_main_t * vam)
{
  unformat_input_t * i = vam->input;
  vl_api_nat_det_close_session_in_t * mp;
  ip4_address_t in_addr, ext_addr;
  u32 in_port, ext_port;
  int ret;

  if (unformat (i, "%U:%d %U:%d",
                unformat_ip4_address, &in_addr, &in_port,
                unformat_ip4_address, &ext_addr, &ext_port))
    ;
  else
    {
      clib_warning("unknown input '%U'", format_unformat_error, i);
      return -99;
    }

  M(NAT_DET_CLOSE_SESSION_IN, mp);
  clib_memcpy(mp->in_addr, &in_addr, 4);
  mp->in_port = ntohs((u16)in_port);
  clib_memcpy(mp->ext_addr, &ext_addr, 4);
  mp->ext_port = ntohs((u16)ext_port);

  S(mp);
  W (ret);
  return ret;
}

static void vl_api_nat_det_session_details_t_handler
  (vl_api_nat_det_session_details_t *mp)
{
  snat_test_main_t * sm = &snat_test_main;
  vat_main_t *vam = sm->vat_main;

  fformat(vam->ofp, "deterministic session, external host address %U, "
                    "external host port %d, outer port %d, inside port %d",
          format_ip4_address, mp->ext_addr, mp->ext_port,
          mp->out_port, mp->in_port);
}

static int api_nat_det_session_dump(vat_main_t * vam)
{
  unformat_input_t* i = vam->input;
  vl_api_nat_det_session_dump_t * mp;
  vl_api_nat_control_ping_t *mp_ping;
  ip4_address_t user_addr;
  int ret;

  if (vam->json_output)
    {
      clib_warning ("JSON output not supported for nat_det_session_dump");
      return -99;
    }

  if (unformat (i, "user_addr %U", unformat_ip4_address, &user_addr))
    ;
  else
    {
      clib_warning ("unknown input '%U'", format_unformat_error, i);
      return -99;
    }

  M(NAT_DET_SESSION_DUMP, mp);
  clib_memcpy (&mp->user_addr, &user_addr, 4);
  S(mp);

  /* Use a control ping for synchronization */
  M(NAT_CONTROL_PING, mp_ping);
  S(mp_ping);

  W (ret);
  return ret;
}

/*
 * List of messages that the api test plugin sends,
 * and that the data plane plugin processes
 */
#define foreach_vpe_api_msg                                       \
_(nat44_add_del_address_range, "<start-addr> [- <end-addr] [del]")\
_(nat44_interface_add_del_feature,                                \
  "<intfc> | sw_if_index <id> [in] [out] [del]")                  \
_(nat44_add_del_static_mapping, "local_addr <ip>"                 \
  " (external_addr <ip> | external_if <intfc> |"                  \
  " external_sw_if_ndex <id>) [local_port <n>]"                   \
  " [external_port <n>] [vrf <table-id>] [del] protocol <n>")     \
_(nat_set_workers, "<wokrers_bitmap>")                            \
_(nat44_static_mapping_dump, "")                                  \
_(nat_show_config, "")                                            \
_(nat44_address_dump, "")                                         \
_(nat44_interface_dump, "")                                       \
_(nat_worker_dump, "")                                            \
_(nat44_add_del_interface_addr,                                   \
  "<intfc> | sw_if_index <id> [del]")                             \
_(nat44_interface_addr_dump, "")                                  \
_(nat_ipfix_enable_disable, "[domain <id>] [src_port <n>] "       \
  "[disable]")                                                    \
_(nat44_user_dump, "")                                            \
_(nat44_user_session_dump, "ip_address <ip> vrf_id <table-id>")   \
_(nat_det_add_del_map, "in <in_addr>/<in_plen> out "              \
  "<out_addr>/<out_plen> [del]")                                  \
_(nat_det_forward, "<in_addr>")                                   \
_(nat_det_reverse, "<out_addr> <out_port>")                       \
_(nat_det_map_dump, "")                                           \
_(nat_set_timeouts, "[udp <sec> | tcp_established <sec> | "       \
  "tcp_transitory <sec> | icmp <sec>]")                           \
_(nat_get_timeouts, "")                                           \
_(nat_det_close_session_out, "<out_addr>:<out_port> "             \
  "<ext_addr>:<ext_port>")                                        \
_(nat_det_close_session_in, "<in_addr>:<in_port> "                \
  "<out_addr>:<out_port>")                                        \
_(nat_det_session_dump, "ip_address <user_addr>")

static void
snat_vat_api_hookup (vat_main_t *vam)
{
  snat_test_main_t * sm __attribute__((unused)) = &snat_test_main;
  /* Hook up handlers for replies from the data plane plug-in */
#define _(N,n)                                                  \
  vl_msg_api_set_handlers((VL_API_##N + sm->msg_id_base),       \
                          #n,                                   \
                          vl_api_##n##_t_handler,               \
                          vl_noop_handler,                      \
                          vl_api_##n##_t_endian,                \
                          vl_api_##n##_t_print,                 \
                          sizeof(vl_api_##n##_t), 1);
  foreach_vpe_api_reply_msg;
#undef _

  /* API messages we can send */
#define _(n,h)                                          \
  hash_set_mem (vam->function_by_name, #n, api_##n);
  foreach_vpe_api_msg;
#undef _

  /* Help strings */
#define _(n,h) hash_set_mem (vam->help_by_name, #n, h);
  foreach_vpe_api_msg;
#undef _
}

clib_error_t * vat_plugin_register (vat_main_t *vam)
{
  snat_test_main_t * sm = &snat_test_main;
  u8 * name;

  sm->vat_main = vam;

  /* Ask the vpp engine for the first assigned message-id */
  name = format (0, "nat_%08x%c", api_version, 0);
  sm->msg_id_base = vl_client_get_first_plugin_msg_id ((char *) name);

  if (sm->msg_id_base != (u16) ~0)
    snat_vat_api_hookup (vam);

  vec_free(name);

  return 0;
}