nat: 1:1 policy NAT

A NAT sub-plugin doing statically configured match/rewrite on IP4 input or output.
It's stateless (no connection tracking).
Currently it supports rewriting of SA, DA and TCP/UDP ports.
It should be simple to add new rewrites if required.

API:
pnat_binding_add, pnat_binding_del, pnat_bindings_get, pnat_interfaces_get

CLI:
set pnat translation interface <name> match <5-tuple> rewrite <5-tuple> {in|out} [del]
show pnat translations
show pnat interfaces

Trying a new C based unit testing scheme. Where the graph node is tested
in isolation. See pnat/pnat_test.c.
Also added new cmake targets to generate coverage directly.
E.g.:
make test_pnat-ccov-report

File '/vpp/sdnat/src/plugins/nat/pnat/pnat.c':
Name                               Regions    Miss   Cover     Lines    Miss   Cover
------------------------------------------------------------------------------------
pnat_interface_by_sw_if_index           39       8  79.49%        13       0 100.00%
pnat_instructions_from_mask              9       0 100.00%        13       0 100.00%
pnat_binding_add                        64       8  87.50%        31       2  93.55%
pnat_flow_lookup                         4       4   0.00%        10      10   0.00%
pnat_binding_attach                    104      75  27.88%        33       6  81.82%
pnat_binding_detach                     30       5  83.33%        23       2  91.30%
pnat_binding_del                        97      33  65.98%        17       3  82.35%
pnat.c:pnat_calc_key_from_5tuple         9       1  88.89%        14       1  92.86%
pnat.c:pnat_interface_check_mask        10       2  80.00%        11       2  81.82%
pnat.c:pnat_enable                       5       0 100.00%        11       0 100.00%
pnat.c:pnat_enable_interface           107      26  75.70%        60      15  75.00%
pnat.c:pnat_disable_interface           91      30  67.03%        32       7  78.12%
pnat.c:pnat_disable                      7       2  71.43%        13       7  46.15%
------------------------------------------------------------------------------------
TOTAL                                  576     194  66.32%       281      55  80.43%

File '/vpp/sdnat/src/plugins/nat/pnat/pnat_node.h':
Name                               Regions    Miss   Cover     Lines    Miss   Cover
------------------------------------------------------------------------------------
pnat_test.c:pnat_node_inline            67      11  83.58%       115       1  99.13%
pnat_test.c:pnat_calc_key                9       2  77.78%        14       2  85.71%
pnat_test.c:pnat_rewrite_ip4            55      11  80.00%        60      12  80.00%
pnat_test.c:format_pnat_trace            1       1   0.00%        12      12   0.00%
pnat_node.c:pnat_node_inline            63      63   0.00%       115     115   0.00%
pnat_node.c:pnat_calc_key                9       9   0.00%        14      14   0.00%
pnat_node.c:pnat_rewrite_ip4            55      55   0.00%        60      60   0.00%
pnat_node.c:format_pnat_trace            5       5   0.00%        12      12   0.00%
------------------------------------------------------------------------------------
TOTAL                                  264     157  40.53%       402     228  43.28%

Type: feature
Change-Id: I9c897f833603054a8303e7369ebff6512517c9e0
Signed-off-by: Ole Troan <ot@cisco.com>
diff --git a/src/tools/vppapigen/vppapigen.py b/src/tools/vppapigen/vppapigen.py
index 7c59696..33510b8 100755
--- a/src/tools/vppapigen/vppapigen.py
+++ b/src/tools/vppapigen/vppapigen.py
@@ -635,7 +635,7 @@
         '''counter_elements : counter_element
                             | counter_elements counter_element'''
         if len(p) == 2:
-            p[0] = p[1]
+            p[0] = [p[1]]
         else:
             if type(p[1]) is dict:
                 p[0] = [p[1], p[2]]
diff --git a/src/tools/vppapigen/vppapigen_c.py b/src/tools/vppapigen/vppapigen_c.py
index 0ded2af..0798e9a 100644
--- a/src/tools/vppapigen/vppapigen_c.py
+++ b/src/tools/vppapigen/vppapigen_c.py
@@ -73,9 +73,7 @@
         write = self.stream.write
         write('#endif\n')
 
-    def get_json_func(self, t):
-        '''Given the type, returns the function to use to create a
-        cJSON object'''
+    def get_base_type(self, t):
         vt_type = None
         try:
             vt = self.types_hash[t]
@@ -83,6 +81,12 @@
                 vt_type = vt.alias['type']
         except KeyError:
             vt = t
+        return vt, vt_type
+
+    def get_json_func(self, t):
+        '''Given the type, returns the function to use to create a
+        cJSON object'''
+        vt, vt_type = self.get_base_type(t)
 
         if t in self.is_number or vt_type in self.is_number:
             return 'cJSON_AddNumberToObject', '', False
@@ -101,6 +105,9 @@
             return 'cJSON_CreateNumber', ''
         if t == 'bool':
             return 'cJSON_CreateBool', ''
+        vt, vt_type = self.get_base_type(t)
+        if vt.type == 'Enum' or vt.type == 'EnumFlag':
+            return '{t}_tojson'.format(t=t), ''
         return '{t}_tojson'.format(t=t), '&'
 
     def print_string(self, o):
@@ -1039,7 +1046,7 @@
 '''
 
     output = ''
-    if o.fieldtype == 'u8' or o.fieldtype == 'string':
+    if o.fieldtype == 'u8' or o.fieldtype == 'string' or o.fieldtype == 'bool':
         output += '    /* a->{n} = a->{n} (no-op) */\n'.format(n=o.fieldname)
     else:
         lfield = 'a->' + o.lengthfield if o.lengthfield else o.length