github.com/cilium/cilium@v1.16.2/pkg/container/bitlpm/cidr_test.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package bitlpm
     5  
     6  import (
     7  	"fmt"
     8  	"net/netip"
     9  	"reflect"
    10  	"testing"
    11  
    12  	"github.com/stretchr/testify/assert"
    13  )
    14  
    15  func TestCIDRTrie(t *testing.T) {
    16  	trie := NewCIDRTrie[string]()
    17  	prefixes := map[string]netip.Prefix{
    18  		"0":    netip.MustParsePrefix("0.0.0.0/0"),
    19  		"1":    netip.MustParsePrefix("1.0.0.0/8"),
    20  		"2a":   netip.MustParsePrefix("1.1.0.0/16"),
    21  		"2b":   netip.MustParsePrefix("1.2.0.0/16"),
    22  		"3a":   netip.MustParsePrefix("1.1.1.0/24"),
    23  		"3b":   netip.MustParsePrefix("1.2.1.0/24"),
    24  		"4a":   netip.MustParsePrefix("1.1.1.0/25"),
    25  		"4b":   netip.MustParsePrefix("1.1.1.128/25"),
    26  		"last": netip.MustParsePrefix("1.1.1.129/32"),
    27  	}
    28  
    29  	// These are prefixes that have a direct longer match
    30  	overridden := []string{
    31  		"3a", // because 1.1.1.0/24 -> 1.1.1.0/25
    32  	}
    33  
    34  	for name, prefix := range prefixes {
    35  		trie.Upsert(prefix, name)
    36  	}
    37  
    38  loop:
    39  	for name := range prefixes {
    40  		for _, over := range overridden {
    41  			if name == over {
    42  				continue loop
    43  			}
    44  		}
    45  		have, _ := trie.LongestPrefixMatch(prefixes[name].Addr())
    46  		if have != name {
    47  			t.Errorf("LongestPrefixMatch(%s) returned %s want %s", prefixes[name].String(), have, name)
    48  		}
    49  	}
    50  
    51  	// Search should return the complete path to the prefix
    52  	// will look up 1.1.1.128/25.
    53  	wantPath := []string{
    54  		"0",    // 0.0.0.0/0
    55  		"1",    // 1.0.0.0/8
    56  		"2a",   // 1.1.0.0/16
    57  		"3a",   // 1.1.1.0/24
    58  		"4b",   // 1.1.1.128/25
    59  		"last", // 1.1.1.129/32
    60  	}
    61  
    62  	havePath := []string{}
    63  	trie.Ancestors(prefixes["last"], func(k netip.Prefix, v string) bool {
    64  		wantK := prefixes[v]
    65  		if wantK != k {
    66  			t.Errorf("Search(%s) returned an unexpected key-value pair: k %s v %s", prefixes["last"], k.String(), v)
    67  		}
    68  		havePath = append(havePath, v)
    69  		return true
    70  	})
    71  	t.Log(havePath)
    72  	assert.Equal(t, wantPath, havePath)
    73  
    74  	for _, tc := range []struct {
    75  		k string
    76  		v string
    77  	}{
    78  		{
    79  			"1.1.1.130/32",
    80  			"4b",
    81  		},
    82  		{
    83  			"1.1.1.1/32",
    84  			"4a",
    85  		},
    86  		{
    87  			"1.24.0.0/32",
    88  			"1",
    89  		},
    90  		{
    91  			"24.24.24.24/32",
    92  			"0",
    93  		},
    94  	} {
    95  		v, ok := trie.LongestPrefixMatch(netip.MustParsePrefix(tc.k).Addr())
    96  		assert.True(t, ok)
    97  		assert.Equal(t, tc.v, v)
    98  	}
    99  
   100  }
   101  
   102  func TestDescendants(t *testing.T) {
   103  	tests := []struct {
   104  		name     string
   105  		prefixes map[int]netip.Prefix
   106  	}{
   107  		{
   108  			name: "all",
   109  			prefixes: map[int]netip.Prefix{
   110  				0:  netip.MustParsePrefix("0.0.0.0/0"),
   111  				1:  netip.MustParsePrefix("0.0.0.0/1"),
   112  				2:  netip.MustParsePrefix("0.0.0.0/2"),
   113  				3:  netip.MustParsePrefix("0.0.0.0/3"),
   114  				4:  netip.MustParsePrefix("0.0.0.0/4"),
   115  				5:  netip.MustParsePrefix("0.0.0.0/5"),
   116  				6:  netip.MustParsePrefix("0.0.0.0/6"),
   117  				7:  netip.MustParsePrefix("0.0.0.0/7"),
   118  				8:  netip.MustParsePrefix("0.0.0.0/8"),
   119  				9:  netip.MustParsePrefix("0.0.0.0/9"),
   120  				10: netip.MustParsePrefix("0.0.0.0/10"),
   121  				11: netip.MustParsePrefix("0.0.0.0/11"),
   122  				12: netip.MustParsePrefix("0.0.0.0/12"),
   123  				13: netip.MustParsePrefix("0.0.0.0/13"),
   124  				14: netip.MustParsePrefix("0.0.0.0/14"),
   125  				15: netip.MustParsePrefix("0.0.0.0/15"),
   126  				16: netip.MustParsePrefix("0.0.0.0/16"),
   127  				17: netip.MustParsePrefix("0.0.0.0/17"),
   128  				18: netip.MustParsePrefix("0.0.0.0/18"),
   129  				19: netip.MustParsePrefix("0.0.0.0/19"),
   130  				20: netip.MustParsePrefix("0.0.0.0/20"),
   131  				21: netip.MustParsePrefix("0.0.0.0/21"),
   132  				22: netip.MustParsePrefix("0.0.0.0/22"),
   133  				23: netip.MustParsePrefix("0.0.0.0/23"),
   134  				24: netip.MustParsePrefix("0.0.0.0/24"),
   135  				25: netip.MustParsePrefix("0.0.0.0/25"),
   136  				26: netip.MustParsePrefix("0.0.0.0/26"),
   137  				27: netip.MustParsePrefix("0.0.0.0/27"),
   138  				28: netip.MustParsePrefix("0.0.0.0/28"),
   139  				29: netip.MustParsePrefix("0.0.0.0/29"),
   140  				30: netip.MustParsePrefix("0.0.0.0/30"),
   141  				31: netip.MustParsePrefix("0.0.0.0/31"),
   142  				32: netip.MustParsePrefix("0.0.0.0/32"),
   143  			},
   144  		}, {
   145  			name: "sparse",
   146  			prefixes: map[int]netip.Prefix{
   147  				0:  netip.MustParsePrefix("0.0.0.0/0"),
   148  				16: netip.MustParsePrefix("0.0.0.0/16"),
   149  				32: netip.MustParsePrefix("0.0.0.0/32"),
   150  			},
   151  		},
   152  	}
   153  	for _, tt := range tests {
   154  		t.Logf("Running test case '" + tt.name + "'")
   155  		tr := NewCIDRTrie[string]()
   156  		for _, v := range tt.prefixes {
   157  			tr.Upsert(v, v.String())
   158  		}
   159  		for i := 0; i <= 32; i++ {
   160  			pref, ok := tt.prefixes[i]
   161  			if !ok {
   162  				// No such prefix
   163  				continue
   164  			}
   165  			expectedRes := make([]string, 0, 32-i)
   166  			for t := i; t <= 32; t++ {
   167  				p, ok := tt.prefixes[t]
   168  				if !ok {
   169  					continue
   170  				}
   171  				expectedRes = append(expectedRes, p.String())
   172  			}
   173  			gotRes := make([]string, 0, 32-i)
   174  			tr.Descendants(pref, func(_ netip.Prefix, v string) bool {
   175  				gotRes = append(gotRes, v)
   176  				return true
   177  			})
   178  			if !reflect.DeepEqual(expectedRes, gotRes) {
   179  				t.Fatalf("Descendants prefix %s, expected to get %v, but got: %v", pref.String(), expectedRes, gotRes)
   180  			}
   181  		}
   182  	}
   183  }
   184  
   185  func TestBitValueAt(t *testing.T) {
   186  	for i, tc := range []struct {
   187  		v    netip.Prefix
   188  		i    uint
   189  		want uint8
   190  	}{
   191  		// note: prefix length does not matter
   192  		{
   193  			v:    netip.MustParsePrefix("00ff:ffff::/128"),
   194  			i:    0,
   195  			want: 0,
   196  		}, {
   197  			v:    netip.MustParsePrefix("00ff:ffff::/128"),
   198  			i:    7,
   199  			want: 0,
   200  		}, {
   201  			v:    netip.MustParsePrefix("00ff:ffff::/128"),
   202  			i:    8,
   203  			want: 1,
   204  		}, {
   205  			v:    netip.MustParsePrefix("00ff:ffff::/128"),
   206  			i:    9,
   207  			want: 1,
   208  		}, {
   209  			v:    netip.MustParsePrefix("00ff:fe00::/128"),
   210  			i:    16,
   211  			want: 1,
   212  		}, {
   213  			v:    netip.MustParsePrefix("00ff:fe00::/128"),
   214  			i:    17,
   215  			want: 1,
   216  		}, {
   217  			v:    netip.MustParsePrefix("00ff:fe00::/128"),
   218  			i:    18,
   219  			want: 1,
   220  		}, {
   221  			v:    netip.MustParsePrefix("00ff:fe00::/128"),
   222  			i:    19,
   223  			want: 1,
   224  		}, {
   225  			v:    netip.MustParsePrefix("00ff:fe00::/128"),
   226  			i:    20,
   227  			want: 1,
   228  		}, {
   229  			v:    netip.MustParsePrefix("00ff:fe00::/128"),
   230  			i:    21,
   231  			want: 1,
   232  		}, {
   233  			v:    netip.MustParsePrefix("00ff:fe00::/128"),
   234  			i:    22,
   235  			want: 1,
   236  		}, {
   237  			v:    netip.MustParsePrefix("00ff:fe00::/128"),
   238  			i:    23,
   239  			want: 0,
   240  		},
   241  	} {
   242  		t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
   243  			have := cidrKey(tc.v).BitValueAt(tc.i)
   244  			if have != tc.want {
   245  				t.Errorf("Prefix %s index %d got bit %d, want %d", tc.v.String(), tc.i, have, tc.want)
   246  			}
   247  		})
   248  	}
   249  }
   250  
   251  func TestCommonPrefix(t *testing.T) {
   252  	for i, tc := range []struct {
   253  		v1   netip.Prefix
   254  		v2   netip.Prefix
   255  		want uint
   256  	}{
   257  		{
   258  			v1:   netip.MustParsePrefix("00ff::/128"),
   259  			v2:   netip.MustParsePrefix("00fe::/128"),
   260  			want: 15,
   261  		},
   262  		{
   263  			v1:   netip.MustParsePrefix("f0ff::/128"),
   264  			v2:   netip.MustParsePrefix("00fe::/128"),
   265  			want: 0,
   266  		},
   267  		{
   268  			v1:   netip.MustParsePrefix("ffff::/128"),
   269  			v2:   netip.MustParsePrefix("ff7f::/128"),
   270  			want: 8,
   271  		},
   272  		{
   273  			v1:   netip.MustParsePrefix("ffff::/128"),
   274  			v2:   netip.MustParsePrefix("fe7f::/128"),
   275  			want: 7,
   276  		},
   277  		{
   278  			v1:   netip.MustParsePrefix("::/128"),
   279  			v2:   netip.MustParsePrefix("::/128"),
   280  			want: 128,
   281  		}, {
   282  			v1:   netip.MustParsePrefix("::/128"),
   283  			v2:   netip.MustParsePrefix("::1/128"),
   284  			want: 127,
   285  		},
   286  	} {
   287  
   288  		t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
   289  			have := cidrKey(tc.v1).CommonPrefix(tc.v2)
   290  			if have != tc.want {
   291  				t.Errorf("p1 %v p2 %v got %d want %d", tc.v1, tc.v2, have, tc.want)
   292  			}
   293  		})
   294  	}
   295  }