LCOV - code coverage report
Current view: top level - vnet/ip - ip_sas.c (source / functions) Hit Total Coverage
Test: coverage-filtered.info Lines: 51 77 66.2 %
Date: 2023-10-26 01:39:38 Functions: 5 6 83.3 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2021 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             : 
      16             : #include "ip_sas.h"
      17             : #include <vppinfra/types.h>
      18             : #include <vnet/ip/ip_interface.h>
      19             : #include <vnet/fib/fib_table.h>
      20             : #include <vnet/ip/ip6_link.h>
      21             : #include <vppinfra/byte_order.h>
      22             : 
      23             : /*
      24             :  * This file implement source address selection for VPP applications
      25             :  * (e.g. ping, DNS, ICMP)
      26             :  * It does not yet implement full fledged RFC6724 SAS.
      27             :  * SAS assumes every IP enabled interface has an address. The algorithm will
      28             :  * not go and hunt for a suitable IP address on other interfaces than the
      29             :  * output interface or the specified preferred sw_if_index.
      30             :  * That means that an interface with just an IPv6 link-local address must also
      31             :  * be configured with an unnumbered configuration pointing to a numbered
      32             :  * interface.
      33             :  */
      34             : 
      35             : static int
      36          25 : ip6_sas_commonlen (const ip6_address_t *a1, const ip6_address_t *a2)
      37             : {
      38          25 :   u64 fa = clib_net_to_host_u64 (a1->as_u64[0]) ^
      39          25 :            clib_net_to_host_u64 (a2->as_u64[0]);
      40          25 :   if (fa == 0)
      41             :     {
      42          21 :       u64 la = clib_net_to_host_u64 (a1->as_u64[1]) ^
      43          21 :                clib_net_to_host_u64 (a2->as_u64[1]);
      44          21 :       if (la == 0)
      45           0 :         return 128;
      46          21 :       return 64 + __builtin_clzll (la);
      47             :     }
      48             :   else
      49             :     {
      50           4 :       return __builtin_clzll (fa);
      51             :     }
      52             : }
      53             : 
      54             : static int
      55         119 : ip4_sas_commonlen (const ip4_address_t *a1, const ip4_address_t *a2)
      56             : {
      57         119 :   u64 a =
      58         119 :     clib_net_to_host_u32 (a1->as_u32) ^ clib_net_to_host_u32 (a2->as_u32);
      59         119 :   if (a == 0)
      60          53 :     return 32;
      61          66 :   return __builtin_clz (a);
      62             : }
      63             : 
      64             : /*
      65             :  * walk all addresses on an interface:
      66             :  *  - prefer a source matching the scope of the destination address.
      67             :  *  - last resort pick the source address with the longest
      68             :  *    common prefix with destination
      69             :  * NOTE: This should at some point implement RFC6724.
      70             :  */
      71             : bool
      72          30 : ip6_sas_by_sw_if_index (u32 sw_if_index, const ip6_address_t *dst,
      73             :                         ip6_address_t *src)
      74             : {
      75          30 :   ip_interface_address_t *ia = 0;
      76          30 :   ip_lookup_main_t *lm6 = &ip6_main.lookup_main;
      77          30 :   ip6_address_t *tmp, *bestsrc = 0;
      78          30 :   int bestlen = 0, l;
      79             : 
      80          60 :   if (ip6_address_is_link_local_unicast (dst) ||
      81          30 :       dst->as_u32[0] == clib_host_to_net_u32 (0xff020000))
      82             :     {
      83           0 :       const ip6_address_t *ll = ip6_get_link_local_address (sw_if_index);
      84           0 :       if (NULL == ll)
      85             :         {
      86           0 :           return false;
      87             :         }
      88           0 :       ip6_address_copy (src, ll);
      89           0 :       return true;
      90             :     }
      91             : 
      92          55 :   foreach_ip_interface_address (
      93             :     lm6, ia, sw_if_index, 1, ({
      94             :       if (ia->flags & IP_INTERFACE_ADDRESS_FLAG_STALE)
      95             :         continue;
      96             :       tmp = ip_interface_address_get_address (lm6, ia);
      97             :       l = ip6_sas_commonlen (tmp, dst);
      98             :       if (l > bestlen || bestsrc == 0)
      99             :         {
     100             :           bestsrc = tmp;
     101             :           bestlen = l;
     102             :         }
     103             :     }));
     104          30 :   if (bestsrc)
     105             :     {
     106          25 :       ip6_address_copy (src, bestsrc);
     107          25 :       return true;
     108             :     }
     109           5 :   return false;
     110             : }
     111             : 
     112             : /*
     113             :  * walk all addresses on an interface and pick the source address with the
     114             :  * longest common prefix with destination.
     115             :  */
     116             : bool
     117         107 : ip4_sas_by_sw_if_index (u32 sw_if_index, const ip4_address_t *dst,
     118             :                         ip4_address_t *src)
     119             : {
     120         107 :   ip_interface_address_t *ia = 0;
     121         107 :   ip_lookup_main_t *lm4 = &ip4_main.lookup_main;
     122         107 :   ip4_address_t *tmp, *bestsrc = 0;
     123         107 :   int bestlen = 0, l;
     124             : 
     125         226 :   foreach_ip_interface_address (
     126             :     lm4, ia, sw_if_index, 1, ({
     127             :       if (ia->flags & IP_INTERFACE_ADDRESS_FLAG_STALE)
     128             :         continue;
     129             :       tmp = ip_interface_address_get_address (lm4, ia);
     130             :       l = ip4_sas_commonlen (tmp, dst);
     131             :       if (l > bestlen || bestsrc == 0)
     132             :         {
     133             :           bestsrc = tmp;
     134             :           bestlen = l;
     135             :         }
     136             :     }));
     137         107 :   if (bestsrc)
     138             :     {
     139         107 :       src->as_u32 = bestsrc->as_u32;
     140         107 :       return true;
     141             :     }
     142           0 :   return false;
     143             : }
     144             : 
     145             : /*
     146             :  * table_id must be set. Default = 0.
     147             :  * sw_if_index is the interface to pick SA from otherwise ~0 will pick from
     148             :  * outbound interface.
     149             :  *
     150             :  * NOTE: What to do if multiple output interfaces?
     151             :  *
     152             :  */
     153             : bool
     154           0 : ip6_sas (u32 table_id, u32 sw_if_index, const ip6_address_t *dst,
     155             :          ip6_address_t *src)
     156             : {
     157             :   fib_prefix_t prefix;
     158           0 :   u32 if_index = sw_if_index;
     159             : 
     160             :   /* If sw_if_index is not specified use the output interface. */
     161           0 :   if (sw_if_index == ~0)
     162             :     {
     163           0 :       clib_memcpy (&prefix.fp_addr.ip6, dst, sizeof (*dst));
     164           0 :       prefix.fp_proto = FIB_PROTOCOL_IP6;
     165           0 :       prefix.fp_len = 128;
     166             : 
     167           0 :       u32 fib_index = fib_table_find (prefix.fp_proto, table_id);
     168           0 :       if (fib_index == (u32) ~0)
     169           0 :         return false;
     170             : 
     171           0 :       fib_node_index_t fei = fib_table_lookup (fib_index, &prefix);
     172           0 :       if (fei == FIB_NODE_INDEX_INVALID)
     173           0 :         return false;
     174             : 
     175           0 :       u32 output_sw_if_index = fib_entry_get_resolving_interface (fei);
     176           0 :       if (output_sw_if_index == ~0)
     177           0 :         return false;
     178           0 :       if_index = output_sw_if_index;
     179             :     }
     180           0 :   return ip6_sas_by_sw_if_index (if_index, dst, src);
     181             : }
     182             : 
     183             : /*
     184             :  * table_id must be set. Default = 0.
     185             :  * sw_if_index is the interface to pick SA from otherwise ~0 will pick from
     186             :  * outbound interface.
     187             :  *
     188             :  * NOTE: What to do if multiple output interfaces?
     189             :  *
     190             :  */
     191             : bool
     192           2 : ip4_sas (u32 table_id, u32 sw_if_index, const ip4_address_t *dst,
     193             :          ip4_address_t *src)
     194             : {
     195             :   fib_prefix_t prefix;
     196           2 :   u32 if_index = sw_if_index;
     197             : 
     198             :   /* If sw_if_index is not specified use the output interface. */
     199           2 :   if (sw_if_index == ~0)
     200             :     {
     201           2 :       clib_memcpy (&prefix.fp_addr.ip4, dst, sizeof (*dst));
     202           2 :       prefix.fp_proto = FIB_PROTOCOL_IP4;
     203           2 :       prefix.fp_len = 32;
     204             : 
     205           2 :       u32 fib_index = fib_table_find (prefix.fp_proto, table_id);
     206           2 :       if (fib_index == (u32) ~0)
     207           0 :         return false;
     208             : 
     209           2 :       fib_node_index_t fei = fib_table_lookup (fib_index, &prefix);
     210           2 :       if (fei == FIB_NODE_INDEX_INVALID)
     211           0 :         return false;
     212             : 
     213           2 :       u32 output_sw_if_index = fib_entry_get_resolving_interface (fei);
     214           2 :       if (output_sw_if_index == ~0)
     215           1 :         return false;
     216           1 :       if_index = output_sw_if_index;
     217             :     }
     218           1 :   return ip4_sas_by_sw_if_index (if_index, dst, src);
     219             : }

Generated by: LCOV version 1.14