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

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package labels
     5  
     6  import (
     7  	"net/netip"
     8  	"testing"
     9  
    10  	"github.com/stretchr/testify/assert"
    11  
    12  	"github.com/cilium/cilium/pkg/option"
    13  )
    14  
    15  func TestGetCIDRLabels(t *testing.T) {
    16  	// save global config and restore it at the end of the test
    17  	enableIPv4, enableIPv6 := option.Config.EnableIPv4, option.Config.EnableIPv6
    18  	t.Cleanup(func() {
    19  		option.Config.EnableIPv4, option.Config.EnableIPv6 = enableIPv4, enableIPv6
    20  	})
    21  
    22  	for _, tc := range []struct {
    23  		name       string
    24  		enableIPv4 bool
    25  		enableIPv6 bool
    26  		prefix     netip.Prefix
    27  		expected   LabelArray
    28  	}{
    29  		{
    30  			name:       "IPv4 /32 prefix",
    31  			enableIPv4: true,
    32  			enableIPv6: false,
    33  			prefix:     netip.MustParsePrefix("192.0.2.3/32"),
    34  			expected: ParseLabelArray(
    35  				"cidr:192.0.2.3/32",
    36  				"reserved:world",
    37  			),
    38  		},
    39  		{
    40  			name:       "IPv4 /24 prefix",
    41  			enableIPv4: true,
    42  			enableIPv6: false,
    43  			prefix:     netip.MustParsePrefix("192.0.2.0/24"),
    44  			expected: ParseLabelArray(
    45  				"cidr:192.0.2.0/24",
    46  				"reserved:world",
    47  			),
    48  		},
    49  		{
    50  			name:       "IPv4 /16 prefix",
    51  			enableIPv4: true,
    52  			enableIPv6: false,
    53  			prefix:     netip.MustParsePrefix("10.0.0.0/16"),
    54  			expected: ParseLabelArray(
    55  				"cidr:10.0.0.0/16",
    56  				"reserved:world",
    57  			),
    58  		},
    59  		{
    60  			name:       "IPv4 zero length prefix",
    61  			enableIPv4: true,
    62  			enableIPv6: false,
    63  			prefix:     netip.MustParsePrefix("0.0.0.0/0"),
    64  			expected: ParseLabelArray(
    65  				"reserved:world",
    66  			),
    67  		},
    68  		{
    69  			name:       "IPv6 /112 prefix",
    70  			enableIPv4: false,
    71  			enableIPv6: true,
    72  			prefix:     netip.MustParsePrefix("2001:db8:cafe::cab:4:b0b:0/112"),
    73  			expected: ParseLabelArray(
    74  				// Note that we convert the colons in IPv6 addresses into dashes when
    75  				// translating into labels, because endpointSelectors don't support
    76  				// colons.
    77  				"cidr:2001-db8-cafe-0-cab-4-b0b-0/112",
    78  				"reserved:world",
    79  			),
    80  		},
    81  		{
    82  			name:       "IPv6 /128 prefix",
    83  			enableIPv4: false,
    84  			enableIPv6: true,
    85  			prefix:     netip.MustParsePrefix("2001:DB8::1/128"),
    86  			expected: ParseLabelArray(
    87  				"cidr:2001-db8--1/128",
    88  				"reserved:world",
    89  			),
    90  		},
    91  		{
    92  			name:       "IPv4 /32 prefix in dual stack mode",
    93  			enableIPv4: true,
    94  			enableIPv6: true,
    95  			prefix:     netip.MustParsePrefix("192.0.2.3/32"),
    96  			expected: ParseLabelArray(
    97  				"cidr:192.0.2.3/32",
    98  				"reserved:world-ipv4",
    99  			),
   100  		},
   101  		{
   102  			name:       "IPv4 /24 prefix in dual stack mode",
   103  			enableIPv4: true,
   104  			enableIPv6: true,
   105  			prefix:     netip.MustParsePrefix("192.0.2.0/24"),
   106  			expected: ParseLabelArray(
   107  				"cidr:192.0.2.0/24",
   108  				"reserved:world-ipv4",
   109  			),
   110  		},
   111  		{
   112  			name:       "IPv4 /16 prefix in dual stack mode",
   113  			enableIPv4: true,
   114  			enableIPv6: true,
   115  			prefix:     netip.MustParsePrefix("10.0.0.0/16"),
   116  			expected: ParseLabelArray(
   117  				"cidr:10.0.0.0/16",
   118  				"reserved:world-ipv4",
   119  			),
   120  		},
   121  		{
   122  			name:       "IPv4 zero length prefix in dual stack mode",
   123  			enableIPv4: true,
   124  			enableIPv6: true,
   125  			prefix:     netip.MustParsePrefix("0.0.0.0/0"),
   126  			expected: ParseLabelArray(
   127  				"reserved:world-ipv4",
   128  			),
   129  		},
   130  		{
   131  			name:       "IPv6 /112 prefix in dual stack mode",
   132  			enableIPv4: true,
   133  			enableIPv6: true,
   134  			prefix:     netip.MustParsePrefix("2001:db8:cafe::cab:4:b0b:0/112"),
   135  			expected: ParseLabelArray(
   136  				"cidr:2001-db8-cafe-0-cab-4-b0b-0/112",
   137  				"reserved:world-ipv6",
   138  			),
   139  		},
   140  		{
   141  			name:       "IPv6 /128 prefix in dual stack mode",
   142  			enableIPv4: true,
   143  			enableIPv6: true,
   144  			prefix:     netip.MustParsePrefix("2001:DB8::1/128"),
   145  			expected: ParseLabelArray(
   146  				"cidr:2001-db8--1/128",
   147  				"reserved:world-ipv6",
   148  			),
   149  		},
   150  	} {
   151  		t.Run(tc.name, func(t *testing.T) {
   152  			option.Config.EnableIPv4 = tc.enableIPv4
   153  			option.Config.EnableIPv6 = tc.enableIPv6
   154  
   155  			lbls := GetCIDRLabels(tc.prefix)
   156  			lblArray := lbls.LabelArray()
   157  			assert.ElementsMatch(t, lblArray, tc.expected)
   158  		})
   159  	}
   160  }
   161  
   162  func TestIPStringToLabel(t *testing.T) {
   163  	for _, tc := range []struct {
   164  		ip      string
   165  		label   string
   166  		wantErr bool
   167  	}{
   168  		{
   169  			ip:    "0.0.0.0/0",
   170  			label: "cidr:0.0.0.0/0",
   171  		},
   172  		{
   173  			ip:    "192.0.2.3",
   174  			label: "cidr:192.0.2.3/32",
   175  		},
   176  		{
   177  			ip:    "192.0.2.3/32",
   178  			label: "cidr:192.0.2.3/32",
   179  		},
   180  		{
   181  			ip:    "192.0.2.3/24",
   182  			label: "cidr:192.0.2.0/24",
   183  		},
   184  		{
   185  			ip:    "192.0.2.0/24",
   186  			label: "cidr:192.0.2.0/24",
   187  		},
   188  		{
   189  			ip:    "::/0",
   190  			label: "cidr:0--0/0",
   191  		},
   192  		{
   193  			ip:    "fdff::ff",
   194  			label: "cidr:fdff--ff/128",
   195  		},
   196  		{
   197  			ip:    "f00d:42::ff/128",
   198  			label: "cidr:f00d-42--ff/128",
   199  		},
   200  		{
   201  			ip:    "f00d:42::ff/96",
   202  			label: "cidr:f00d-42--0/96",
   203  		},
   204  		{
   205  			ip:      "",
   206  			wantErr: true,
   207  		},
   208  		{
   209  			ip:      "foobar",
   210  			wantErr: true,
   211  		},
   212  	} {
   213  		lbl, err := IPStringToLabel(tc.ip)
   214  		if !tc.wantErr {
   215  			assert.NoError(t, err)
   216  			assert.Equal(t, lbl.String(), tc.label)
   217  		} else {
   218  			assert.Error(t, err)
   219  		}
   220  	}
   221  }
   222  
   223  func BenchmarkIPStringToLabel(b *testing.B) {
   224  	for _, ip := range []string{
   225  		"0.0.0.0/0",
   226  		"192.0.2.3",
   227  		"192.0.2.3/32",
   228  		"192.0.2.3/24",
   229  		"192.0.2.0/24",
   230  		"::/0",
   231  		"fdff::ff",
   232  		"f00d:42::ff/128",
   233  		"f00d:42::ff/96",
   234  	} {
   235  		b.Run(ip, func(b *testing.B) {
   236  			b.ReportAllocs()
   237  			for i := 0; i < b.N; i++ {
   238  				_, err := IPStringToLabel(ip)
   239  				if err != nil {
   240  					b.Fatal(err)
   241  				}
   242  			}
   243  		})
   244  	}
   245  }
   246  
   247  func TestGetPrintableModel(t *testing.T) {
   248  	assert.Equal(t,
   249  		[]string{"k8s:foo=bar"},
   250  		NewLabelsFromModel([]string{
   251  			"k8s:foo=bar",
   252  		}).GetPrintableModel(),
   253  	)
   254  
   255  	assert.Equal(t,
   256  		[]string{
   257  			"k8s:foo=bar",
   258  			"reserved:remote-node",
   259  		},
   260  		NewLabelsFromModel([]string{
   261  			"k8s:foo=bar",
   262  			"reserved:remote-node",
   263  		}).GetPrintableModel(),
   264  	)
   265  
   266  	assert.Equal(t,
   267  		[]string{
   268  			"k8s:foo=bar",
   269  			"reserved:remote-node",
   270  		},
   271  		NewLabelsFromModel([]string{
   272  			"k8s:foo=bar",
   273  			"reserved:remote-node",
   274  		}).GetPrintableModel(),
   275  	)
   276  
   277  	// Test multiple CIDRs, as well as other labels
   278  	cl := NewLabelsFromModel([]string{
   279  		"k8s:foo=bar",
   280  		"reserved:remote-node",
   281  	})
   282  	cl.MergeLabels(GetCIDRLabels(netip.MustParsePrefix("10.0.0.6/32")))
   283  	cl.MergeLabels(GetCIDRLabels(netip.MustParsePrefix("10.0.1.0/24")))
   284  	cl.MergeLabels(GetCIDRLabels(netip.MustParsePrefix("192.168.0.0/24")))
   285  	cl.MergeLabels(GetCIDRLabels(netip.MustParsePrefix("fc00:c111::5/128")))
   286  	cl.MergeLabels(GetCIDRLabels(netip.MustParsePrefix("fc00:c112::0/64")))
   287  	assert.Equal(t,
   288  		[]string{
   289  			"cidr:10.0.0.6/32",
   290  			"cidr:10.0.1.0/24",
   291  			"cidr:192.168.0.0/24",
   292  			"cidr:fc00:c111::5/128",
   293  			"cidr:fc00:c112::/64",
   294  			"k8s:foo=bar",
   295  			"reserved:remote-node",
   296  			"reserved:world-ipv4",
   297  			"reserved:world-ipv6",
   298  		},
   299  		cl.GetPrintableModel(),
   300  	)
   301  }
   302  
   303  func TestLabelToPrefix(t *testing.T) {
   304  	for _, pfx := range []string{
   305  		"1.1.1.1/32",
   306  		"1.1.1.0/24",
   307  		"2001::4/128",
   308  		"2001::fffc/126",
   309  		"::/0",
   310  		"2001::/64",
   311  		"0.0.0.0/0",
   312  	} {
   313  		want, err := netip.ParsePrefix(pfx)
   314  		if err != nil {
   315  			t.Fatalf("failed to parse prefix %s: %v", pfx, err)
   316  		}
   317  		want = want.Masked()
   318  
   319  		label := maskedIPToLabel(want.Addr().String(), want.Bits())
   320  		have, err := LabelToPrefix(label.Key)
   321  		if err != nil {
   322  			t.Fatalf("unexpected err: %v", err)
   323  		}
   324  		if have != want {
   325  			t.Fatalf("prefixes did not match: want %s, have %s, label %s", want, have, label.Key)
   326  		}
   327  	}
   328  }