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 }