LCOV - code coverage report
Current view: top level - plugins/ioam/ip6 - ioam_cache.h (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 0 322 0.0 %
Date: 2023-10-26 01:39:38 Functions: 0 23 0.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2017 Cisco and/or its affiliates.
       3             :  * Licensed under the Apache License, Version 2.0 (the "License");
       4             :  * you may not use this file except in compliance with the License.
       5             :  * You may obtain a copy of the License at:
       6             :  *
       7             :  *     http://www.apache.org/licenses/LICENSE-2.0
       8             :  *
       9             :  * Unless required by applicable law or agreed to in writing, software
      10             :  * distributed under the License is distributed on an "AS IS" BASIS,
      11             :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12             :  * See the License for the specific language governing permissions and
      13             :  * limitations under the License.
      14             :  */
      15             : #ifndef __included_ioam_cache_h__
      16             : #define __included_ioam_cache_h__
      17             : 
      18             : #include <vnet/vnet.h>
      19             : #include <vnet/ip/ip.h>
      20             : #include <vnet/ip/ip_packet.h>
      21             : #include <vnet/ip/ip4_packet.h>
      22             : #include <vnet/ip/ip6_packet.h>
      23             : #include <vnet/srv6/sr.h>
      24             : 
      25             : #include <vppinfra/pool.h>
      26             : #include <vppinfra/hash.h>
      27             : #include <vppinfra/error.h>
      28             : #include <vppinfra/elog.h>
      29             : #include <vppinfra/bihash_8_8.h>
      30             : #include <ioam/analyse/ip6/ip6_ioam_analyse.h>
      31             : #include <vppinfra/tw_timer_16t_2w_512sl.h>
      32             : /*
      33             :  * ioam_cache.h
      34             :  * This header contains routines for caching of ioam header and
      35             :  * buffer:
      36             :  * 1 - On application facing node: to cache ioam header recvd
      37             :  *     in request and reattach in response to provide round
      38             :  *     trip path visibility. Since request response matching
      39             :  *     is needed works with TCP and relies on (5 tuples,seq no)
      40             :  * 2 - On M-Anycast server node: This node replicates requests
      41             :  *    towards multiple anycast service nodes serving anycast
      42             :  *    IP6 address. It evaluates response and forwards the best
      43             :  *    response towards the client of requesting the service.
      44             :  *    Again since request-response matching is needed, works
      45             :  *    with TCP  and relies on (5 tuples,seq no) for matching.
      46             :  *    To do this it caches SYN-ACK responses for a short time to
      47             :  *    evaluate multiple responses received before the selected
      48             :  *    SYN-ACK response is forwared and others dropped.
      49             :  *
      50             :  * M-Anycast server cache:
      51             :  *   - There is a pool of cache entries per worker thread.
      52             :  *   - Cache entry is created when SYN is received expected
      53             :  *     number of responses are marked based on number of
      54             :  *     SR tunnels for the anycast destination address
      55             :  *   - The pool/thread id and pool index are attached in the
      56             :  *    message as an ioam option for quick look up.
      57             :  *   - When is received SYN-ACK the ioam option containing
      58             :  *     thread id + pool index of the cache entry is used to
      59             :  *     look up cache entry.
      60             :  *   - Cache synchronization:
      61             :  *      - This is achieved by cache entry add/del/update all handled
      62             :  *        by the same worker/main thread
      63             :  *      - Packets from client to threads - syn packets, can be disctributed
      64             :  *        based on incoming interface affinity to the cpu core pinned to
      65             :  *        the thread or a simple sequence number based distribution
      66             :  *        if thread per interface is not scaling
      67             :  *      - Response packets from server towards clients - syn-acks, are
      68             :  *        forced to the same thread that created the cache entry
      69             :  *        using SR and the destination of SR v6 address assigned
      70             :  *        to the core/thread. This adderss is sent as an ioam option
      71             :  *        in the syn that can be then used on the other side to
      72             :  *        populate v6 dst address in the response
      73             :  *      - Timeout: timer wheel per thread is used to track the syn-ack wait
      74             :  *        time. The timer wheel tick is updated via an input node per thread.
      75             :  *
      76             :  * Application facing node/Service side cache:
      77             :  *  - Single pool of cache entries.
      78             :  *  - Cache entry is created when SYN is received. Caches the ioam
      79             :  *    header. Hash table entry is created based on 5 tuple and
      80             :  *    TCP seq no to pool index
      81             :  *  - Response SYN-ACK processed by looking up pool index in hash table
      82             :  *    and cache entry in the pool is used to get the ioam header rewrite
      83             :  *    string. Entry is freed from pool and hash table after use.
      84             :  *  - Locking/Synchronization: Currently this functionality is deployed
      85             :  *    with main/single thread only. Hence no locking is used.
      86             :  *  - Deployment: A VPP node per application server servicing anycast
      87             :  *    address is expected. Locking/synchronization needed when the server
      88             :  *    /application facing node is started with multiple worker threads.
      89             :  *
      90             :  */
      91             : 
      92             : /*
      93             :  * Application facing server side caching:
      94             :  * Cache entry for ioam header
      95             :  * Currently caters to TCP and relies on
      96             :  * TCP - 5 tuples + seqno to cache and reinsert
      97             :  * ioam header b/n TCP request response
      98             :  */
      99             : typedef struct
     100             : {
     101             :   /** Required for pool_get_aligned */
     102             :   CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
     103             :   ip6_address_t src_address;
     104             :   ip6_address_t dst_address;
     105             :   u16 src_port;
     106             :   u16 dst_port;
     107             :   u8 protocol;
     108             :   u32 seq_no;
     109             :   ip6_address_t next_hop;
     110             :   u16 my_address_offset;
     111             :   u8 *ioam_rewrite_string;
     112             : } ioam_cache_entry_t;
     113             : 
     114             : /*
     115             :  * Cache entry for anycast server selection
     116             :  * Works for TCP as 5 tuple + sequence number
     117             :  * is required for request response matching
     118             :  * max_responses expected is set based on number
     119             :  *              of SR tunnels for the dst_address
     120             :  * Timeout or all response_received = max_responses
     121             :  *            will clear the entry
     122             :  * buffer_index index of the response msg vlib buffer
     123             :  *           that is currently the best response
     124             :  */
     125             : typedef struct
     126             : {
     127             :   /** Required for pool_get_aligned */
     128             :   CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
     129             :   u32 pool_id;
     130             :   u32 pool_index;
     131             :   ip6_address_t src_address;
     132             :   ip6_address_t dst_address;
     133             :   u16 src_port;
     134             :   u16 dst_port;
     135             :   u8 protocol;
     136             :   u32 seq_no;
     137             :   u32 buffer_index;
     138             :   ip6_hop_by_hop_header_t *hbh; //pointer to hbh header in the buffer
     139             :   u64 created_at;
     140             :   u8 response_received;
     141             :   u8 max_responses;
     142             :   u32 stop_timer_handle;
     143             :   /** Handle returned from tw_start_timer */
     144             :   u32 timer_handle;
     145             :   /** entry should expire at this clock tick */
     146             :   u32 expected_to_expire;
     147             : } ioam_cache_ts_entry_t;
     148             : 
     149             : /*
     150             :  * Per thread tunnel selection cache stats
     151             :  */
     152             : typedef struct
     153             : {
     154             :   u64 inuse;
     155             :   u64 add_failed;
     156             : } ioam_cache_ts_pool_stats_t;
     157             : 
     158             : /* Server side: iOAM header caching */
     159             : #define MAX_CACHE_ENTRIES 4096
     160             : /* M-Anycast: Cache for SR tunnel selection */
     161             : #define MAX_CACHE_TS_ENTRIES 1048576
     162             : 
     163             : #define IOAM_CACHE_TABLE_DEFAULT_HASH_NUM_BUCKETS (4 * 1024)
     164             : #define IOAM_CACHE_TABLE_DEFAULT_HASH_MEMORY_SIZE (2<<20)
     165             : 
     166             : typedef struct
     167             : {
     168             :   /* API message ID base */
     169             :   u16 msg_id_base;
     170             : 
     171             :   /* Pool of ioam_cache_buffer_t */
     172             :   ioam_cache_entry_t *ioam_rewrite_pool;
     173             : 
     174             :   /* For steering packets ioam cache entry is followed by
     175             :    * SR header. This is the SR rewrite template */
     176             :   u8 *sr_rewrite_template;
     177             :   /* The current rewrite string being used */
     178             :   u8 *rewrite;
     179             :   u8 rewrite_pool_index_offset;
     180             :   ip6_address_t sr_localsid_cache;
     181             : 
     182             :   u64 lookup_table_nbuckets;
     183             :   u64 lookup_table_size;
     184             :   clib_bihash_8_8_t ioam_rewrite_cache_table;
     185             : 
     186             :   /* M-Anycast: Pool of ioam_cache_ts_entry_t per thread */
     187             :   ioam_cache_ts_entry_t **ioam_ts_pool;
     188             :   ioam_cache_ts_pool_stats_t *ts_stats;
     189             :   /** per thread single-wheel */
     190             :   tw_timer_wheel_16t_2w_512sl_t *timer_wheels;
     191             : 
     192             :   /*
     193             :    * Selection criteria: oneway delay: Server to M-Anycast
     194             :    * or RTT
     195             :    */
     196             :   bool criteria_oneway;
     197             :   u8 wait_for_responses;
     198             :   ip6_address_t sr_localsid_ts;
     199             : 
     200             :   /* convenience */
     201             :   vlib_main_t *vlib_main;
     202             : 
     203             :   uword cache_hbh_slot;
     204             :   uword ts_hbh_slot;
     205             :   u32 ip6_hbh_pop_node_index;
     206             :   u32 error_node_index;
     207             :   u32 cleanup_process_node_index;
     208             : 
     209             :   u32 ip6_add_from_cache_hbh_node_index;
     210             :   u32 ip6_reset_ts_hbh_node_index;
     211             : } ioam_cache_main_t;
     212             : 
     213             : extern ioam_cache_main_t ioam_cache_main;
     214             : 
     215             : extern vlib_node_registration_t ioam_cache_node;
     216             : extern vlib_node_registration_t ioam_cache_ts_node;
     217             : 
     218             : /*  Compute flow hash.  We'll use it to select which Sponge to use for this
     219             :  *  flow.  And other things.
     220             :  *  ip6_compute_flow_hash in ip6.h doesnt locate tcp/udp when
     221             :  *  ext headers are present. While it could be made to it will be a
     222             :  *  performance hit for ECMP flows.
     223             :  *  HEnce this function here, with L4 information directly input
     224             :  *  Useful when tcp/udp headers are already located in presence of
     225             :  *  ext headers
     226             :  */
     227             : always_inline u32
     228           0 : ip6_compute_flow_hash_ext (const ip6_header_t * ip,
     229             :                            u8 protocol,
     230             :                            u16 src_port,
     231             :                            u16 dst_port, flow_hash_config_t flow_hash_config)
     232             : {
     233             :   u64 a, b, c;
     234             :   u64 t1, t2;
     235             : 
     236           0 :   t1 = (ip->src_address.as_u64[0] ^ ip->src_address.as_u64[1]);
     237           0 :   t1 = (flow_hash_config & IP_FLOW_HASH_SRC_ADDR) ? t1 : 0;
     238             : 
     239           0 :   t2 = (ip->dst_address.as_u64[0] ^ ip->dst_address.as_u64[1]);
     240           0 :   t2 = (flow_hash_config & IP_FLOW_HASH_DST_ADDR) ? t2 : 0;
     241             : 
     242           0 :   a = (flow_hash_config & IP_FLOW_HASH_REVERSE_SRC_DST) ? t2 : t1;
     243           0 :   b = (flow_hash_config & IP_FLOW_HASH_REVERSE_SRC_DST) ? t1 : t2;
     244           0 :   b ^= (flow_hash_config & IP_FLOW_HASH_PROTO) ? protocol : 0;
     245             : 
     246           0 :   t1 = src_port;
     247           0 :   t2 = dst_port;
     248             : 
     249           0 :   t1 = (flow_hash_config & IP_FLOW_HASH_SRC_PORT) ? t1 : 0;
     250           0 :   t2 = (flow_hash_config & IP_FLOW_HASH_DST_PORT) ? t2 : 0;
     251             : 
     252           0 :   c = (flow_hash_config & IP_FLOW_HASH_REVERSE_SRC_DST) ?
     253           0 :     ((t1 << 16) | t2) : ((t2 << 16) | t1);
     254             : 
     255           0 :   hash_mix64 (a, b, c);
     256           0 :   return (u32) c;
     257             : }
     258             : 
     259             : 
     260             : /* 2 new ioam E2E options :
     261             :  * 1. HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE_ID: IP6 address
     262             :  *                of ioam node that inserted ioam header
     263             :  * 2. HBH_OPTION_TYPE_IOAM_E2E_CACHE_ID: Pool id and index
     264             :  *                   to look up tunnel select cache entry
     265             :  */
     266             : #define HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE_ID 30
     267             : #define HBH_OPTION_TYPE_IOAM_E2E_CACHE_ID 31
     268             : 
     269             : typedef CLIB_PACKED (struct
     270             :                      {
     271             :                      ip6_hop_by_hop_option_t hdr; u8 e2e_type; u8 reserved[5];
     272             :                      ip6_address_t id;
     273             :                      }) ioam_e2e_id_option_t;
     274             : 
     275             : typedef CLIB_PACKED (struct
     276             :                      {
     277             :                      ip6_hop_by_hop_option_t hdr; u8 e2e_type; u8 pool_id;
     278             :                      u32 pool_index;
     279             :                      }) ioam_e2e_cache_option_t;
     280             : 
     281             : #define IOAM_E2E_ID_OPTION_RND ((sizeof(ioam_e2e_id_option_t) + 7) & ~7)
     282             : #define IOAM_E2E_ID_HBH_EXT_LEN (IOAM_E2E_ID_OPTION_RND >> 3)
     283             : #define IOAM_E2E_CACHE_OPTION_RND ((sizeof(ioam_e2e_cache_option_t) + 7) & ~7)
     284             : #define IOAM_E2E_CACHE_HBH_EXT_LEN (IOAM_E2E_CACHE_OPTION_RND >> 3)
     285             : 
     286             : static inline void
     287           0 : ioam_e2e_id_rewrite_handler (ioam_e2e_id_option_t * e2e_option,
     288             :                              ip6_address_t * address)
     289             : {
     290           0 :   e2e_option->id.as_u64[0] = address->as_u64[0];
     291           0 :   e2e_option->id.as_u64[1] = address->as_u64[1];
     292             : 
     293           0 : }
     294             : 
     295             : /* Following functions are for the caching of ioam header
     296             :  * to enable reattaching it for a complete request-response
     297             :  * message exchange */
     298             : inline static void
     299           0 : ioam_cache_entry_free (ioam_cache_entry_t * entry)
     300             : {
     301           0 :   ioam_cache_main_t *cm = &ioam_cache_main;
     302           0 :   if (entry)
     303             :     {
     304           0 :       vec_free (entry->ioam_rewrite_string);
     305           0 :       clib_memset (entry, 0, sizeof (*entry));
     306           0 :       pool_put (cm->ioam_rewrite_pool, entry);
     307             :     }
     308           0 : }
     309             : 
     310             : inline static ioam_cache_entry_t *
     311           0 : ioam_cache_entry_cleanup (u32 pool_index)
     312             : {
     313           0 :   ioam_cache_main_t *cm = &ioam_cache_main;
     314           0 :   ioam_cache_entry_t *entry = 0;
     315             : 
     316           0 :   entry = pool_elt_at_index (cm->ioam_rewrite_pool, pool_index);
     317           0 :   ioam_cache_entry_free (entry);
     318           0 :   return (0);
     319             : }
     320             : 
     321             : inline static ioam_cache_entry_t *
     322           0 : ioam_cache_lookup (ip6_header_t * ip0, u16 src_port, u16 dst_port, u32 seq_no)
     323             : {
     324           0 :   ioam_cache_main_t *cm = &ioam_cache_main;
     325           0 :   u32 flow_hash = ip6_compute_flow_hash_ext (ip0, ip0->protocol,
     326             :                                              src_port, dst_port,
     327             :                                              IP_FLOW_HASH_DEFAULT |
     328             :                                              IP_FLOW_HASH_REVERSE_SRC_DST);
     329             :   clib_bihash_kv_8_8_t kv, value;
     330             : 
     331           0 :   kv.key = (u64) flow_hash << 32 | seq_no;
     332           0 :   kv.value = 0;
     333           0 :   value.key = 0;
     334           0 :   value.value = 0;
     335             : 
     336           0 :   if (clib_bihash_search_8_8 (&cm->ioam_rewrite_cache_table, &kv, &value) >=
     337             :       0)
     338             :     {
     339           0 :       ioam_cache_entry_t *entry = 0;
     340             : 
     341           0 :       entry = pool_elt_at_index (cm->ioam_rewrite_pool, value.value);
     342             :       /* match */
     343           0 :       if (ip6_address_compare (&ip0->src_address, &entry->dst_address) == 0 &&
     344           0 :           ip6_address_compare (&ip0->dst_address, &entry->src_address) == 0 &&
     345           0 :           entry->src_port == dst_port &&
     346           0 :           entry->dst_port == src_port && entry->seq_no == seq_no)
     347             :         {
     348             :           /* If lookup is successful remove it from the hash */
     349           0 :           clib_bihash_add_del_8_8 (&cm->ioam_rewrite_cache_table, &kv, 0);
     350           0 :           return (entry);
     351             :         }
     352             :       else
     353           0 :         return (0);
     354             : 
     355             :     }
     356           0 :   return (0);
     357             : }
     358             : 
     359             : /*
     360             :  * Caches ioam hbh header
     361             :  * Extends the hbh header with option to contain IP6 address of the node
     362             :  * that caches it
     363             :  */
     364             : inline static int
     365           0 : ioam_cache_add (vlib_buffer_t * b0,
     366             :                 ip6_header_t * ip0,
     367             :                 u16 src_port,
     368             :                 u16 dst_port, ip6_hop_by_hop_header_t * hbh0, u32 seq_no)
     369             : {
     370           0 :   ioam_cache_main_t *cm = &ioam_cache_main;
     371           0 :   ioam_cache_entry_t *entry = 0;
     372           0 :   u32 rewrite_len = 0, e2e_id_offset = 0;
     373           0 :   u32 pool_index = 0;
     374           0 :   ioam_e2e_id_option_t *e2e = 0;
     375             : 
     376           0 :   pool_get_aligned (cm->ioam_rewrite_pool, entry, CLIB_CACHE_LINE_BYTES);
     377           0 :   clib_memset (entry, 0, sizeof (*entry));
     378           0 :   pool_index = entry - cm->ioam_rewrite_pool;
     379             : 
     380           0 :   clib_memcpy_fast (entry->dst_address.as_u64, ip0->dst_address.as_u64,
     381             :                     sizeof (ip6_address_t));
     382           0 :   clib_memcpy_fast (entry->src_address.as_u64, ip0->src_address.as_u64,
     383             :                     sizeof (ip6_address_t));
     384           0 :   entry->src_port = src_port;
     385           0 :   entry->dst_port = dst_port;
     386           0 :   entry->seq_no = seq_no;
     387           0 :   rewrite_len = ((hbh0->length + 1) << 3);
     388           0 :   vec_validate (entry->ioam_rewrite_string, rewrite_len - 1);
     389           0 :   e2e = ip6_ioam_find_hbh_option (hbh0, HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE_ID);
     390           0 :   if (e2e)
     391             :     {
     392           0 :       entry->next_hop.as_u64[0] = e2e->id.as_u64[0];
     393           0 :       entry->next_hop.as_u64[1] = e2e->id.as_u64[1];
     394             :     }
     395             :   else
     396             :     {
     397           0 :       return (-1);
     398             :     }
     399           0 :   e2e_id_offset = (u8 *) e2e - (u8 *) hbh0;
     400             :   /* setup e2e id option to insert v6 address of the node caching it */
     401           0 :   clib_memcpy_fast (entry->ioam_rewrite_string, hbh0, rewrite_len);
     402           0 :   hbh0 = (ip6_hop_by_hop_header_t *) entry->ioam_rewrite_string;
     403             : 
     404             :   /* suffix rewrite string with e2e ID option */
     405           0 :   e2e = (ioam_e2e_id_option_t *) (entry->ioam_rewrite_string + e2e_id_offset);
     406           0 :   ioam_e2e_id_rewrite_handler (e2e, &cm->sr_localsid_cache);
     407           0 :   entry->my_address_offset = (u8 *) (&e2e->id) - (u8 *) hbh0;
     408             : 
     409             :   /* add it to hash, replacing and freeing any collision for now */
     410             :   u32 flow_hash =
     411           0 :     ip6_compute_flow_hash_ext (ip0, hbh0->protocol, src_port, dst_port,
     412             :                                IP_FLOW_HASH_DEFAULT);
     413             :   clib_bihash_kv_8_8_t kv, value;
     414           0 :   kv.key = (u64) flow_hash << 32 | seq_no;
     415           0 :   kv.value = 0;
     416           0 :   if (clib_bihash_search_8_8 (&cm->ioam_rewrite_cache_table, &kv, &value) >=
     417             :       0)
     418             :     {
     419             :       /* replace */
     420           0 :       ioam_cache_entry_cleanup (value.value);
     421             :     }
     422           0 :   kv.value = pool_index;
     423           0 :   clib_bihash_add_del_8_8 (&cm->ioam_rewrite_cache_table, &kv, 1);
     424           0 :   return (0);
     425             : }
     426             : 
     427             : /* Creates SR rewrite string
     428             :  * This is appended with ioam header on the server facing
     429             :  * node.
     430             :  * This SR header is necessary to attract packets towards
     431             :  * selected Anycast server.
     432             :  */
     433             : inline static void
     434           0 : ioam_cache_sr_rewrite_template_create (void)
     435             : {
     436           0 :   ioam_cache_main_t *cm = &ioam_cache_main;
     437           0 :   ip6_address_t *segments = 0;
     438           0 :   ip6_address_t *this_seg = 0;
     439             : 
     440             :   /* This nodes address and the original dest will be
     441             :    * filled when the packet is processed */
     442           0 :   vec_add2 (segments, this_seg, 1);
     443           0 :   clib_memset (this_seg, 0xfe, sizeof (ip6_address_t));
     444           0 :   cm->sr_rewrite_template = ip6_sr_compute_rewrite_string_insert (segments);
     445           0 :   vec_free (segments);
     446           0 : }
     447             : 
     448             : inline static int
     449           0 : ioam_cache_table_init (vlib_main_t * vm)
     450             : {
     451           0 :   ioam_cache_main_t *cm = &ioam_cache_main;
     452             : 
     453           0 :   pool_alloc_aligned (cm->ioam_rewrite_pool,
     454             :                       MAX_CACHE_ENTRIES, CLIB_CACHE_LINE_BYTES);
     455           0 :   cm->lookup_table_nbuckets = IOAM_CACHE_TABLE_DEFAULT_HASH_NUM_BUCKETS;
     456           0 :   cm->lookup_table_nbuckets = 1 << max_log2 (cm->lookup_table_nbuckets);
     457           0 :   cm->lookup_table_size = IOAM_CACHE_TABLE_DEFAULT_HASH_MEMORY_SIZE;
     458             : 
     459           0 :   clib_bihash_init_8_8 (&cm->ioam_rewrite_cache_table,
     460             :                         "ioam rewrite cache table",
     461           0 :                         cm->lookup_table_nbuckets, cm->lookup_table_size);
     462             :   /* Create SR rewrite template */
     463           0 :   ioam_cache_sr_rewrite_template_create ();
     464           0 :   return (1);
     465             : }
     466             : 
     467             : inline static int
     468           0 : ioam_cache_table_destroy (vlib_main_t * vm)
     469             : {
     470           0 :   ioam_cache_main_t *cm = &ioam_cache_main;
     471           0 :   ioam_cache_entry_t *entry = 0;
     472             :   /* free pool and hash table */
     473           0 :   clib_bihash_free_8_8 (&cm->ioam_rewrite_cache_table);
     474           0 :   pool_foreach (entry, cm->ioam_rewrite_pool)
     475             :   {
     476           0 :     ioam_cache_entry_free (entry);
     477             :   }
     478           0 :   pool_free (cm->ioam_rewrite_pool);
     479           0 :   cm->ioam_rewrite_pool = 0;
     480           0 :   vec_free (cm->sr_rewrite_template);
     481           0 :   cm->sr_rewrite_template = 0;
     482           0 :   return (0);
     483             : }
     484             : 
     485             : inline static u8 *
     486           0 : format_ioam_cache_entry (u8 * s, va_list * args)
     487             : {
     488           0 :   ioam_cache_entry_t *e = va_arg (*args, ioam_cache_entry_t *);
     489           0 :   ioam_cache_main_t *cm = &ioam_cache_main;
     490           0 :   int rewrite_len = vec_len (e->ioam_rewrite_string);
     491             : 
     492           0 :   s = format (s, "%d: %U:%d to  %U:%d seq_no %lu\n",
     493           0 :               (e - cm->ioam_rewrite_pool),
     494             :               format_ip6_address, &e->src_address,
     495           0 :               e->src_port,
     496           0 :               format_ip6_address, &e->dst_address, e->dst_port, e->seq_no);
     497             : 
     498           0 :   if (rewrite_len)
     499             :     {
     500           0 :       s = format (s, "  %U",
     501             :                   format_ip6_hop_by_hop_ext_hdr,
     502           0 :                   (ip6_hop_by_hop_header_t *) e->ioam_rewrite_string,
     503             :                   rewrite_len - 1);
     504             :     }
     505           0 :   return s;
     506             : }
     507             : 
     508             : void ioam_cache_ts_timer_node_enable (vlib_main_t * vm, u8 enable);
     509             : 
     510             : #define IOAM_CACHE_TS_TIMEOUT 1.0       //SYN timeout 1 sec
     511             : #define IOAM_CACHE_TS_TICK 100e-3
     512             : /* Timer delays as multiples of 100ms */
     513             : #define IOAM_CACHE_TS_TIMEOUT_TICKS IOAM_CACHE_TS_TICK*9
     514             : #define TIMER_HANDLE_INVALID ((u32) ~0)
     515             : 
     516             : 
     517             : void expired_cache_ts_timer_callback (u32 * expired_timers);
     518             : 
     519             : /*
     520             :  * Following functions are to manage M-Anycast server selection
     521             :  * cache
     522             :  * There is a per worker thread pool to create a cache entry
     523             :  * for a TCP SYN received. TCP SYN-ACK contians ioam header
     524             :  * with HBH_OPTION_TYPE_IOAM_E2E_CACHE_ID option to point to the
     525             :  * entry.
     526             :  */
     527             : inline static int
     528           0 : ioam_cache_ts_table_init (vlib_main_t * vm)
     529             : {
     530           0 :   ioam_cache_main_t *cm = &ioam_cache_main;
     531           0 :   int no_of_threads = vec_len (vlib_worker_threads);
     532             :   int i;
     533             : 
     534           0 :   vec_validate_aligned (cm->ioam_ts_pool, no_of_threads - 1,
     535             :                         CLIB_CACHE_LINE_BYTES);
     536           0 :   vec_validate_aligned (cm->ts_stats, no_of_threads - 1,
     537             :                         CLIB_CACHE_LINE_BYTES);
     538           0 :   vec_validate (cm->timer_wheels, no_of_threads - 1);
     539           0 :   cm->lookup_table_nbuckets = IOAM_CACHE_TABLE_DEFAULT_HASH_NUM_BUCKETS;
     540           0 :   cm->lookup_table_nbuckets = 1 << max_log2 (cm->lookup_table_nbuckets);
     541           0 :   cm->lookup_table_size = IOAM_CACHE_TABLE_DEFAULT_HASH_MEMORY_SIZE;
     542           0 :   for (i = 0; i < no_of_threads; i++)
     543             :     {
     544           0 :       pool_alloc_aligned (cm->ioam_ts_pool[i],
     545             :                           MAX_CACHE_TS_ENTRIES, CLIB_CACHE_LINE_BYTES);
     546           0 :       clib_memset (&cm->ts_stats[i], 0, sizeof (ioam_cache_ts_pool_stats_t));
     547           0 :       tw_timer_wheel_init_16t_2w_512sl (&cm->timer_wheels[i],
     548             :                                         expired_cache_ts_timer_callback,
     549             :                                         IOAM_CACHE_TS_TICK
     550             :                                         /* timer period 100ms */ ,
     551             :                                         10e4);
     552           0 :       cm->timer_wheels[i].last_run_time = vlib_time_now (vm);
     553             :     }
     554           0 :   ioam_cache_ts_timer_node_enable (vm, 1);
     555           0 :   return (1);
     556             : }
     557             : 
     558             : always_inline void
     559           0 : ioam_cache_ts_timer_set (ioam_cache_main_t * cm,
     560             :                          ioam_cache_ts_entry_t * entry, u32 interval)
     561             : {
     562             :   entry->timer_handle
     563           0 :     = tw_timer_start_16t_2w_512sl (&cm->timer_wheels[entry->pool_id],
     564             :                                    entry->pool_index, 1, interval);
     565           0 : }
     566             : 
     567             : always_inline void
     568           0 : ioam_cache_ts_timer_reset (ioam_cache_main_t * cm,
     569             :                            ioam_cache_ts_entry_t * entry)
     570             : {
     571           0 :   tw_timer_stop_16t_2w_512sl (&cm->timer_wheels[entry->pool_id],
     572             :                               entry->timer_handle);
     573           0 :   entry->timer_handle = TIMER_HANDLE_INVALID;
     574           0 : }
     575             : 
     576             : inline static void
     577           0 : ioam_cache_ts_entry_free (u32 thread_id,
     578             :                           ioam_cache_ts_entry_t * entry, u32 node_index)
     579             : {
     580           0 :   ioam_cache_main_t *cm = &ioam_cache_main;
     581           0 :   vlib_main_t *vm = cm->vlib_main;
     582           0 :   vlib_frame_t *nf = 0;
     583             :   u32 *to_next;
     584             : 
     585           0 :   if (entry)
     586             :     {
     587           0 :       if (entry->hbh != 0)
     588             :         {
     589           0 :           nf = vlib_get_frame_to_node (vm, node_index);
     590           0 :           nf->n_vectors = 0;
     591           0 :           to_next = vlib_frame_vector_args (nf);
     592           0 :           nf->n_vectors = 1;
     593           0 :           to_next[0] = entry->buffer_index;
     594           0 :           vlib_put_frame_to_node (vm, node_index, nf);
     595             :         }
     596           0 :       pool_put (cm->ioam_ts_pool[thread_id], entry);
     597           0 :       cm->ts_stats[thread_id].inuse--;
     598           0 :       clib_memset (entry, 0, sizeof (*entry));
     599             :     }
     600           0 : }
     601             : 
     602             : inline static int
     603           0 : ioam_cache_ts_table_destroy (vlib_main_t * vm)
     604             : {
     605           0 :   ioam_cache_main_t *cm = &ioam_cache_main;
     606           0 :   ioam_cache_ts_entry_t *entry = 0;
     607           0 :   int no_of_threads = vec_len (vlib_worker_threads);
     608             :   int i;
     609             : 
     610             :   /* free pool and hash table */
     611           0 :   if (cm->ioam_ts_pool)
     612             :     {
     613           0 :       for (i = 0; i < no_of_threads; i++)
     614             :         {
     615           0 :           pool_foreach (entry, cm->ioam_ts_pool[i])
     616             :             {
     617           0 :               ioam_cache_ts_entry_free (i, entry, cm->error_node_index);
     618             :             }
     619           0 :           pool_free (cm->ioam_ts_pool[i]);
     620           0 :           cm->ioam_ts_pool[i] = 0;
     621           0 :           tw_timer_wheel_free_16t_2w_512sl (&cm->timer_wheels[i]);
     622             :         }
     623           0 :       vec_free (cm->ioam_ts_pool);
     624             :     }
     625           0 :   return (0);
     626             : }
     627             : 
     628             : inline static int
     629             : ioam_cache_ts_entry_cleanup (u32 thread_id, u32 pool_index)
     630             : {
     631             :   ioam_cache_main_t *cm = &ioam_cache_main;
     632             :   ioam_cache_ts_entry_t *entry = 0;
     633             : 
     634             :   entry = pool_elt_at_index (cm->ioam_ts_pool[thread_id], pool_index);
     635             :   ioam_cache_ts_entry_free (thread_id, entry, cm->error_node_index);
     636             :   return (0);
     637             : }
     638             : 
     639             : /*
     640             :  * Caches buffer for ioam SR tunnel select for Anycast service
     641             :  */
     642             : inline static int
     643           0 : ioam_cache_ts_add (ip6_header_t * ip0,
     644             :                    u16 src_port,
     645             :                    u16 dst_port,
     646             :                    u32 seq_no,
     647             :                    u8 max_responses, u64 now, u32 thread_id, u32 * pool_index)
     648             : {
     649           0 :   ioam_cache_main_t *cm = &ioam_cache_main;
     650           0 :   ioam_cache_ts_entry_t *entry = 0;
     651             : 
     652           0 :   if (cm->ts_stats[thread_id].inuse == MAX_CACHE_TS_ENTRIES)
     653             :     {
     654           0 :       cm->ts_stats[thread_id].add_failed++;
     655           0 :       return (-1);
     656             :     }
     657             : 
     658           0 :   pool_get_aligned (cm->ioam_ts_pool[thread_id], entry,
     659             :                     CLIB_CACHE_LINE_BYTES);
     660           0 :   clib_memset (entry, 0, sizeof (*entry));
     661           0 :   *pool_index = entry - cm->ioam_ts_pool[thread_id];
     662             : 
     663           0 :   clib_memcpy_fast (entry->dst_address.as_u64, ip0->dst_address.as_u64,
     664             :                     sizeof (ip6_address_t));
     665           0 :   clib_memcpy_fast (entry->src_address.as_u64, ip0->src_address.as_u64,
     666             :                     sizeof (ip6_address_t));
     667           0 :   entry->src_port = src_port;
     668           0 :   entry->dst_port = dst_port;
     669           0 :   entry->seq_no = seq_no;
     670           0 :   entry->response_received = 0;
     671           0 :   entry->max_responses = max_responses;
     672           0 :   entry->created_at = now;
     673           0 :   entry->hbh = 0;
     674           0 :   entry->buffer_index = 0;
     675           0 :   entry->pool_id = thread_id;
     676           0 :   entry->pool_index = *pool_index;
     677           0 :   ioam_cache_ts_timer_set (cm, entry, IOAM_CACHE_TS_TIMEOUT);
     678           0 :   cm->ts_stats[thread_id].inuse++;
     679           0 :   return (0);
     680             : }
     681             : 
     682             : inline static void
     683           0 : ioam_cache_ts_send (u32 thread_id, i32 pool_index)
     684             : {
     685           0 :   ioam_cache_main_t *cm = &ioam_cache_main;
     686           0 :   ioam_cache_ts_entry_t *entry = 0;
     687             : 
     688           0 :   entry = pool_elt_at_index (cm->ioam_ts_pool[thread_id], pool_index);
     689           0 :   if (!pool_is_free (cm->ioam_ts_pool[thread_id], entry) && entry)
     690             :     {
     691             :       /* send and free pool entry */
     692           0 :       ioam_cache_ts_entry_free (thread_id, entry, cm->ip6_hbh_pop_node_index);
     693             :     }
     694           0 : }
     695             : 
     696             : inline static void
     697           0 : ioam_cache_ts_check_and_send (u32 thread_id, i32 pool_index)
     698             : {
     699           0 :   ioam_cache_main_t *cm = &ioam_cache_main;
     700           0 :   ioam_cache_ts_entry_t *entry = 0;
     701           0 :   entry = pool_elt_at_index (cm->ioam_ts_pool[thread_id], pool_index);
     702           0 :   if (entry && entry->hbh)
     703             :     {
     704           0 :       if (entry->response_received == entry->max_responses ||
     705           0 :           entry->created_at + IOAM_CACHE_TS_TIMEOUT <=
     706           0 :           vlib_time_now (cm->vlib_main))
     707             :         {
     708           0 :           ioam_cache_ts_timer_reset (cm, entry);
     709           0 :           ioam_cache_ts_send (thread_id, pool_index);
     710             :         }
     711             :     }
     712           0 : }
     713             : 
     714             : inline static int
     715           0 : ioam_cache_ts_update (u32 thread_id,
     716             :                       i32 pool_index,
     717             :                       u32 buffer_index, ip6_hop_by_hop_header_t * hbh)
     718             : {
     719           0 :   ioam_cache_main_t *cm = &ioam_cache_main;
     720           0 :   ioam_cache_ts_entry_t *entry = 0;
     721           0 :   vlib_main_t *vm = cm->vlib_main;
     722           0 :   vlib_frame_t *nf = 0;
     723             :   u32 *to_next;
     724             : 
     725           0 :   entry = pool_elt_at_index (cm->ioam_ts_pool[thread_id], pool_index);
     726           0 :   if (!pool_is_free (cm->ioam_ts_pool[thread_id], entry) && entry)
     727             :     {
     728             :       /* drop existing buffer */
     729           0 :       if (entry->hbh != 0)
     730             :         {
     731           0 :           nf = vlib_get_frame_to_node (vm, cm->error_node_index);
     732           0 :           nf->n_vectors = 0;
     733           0 :           to_next = vlib_frame_vector_args (nf);
     734           0 :           nf->n_vectors = 1;
     735           0 :           to_next[0] = entry->buffer_index;
     736           0 :           vlib_put_frame_to_node (vm, cm->error_node_index, nf);
     737             :         }
     738             :       /* update */
     739           0 :       entry->buffer_index = buffer_index;
     740           0 :       entry->hbh = hbh;
     741             :       /* check and send */
     742           0 :       ioam_cache_ts_check_and_send (thread_id, pool_index);
     743           0 :       return (0);
     744             :     }
     745           0 :   return (-1);
     746             : }
     747             : 
     748             : /*
     749             :  * looks up the entry based on the e2e option pool index
     750             :  * result = 0 found the entry
     751             :  * result < 0 indicates failture to find an entry
     752             :  */
     753             : inline static int
     754           0 : ioam_cache_ts_lookup (ip6_header_t * ip0,
     755             :                       u8 protocol,
     756             :                       u16 src_port,
     757             :                       u16 dst_port,
     758             :                       u32 seq_no,
     759             :                       ip6_hop_by_hop_header_t ** hbh,
     760             :                       u32 * pool_index, u8 * thread_id, u8 response_seen)
     761             : {
     762           0 :   ioam_cache_main_t *cm = &ioam_cache_main;
     763           0 :   ip6_hop_by_hop_header_t *hbh0 = 0;
     764           0 :   ioam_e2e_cache_option_t *e2e = 0;
     765             : 
     766           0 :   hbh0 = (ip6_hop_by_hop_header_t *) (ip0 + 1);
     767           0 :   e2e =
     768           0 :     (ioam_e2e_cache_option_t *) ((u8 *) hbh0 + cm->rewrite_pool_index_offset);
     769           0 :   if ((u8 *) e2e < ((u8 *) hbh0 + ((hbh0->length + 1) << 3))
     770           0 :       && e2e->hdr.type == HBH_OPTION_TYPE_IOAM_E2E_CACHE_ID)
     771             :     {
     772           0 :       ioam_cache_ts_entry_t *entry = 0;
     773           0 :       *pool_index = e2e->pool_index;
     774           0 :       *thread_id = e2e->pool_id;
     775           0 :       entry = pool_elt_at_index (cm->ioam_ts_pool[*thread_id], *pool_index);
     776             :       /* match */
     777           0 :       if (entry &&
     778           0 :           ip6_address_compare (&ip0->src_address, &entry->dst_address) == 0 &&
     779           0 :           ip6_address_compare (&ip0->dst_address, &entry->src_address) == 0 &&
     780           0 :           entry->src_port == dst_port &&
     781           0 :           entry->dst_port == src_port && entry->seq_no == seq_no)
     782             :         {
     783           0 :           *hbh = entry->hbh;
     784           0 :           entry->response_received += response_seen;
     785           0 :           return (0);
     786             :         }
     787           0 :       else if (entry)
     788             :         {
     789           0 :           return (-1);
     790             :         }
     791             :     }
     792           0 :   return (-1);
     793             : }
     794             : 
     795             : inline static u8 *
     796           0 : format_ioam_cache_ts_entry (u8 * s, va_list * args)
     797             : {
     798           0 :   ioam_cache_ts_entry_t *e = va_arg (*args, ioam_cache_ts_entry_t *);
     799           0 :   u32 thread_id = va_arg (*args, u32);
     800           0 :   ioam_cache_main_t *cm = &ioam_cache_main;
     801           0 :   ioam_e2e_id_option_t *e2e = 0;
     802           0 :   vlib_main_t *vm = cm->vlib_main;
     803           0 :   clib_time_t *ct = &vm->clib_time;
     804             : 
     805           0 :   if (!e)
     806           0 :     goto end;
     807             : 
     808           0 :   if (e->hbh)
     809             :     {
     810             :       e2e =
     811           0 :         ip6_ioam_find_hbh_option (e->hbh,
     812             :                                   HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE_ID);
     813             : 
     814             :       s =
     815           0 :         format (s,
     816             :                 "%d: %U:%d to  %U:%d seq_no %u buffer %u %U \n\t\tCreated at %U Received %d\n",
     817           0 :                 (e - cm->ioam_ts_pool[thread_id]), format_ip6_address,
     818           0 :                 &e->src_address, e->src_port, format_ip6_address,
     819           0 :                 &e->dst_address, e->dst_port, e->seq_no, e->buffer_index,
     820             :                 format_ip6_address, e2e ? &e2e->id : 0, format_time_interval,
     821             :                 "h:m:s:u",
     822           0 :                 (e->created_at -
     823           0 :                  vm->cpu_time_main_loop_start) * ct->seconds_per_clock,
     824           0 :                 e->response_received);
     825             :     }
     826             :   else
     827             :     {
     828             :       s =
     829           0 :         format (s,
     830             :                 "%d: %U:%d to  %U:%d seq_no %u Buffer %u \n\t\tCreated at %U Received %d\n",
     831           0 :                 (e - cm->ioam_ts_pool[thread_id]), format_ip6_address,
     832           0 :                 &e->src_address, e->src_port, format_ip6_address,
     833           0 :                 &e->dst_address, e->dst_port, e->seq_no, e->buffer_index,
     834             :                 format_time_interval, "h:m:s:u",
     835           0 :                 (e->created_at -
     836           0 :                  vm->cpu_time_main_loop_start) * ct->seconds_per_clock,
     837           0 :                 e->response_received);
     838             :     }
     839             : 
     840           0 : end:
     841           0 :   return s;
     842             : }
     843             : 
     844             : /*
     845             :  * Get extended rewrite string for iOAM data in v6
     846             :  * This makes space for an e2e options to carry cache pool info
     847             :  * and manycast server address.
     848             :  * It set the rewrite string per configs in ioam ip6 + new option
     849             :  * for cache along with offset to the option to populate cache
     850             :  * pool id and index
     851             :  */
     852             : static inline int
     853           0 : ip6_ioam_ts_cache_set_rewrite (void)
     854             : {
     855           0 :   ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main;
     856           0 :   ioam_cache_main_t *cm = &ioam_cache_main;
     857             :   ip6_hop_by_hop_header_t *hbh;
     858           0 :   u32 rewrite_len = 0;
     859           0 :   ioam_e2e_cache_option_t *e2e = 0;
     860           0 :   ioam_e2e_id_option_t *e2e_id = 0;
     861             : 
     862           0 :   vec_free (cm->rewrite);
     863           0 :   ip6_ioam_set_rewrite (&(cm->rewrite), hm->has_trace_option,
     864           0 :                         hm->has_pot_option, hm->has_seqno_option);
     865           0 :   hbh = (ip6_hop_by_hop_header_t *) cm->rewrite;
     866           0 :   rewrite_len = ((hbh->length + 1) << 3);
     867           0 :   vec_validate (cm->rewrite,
     868             :                 rewrite_len - 1 + IOAM_E2E_CACHE_OPTION_RND +
     869             :                 IOAM_E2E_ID_OPTION_RND);
     870           0 :   hbh = (ip6_hop_by_hop_header_t *) cm->rewrite;
     871             :   /* setup e2e id option to insert pool id and index of the node caching it */
     872           0 :   hbh->length += IOAM_E2E_CACHE_HBH_EXT_LEN + IOAM_E2E_ID_HBH_EXT_LEN;
     873           0 :   cm->rewrite_pool_index_offset = rewrite_len;
     874           0 :   e2e = (ioam_e2e_cache_option_t *) (cm->rewrite + rewrite_len);
     875           0 :   e2e->hdr.type = HBH_OPTION_TYPE_IOAM_E2E_CACHE_ID
     876             :     | HBH_OPTION_TYPE_SKIP_UNKNOWN;
     877           0 :   e2e->hdr.length = sizeof (ioam_e2e_cache_option_t) -
     878             :     sizeof (ip6_hop_by_hop_option_t);
     879           0 :   e2e->e2e_type = 2;
     880           0 :   e2e_id =
     881             :     (ioam_e2e_id_option_t *) ((u8 *) e2e + sizeof (ioam_e2e_cache_option_t));
     882           0 :   e2e_id->hdr.type =
     883             :     HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE_ID | HBH_OPTION_TYPE_SKIP_UNKNOWN;
     884           0 :   e2e_id->hdr.length =
     885             :     sizeof (ioam_e2e_id_option_t) - sizeof (ip6_hop_by_hop_option_t);
     886           0 :   e2e_id->e2e_type = 1;
     887             : 
     888           0 :   return (0);
     889             : }
     890             : 
     891             : static inline int
     892           0 : ip6_ioam_ts_cache_cleanup_rewrite (void)
     893             : {
     894           0 :   ioam_cache_main_t *cm = &ioam_cache_main;
     895             : 
     896           0 :   vec_free (cm->rewrite);
     897           0 :   cm->rewrite = 0;
     898           0 :   cm->rewrite_pool_index_offset = 0;
     899           0 :   return (0);
     900             : }
     901             : #endif /* __included_ioam_cache_h__ */
     902             : 
     903             : /*
     904             :  * fd.io coding-style-patch-verification: ON
     905             :  *
     906             :  * Local Variables:
     907             :  * eval: (c-set-style "gnu")
     908             :  * End:
     909             :  */

Generated by: LCOV version 1.14