Line data Source code
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 16 : npt66_feature_enable_disable (u32 sw_if_index, bool is_add) 20 : { 21 16 : if (vnet_feature_enable_disable ("ip6-unicast", "npt66-input", sw_if_index, 22 : is_add, 0, 0) != 0) 23 0 : return -1; 24 16 : if (vnet_feature_enable_disable ("ip6-output", "npt66-output", sw_if_index, 25 : is_add, 0, 0) != 0) 26 0 : return -1; 27 16 : return 0; 28 : } 29 : 30 : static void 31 16 : ipv6_prefix_zero (ip6_address_t *address, int prefix_len) 32 : { 33 16 : int byte_index = prefix_len / 8; 34 16 : int bit_offset = prefix_len % 8; 35 16 : uint8_t mask = (1 << (8 - bit_offset)) - 1; 36 16 : if (byte_index < 16) 37 : { 38 16 : address->as_u8[byte_index] &= mask; 39 164 : for (int i = byte_index + 1; i < 16; i++) 40 : { 41 148 : address->as_u8[i] = 0; 42 : } 43 : } 44 16 : } 45 : 46 : int 47 16 : 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 16 : npt66_main_t *nm = &npt66_main; 52 16 : int rv = 0; 53 : 54 : /* Currently limited to a single binding per interface */ 55 16 : npt66_binding_t *b = npt66_interface_by_sw_if_index (sw_if_index); 56 : 57 16 : if (is_add) 58 : { 59 8 : bool configure_feature = false; 60 : /* Ensure prefix lengths are less than or equal to a /64 */ 61 8 : if (internal_plen > 64 || external_plen > 64) 62 0 : return VNET_API_ERROR_INVALID_VALUE; 63 : 64 : /* Create a binding entry (or update existing) */ 65 8 : if (!b) 66 : { 67 8 : pool_get_zero (nm->bindings, b); 68 8 : configure_feature = true; 69 : } 70 8 : b->internal = *internal; 71 8 : b->internal_plen = internal_plen; 72 8 : b->external = *external; 73 8 : b->external_plen = external_plen; 74 8 : b->sw_if_index = sw_if_index; 75 : 76 8 : ipv6_prefix_zero (&b->internal, internal_plen); 77 8 : ipv6_prefix_zero (&b->external, external_plen); 78 11 : vec_validate_init_empty (nm->interface_by_sw_if_index, sw_if_index, ~0); 79 8 : nm->interface_by_sw_if_index[sw_if_index] = b - nm->bindings; 80 : 81 8 : uword delta = 0; 82 8 : delta = ip_csum_add_even (delta, b->external.as_u64[0]); 83 8 : delta = ip_csum_add_even (delta, b->external.as_u64[1]); 84 8 : delta = ip_csum_sub_even (delta, b->internal.as_u64[0]); 85 8 : delta = ip_csum_sub_even (delta, b->internal.as_u64[1]); 86 8 : delta = ip_csum_fold (delta); 87 8 : b->delta = delta; 88 : 89 8 : if (configure_feature) 90 8 : rv = npt66_feature_enable_disable (sw_if_index, is_add); 91 : } 92 : else 93 : { 94 : /* Delete a binding entry */ 95 8 : npt66_binding_t *b = npt66_interface_by_sw_if_index (sw_if_index); 96 8 : if (!b) 97 0 : return VNET_API_ERROR_NO_SUCH_ENTRY; 98 8 : nm->interface_by_sw_if_index[sw_if_index] = ~0; 99 8 : pool_put (nm->bindings, b); 100 8 : rv = npt66_feature_enable_disable (sw_if_index, is_add); 101 : } 102 : 103 16 : 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 41 : npt66_interface_by_sw_if_index (u32 sw_if_index) 112 : { 113 41 : npt66_main_t *nm = &npt66_main; 114 : 115 81 : if (!nm->interface_by_sw_if_index || 116 40 : sw_if_index > (vec_len (nm->interface_by_sw_if_index) - 1)) 117 1 : return 0; 118 40 : u32 index = nm->interface_by_sw_if_index[sw_if_index]; 119 40 : if (index == ~0) 120 7 : return 0; 121 33 : if (pool_is_free_index (nm->bindings, index)) 122 0 : return 0; 123 33 : return pool_elt_at_index (nm->bindings, index); 124 : }