npt66: ensure feature is not configured multiple times
[vpp.git] / src / plugins / npt66 / npt66.c
1 // SPDX-License-Identifier: Apache-2.0
2 // Copyright(c) 2023 Cisco Systems, Inc.
3
4 /*
5  * npt66.c: NPT66 plugin
6  * An implementation of Network Prefix Translation for IPv6-to-IPv6 (NPTv6) as
7  * specified in RFC6296.
8  */
9
10 #include <stdio.h>
11 #include <stdint.h>
12 #include <inttypes.h>
13 #include <vlib/vlib.h>
14 #include <vnet/feature/feature.h>
15 #include <vppinfra/pool.h>
16 #include "npt66.h"
17
18 static int
19 npt66_feature_enable_disable (u32 sw_if_index, bool is_add)
20 {
21   if (vnet_feature_enable_disable ("ip6-unicast", "npt66-input", sw_if_index,
22                                    is_add, 0, 0) != 0)
23     return -1;
24   if (vnet_feature_enable_disable ("ip6-output", "npt66-output", sw_if_index,
25                                    is_add, 0, 0) != 0)
26     return -1;
27   return 0;
28 }
29
30 static void
31 ipv6_prefix_zero (ip6_address_t *address, int prefix_len)
32 {
33   int byte_index = prefix_len / 8;
34   int bit_offset = prefix_len % 8;
35   uint8_t mask = (1 << (8 - bit_offset)) - 1;
36   if (byte_index < 16)
37     {
38       address->as_u8[byte_index] &= mask;
39       for (int i = byte_index + 1; i < 16; i++)
40         {
41           address->as_u8[i] = 0;
42         }
43     }
44 }
45
46 int
47 npt66_binding_add_del (u32 sw_if_index, ip6_address_t *internal,
48                        int internal_plen, ip6_address_t *external,
49                        int external_plen, bool is_add)
50 {
51   npt66_main_t *nm = &npt66_main;
52   int rv = 0;
53
54   /* Currently limited to a single binding per interface */
55   npt66_binding_t *b = npt66_interface_by_sw_if_index (sw_if_index);
56
57   if (is_add)
58     {
59       bool configure_feature = false;
60       /* Ensure prefix lengths are less than or equal to a /64 */
61       if (internal_plen > 64 || external_plen > 64)
62         return VNET_API_ERROR_INVALID_VALUE;
63
64       /* Create a binding entry (or update existing) */
65       if (!b)
66         {
67           pool_get_zero (nm->bindings, b);
68           configure_feature = true;
69         }
70       b->internal = *internal;
71       b->internal_plen = internal_plen;
72       b->external = *external;
73       b->external_plen = external_plen;
74       b->sw_if_index = sw_if_index;
75
76       ipv6_prefix_zero (&b->internal, internal_plen);
77       ipv6_prefix_zero (&b->external, external_plen);
78       vec_validate_init_empty (nm->interface_by_sw_if_index, sw_if_index, ~0);
79       nm->interface_by_sw_if_index[sw_if_index] = b - nm->bindings;
80
81       uword delta = 0;
82       delta = ip_csum_add_even (delta, b->external.as_u64[0]);
83       delta = ip_csum_add_even (delta, b->external.as_u64[1]);
84       delta = ip_csum_sub_even (delta, b->internal.as_u64[0]);
85       delta = ip_csum_sub_even (delta, b->internal.as_u64[1]);
86       delta = ip_csum_fold (delta);
87       b->delta = delta;
88
89       if (configure_feature)
90         rv = npt66_feature_enable_disable (sw_if_index, is_add);
91     }
92   else
93     {
94       /* Delete a binding entry */
95       npt66_binding_t *b = npt66_interface_by_sw_if_index (sw_if_index);
96       if (!b)
97         return VNET_API_ERROR_NO_SUCH_ENTRY;
98       nm->interface_by_sw_if_index[sw_if_index] = ~0;
99       pool_put (nm->bindings, b);
100       rv = npt66_feature_enable_disable (sw_if_index, is_add);
101     }
102
103   return rv;
104 }
105
106 /*
107  * Do a lookup in the interface vector (interface_by_sw_if_index)
108  * and return pool entry.
109  */
110 npt66_binding_t *
111 npt66_interface_by_sw_if_index (u32 sw_if_index)
112 {
113   npt66_main_t *nm = &npt66_main;
114
115   if (!nm->interface_by_sw_if_index ||
116       sw_if_index > (vec_len (nm->interface_by_sw_if_index) - 1))
117     return 0;
118   u32 index = nm->interface_by_sw_if_index[sw_if_index];
119   if (index == ~0)
120     return 0;
121   if (pool_is_free_index (nm->bindings, index))
122     return 0;
123   return pool_elt_at_index (nm->bindings, index);
124 }