github.com/cilium/cilium@v1.16.2/pkg/datapath/linux/ipsec/ipsec_linux_test.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package ipsec
     5  
     6  import (
     7  	"bytes"
     8  	"log/slog"
     9  	"net"
    10  	"os"
    11  	"testing"
    12  
    13  	"github.com/cilium/ebpf/rlimit"
    14  	"github.com/cilium/hive/hivetest"
    15  	"github.com/stretchr/testify/require"
    16  	"github.com/vishvananda/netlink"
    17  
    18  	"github.com/cilium/cilium/pkg/datapath/linux/linux_defaults"
    19  	"github.com/cilium/cilium/pkg/node"
    20  	"github.com/cilium/cilium/pkg/testutils"
    21  )
    22  
    23  func setupIPSecSuitePrivileged(tb testing.TB) *slog.Logger {
    24  	testutils.PrivilegedTest(tb)
    25  	node.SetTestLocalNodeStore()
    26  	err := rlimit.RemoveMemlock()
    27  	require.NoError(tb, err)
    28  	log := hivetest.Logger(tb)
    29  
    30  	tb.Cleanup(func() {
    31  		ipSecKeysGlobal = make(map[string]*ipSecKey)
    32  		node.UnsetTestLocalNodeStore()
    33  		err := DeleteXFRM(log)
    34  		if err != nil {
    35  			tb.Errorf("Failed cleaning XFRM state: %v", err)
    36  		}
    37  	})
    38  	return log
    39  }
    40  
    41  var (
    42  	path           = "ipsec_keys_test"
    43  	keysDat        = []byte("1 hmac(sha256) 0123456789abcdef0123456789abcdef cbc(aes) 0123456789abcdef0123456789abcdef\n1 hmac(sha256) 0123456789abcdef0123456789abcdef cbc(aes) 0123456789abcdef0123456789abcdef foobar\n1 digest_null \"\" cipher_null \"\"\n")
    44  	keysAeadDat    = []byte("6 rfc4106(gcm(aes)) 44434241343332312423222114131211f4f3f2f1 128\n")
    45  	keysAeadDat256 = []byte("6 rfc4106(gcm(aes)) 44434241343332312423222114131211f4f3f2f144434241343332312423222114131211 128\n")
    46  	invalidKeysDat = []byte("1 test abcdefghijklmnopqrstuvwzyzABCDEF test abcdefghijklmnopqrstuvwzyzABCDEF\n")
    47  )
    48  
    49  func TestLoadKeysNoFile(t *testing.T) {
    50  	log := setupIPSecSuitePrivileged(t)
    51  
    52  	_, _, err := LoadIPSecKeysFile(log, path)
    53  	require.Equal(t, true, os.IsNotExist(err))
    54  }
    55  
    56  func TestInvalidLoadKeys(t *testing.T) {
    57  	log := setupIPSecSuitePrivileged(t)
    58  
    59  	keys := bytes.NewReader(invalidKeysDat)
    60  	_, _, err := LoadIPSecKeys(log, keys)
    61  	require.Error(t, err)
    62  
    63  	_, local, err := net.ParseCIDR("1.1.3.4/16")
    64  	require.NoError(t, err)
    65  	_, remote, err := net.ParseCIDR("1.2.3.4/16")
    66  	require.NoError(t, err)
    67  
    68  	_, err = UpsertIPsecEndpoint(log, local, remote, local.IP, remote.IP, 0, "remote-boot-id", IPSecDirBoth, false, false, DefaultReqID)
    69  	require.Error(t, err)
    70  }
    71  
    72  func TestLoadKeys(t *testing.T) {
    73  	log := setupIPSecSuitePrivileged(t)
    74  
    75  	testCases := [][]byte{keysDat, keysAeadDat, keysAeadDat256}
    76  	for _, testCase := range testCases {
    77  		keys := bytes.NewReader(testCase)
    78  		_, spi, err := LoadIPSecKeys(log, keys)
    79  		require.NoError(t, err)
    80  		err = SetIPSecSPI(log, spi)
    81  		require.NoError(t, err)
    82  	}
    83  }
    84  
    85  func TestParseSPI(t *testing.T) {
    86  	log := setupIPSecSuitePrivileged(t)
    87  
    88  	testCases := []struct {
    89  		input    string
    90  		expSPI   uint8
    91  		expOff   int
    92  		expESN   bool
    93  		expError bool
    94  	}{
    95  		{"254", 0, 0, false, true},
    96  		{"15", 15, 0, false, false},
    97  		{"3+", 3, 0, true, false},
    98  		{"abc", 1, -1, false, false},
    99  		{"0", 0, 0, false, true},
   100  	}
   101  	for _, tc := range testCases {
   102  		spi, off, esn, err := parseSPI(log, tc.input)
   103  		if spi != tc.expSPI {
   104  			t.Fatalf("For input %q, expected SPI %d, but got %d", tc.input, tc.expSPI, spi)
   105  		}
   106  		if off != tc.expOff {
   107  			t.Fatalf("For input %q, expected base offset %d, but got %d", tc.input, tc.expOff, off)
   108  		}
   109  		if esn != tc.expESN {
   110  			t.Fatalf("For input %q, expected ESN %t, but got %t", tc.input, tc.expESN, esn)
   111  		}
   112  		if tc.expError {
   113  			require.Error(t, err)
   114  		} else {
   115  			require.NoError(t, err)
   116  		}
   117  	}
   118  }
   119  
   120  func TestUpsertIPSecEquals(t *testing.T) {
   121  	log := setupIPSecSuitePrivileged(t)
   122  
   123  	_, local, err := net.ParseCIDR("1.2.3.4/16")
   124  	require.NoError(t, err)
   125  	_, remote, err := net.ParseCIDR("1.2.3.4/16")
   126  	require.NoError(t, err)
   127  
   128  	_, authKey, err := decodeIPSecKey("0123456789abcdef0123456789abcdef")
   129  	require.NoError(t, err)
   130  	_, cryptKey, err := decodeIPSecKey("0123456789abcdef0123456789abcdef")
   131  	require.NoError(t, err)
   132  	key := &ipSecKey{
   133  		Spi:   1,
   134  		ReqID: 1,
   135  		Auth:  &netlink.XfrmStateAlgo{Name: "hmac(sha256)", Key: authKey},
   136  		Crypt: &netlink.XfrmStateAlgo{Name: "cbc(aes)", Key: cryptKey},
   137  	}
   138  
   139  	ipSecKeysGlobal["1.2.3.4"] = key
   140  	ipSecKeysGlobal[""] = key
   141  
   142  	_, err = UpsertIPsecEndpoint(log, local, remote, local.IP, remote.IP, 0, "remote-boot-id", IPSecDirBoth, false, false, DefaultReqID)
   143  	require.NoError(t, err)
   144  
   145  	// Let's check that state was not added as source and destination are the same
   146  	result, err := netlink.XfrmStateList(netlink.FAMILY_ALL)
   147  	require.NoError(t, err)
   148  	require.Equal(t, 0, len(result))
   149  
   150  	err = DeleteXFRM(log)
   151  	require.NoError(t, err)
   152  
   153  	_, aeadKey, err := decodeIPSecKey("44434241343332312423222114131211f4f3f2f1")
   154  	require.NoError(t, err)
   155  	key = &ipSecKey{
   156  		Spi:   1,
   157  		ReqID: 1,
   158  		Aead:  &netlink.XfrmStateAlgo{Name: "rfc4106(gcm(aes))", Key: aeadKey, ICVLen: 128},
   159  		Crypt: nil,
   160  		Auth:  nil,
   161  	}
   162  
   163  	ipSecKeysGlobal["1.2.3.4"] = key
   164  	ipSecKeysGlobal[""] = key
   165  
   166  	_, err = UpsertIPsecEndpoint(log, local, remote, local.IP, remote.IP, 0, "remote-boot-id", IPSecDirBoth, false, false, DefaultReqID)
   167  	require.NoError(t, err)
   168  
   169  	// Let's check that state was not added as source and destination are the same
   170  	result, err = netlink.XfrmStateList(netlink.FAMILY_ALL)
   171  	require.NoError(t, err)
   172  	require.Equal(t, 0, len(result))
   173  }
   174  
   175  func TestUpsertIPSecEndpoint(t *testing.T) {
   176  	log := setupIPSecSuitePrivileged(t)
   177  
   178  	_, local, err := net.ParseCIDR("1.1.3.4/16")
   179  	require.NoError(t, err)
   180  	_, remote, err := net.ParseCIDR("1.2.3.4/16")
   181  	require.NoError(t, err)
   182  
   183  	_, authKey, err := decodeIPSecKey("0123456789abcdef0123456789abcdef")
   184  	require.NoError(t, err)
   185  	_, cryptKey, err := decodeIPSecKey("0123456789abcdef0123456789abcdef")
   186  	require.NoError(t, err)
   187  	key := &ipSecKey{
   188  		Spi:   1,
   189  		ReqID: 1,
   190  		Auth:  &netlink.XfrmStateAlgo{Name: "hmac(sha256)", Key: authKey},
   191  		Crypt: &netlink.XfrmStateAlgo{Name: "cbc(aes)", Key: cryptKey},
   192  	}
   193  
   194  	ipSecKeysGlobal["1.1.3.4"] = key
   195  	ipSecKeysGlobal["1.2.3.4"] = key
   196  	ipSecKeysGlobal[""] = key
   197  
   198  	_, err = UpsertIPsecEndpoint(log, local, remote, local.IP, remote.IP, 0, "remote-boot-id", IPSecDirBoth, false, false, DefaultReqID)
   199  	require.NoError(t, err)
   200  
   201  	getState := &netlink.XfrmState{
   202  		Src:   local.IP,
   203  		Dst:   remote.IP,
   204  		Proto: netlink.XFRM_PROTO_ESP,
   205  		Spi:   int(key.Spi),
   206  		Mark: &netlink.XfrmMark{
   207  			Value: ipSecXfrmMarkSetSPI(linux_defaults.RouteMarkEncrypt, uint8(key.Spi)),
   208  			Mask:  linux_defaults.IPsecMarkMaskOut,
   209  		},
   210  	}
   211  
   212  	state, err := netlink.XfrmStateGet(getState)
   213  	require.NoError(t, err)
   214  	require.NotNil(t, state)
   215  	require.Nil(t, state.Aead)
   216  	require.NotNil(t, state.Auth)
   217  	require.Equal(t, "hmac(sha256)", state.Auth.Name)
   218  	require.Equal(t, authKey, state.Auth.Key)
   219  	require.NotNil(t, state.Crypt)
   220  	require.Equal(t, "cbc(aes)", state.Crypt.Name)
   221  	require.Equal(t, cryptKey, state.Crypt.Key)
   222  	// ESN bit is not set, so ReplayWindow should be 0
   223  	require.Equal(t, 0, state.ReplayWindow)
   224  
   225  	err = DeleteXFRM(log)
   226  	require.NoError(t, err)
   227  
   228  	_, aeadKey, err := decodeIPSecKey("44434241343332312423222114131211f4f3f2f1")
   229  	require.NoError(t, err)
   230  	key = &ipSecKey{
   231  		Spi:   1,
   232  		ReqID: 1,
   233  		Aead:  &netlink.XfrmStateAlgo{Name: "rfc4106(gcm(aes))", Key: aeadKey, ICVLen: 128},
   234  		Crypt: nil,
   235  		Auth:  nil,
   236  	}
   237  
   238  	ipSecKeysGlobal["1.1.3.4"] = key
   239  	ipSecKeysGlobal["1.2.3.4"] = key
   240  	ipSecKeysGlobal[""] = key
   241  
   242  	_, err = UpsertIPsecEndpoint(log, local, remote, local.IP, remote.IP, 0, "remote-boot-id", IPSecDirBoth, false, false, DefaultReqID)
   243  	require.NoError(t, err)
   244  
   245  	// Assert additional rule when tunneling is enabled is inserted
   246  	_, err = UpsertIPsecEndpoint(log, local, remote, local.IP, remote.IP, 0, "remote-boot-id", IPSecDirBoth, false, false, DefaultReqID)
   247  	require.NoError(t, err)
   248  	toProxyPolicy, err := netlink.XfrmPolicyGet(&netlink.XfrmPolicy{
   249  		Src: remote,
   250  		Dst: local,
   251  		Dir: netlink.XFRM_DIR_IN,
   252  		Mark: &netlink.XfrmMark{
   253  			Mask:  linux_defaults.IPsecMarkBitMask,
   254  			Value: linux_defaults.RouteMarkToProxy,
   255  		},
   256  	})
   257  	require.NoError(t, err)
   258  	require.NotNil(t, toProxyPolicy)
   259  }
   260  
   261  func TestUpsertIPSecKeyMissing(t *testing.T) {
   262  	log := setupIPSecSuitePrivileged(t)
   263  
   264  	_, local, err := net.ParseCIDR("1.1.3.4/16")
   265  	require.NoError(t, err)
   266  	_, remote, err := net.ParseCIDR("1.2.3.4/16")
   267  	require.NoError(t, err)
   268  
   269  	_, err = UpsertIPsecEndpoint(log, local, remote, local.IP, remote.IP, 0, "remote-boot-id", IPSecDirBoth, false, false, DefaultReqID)
   270  	require.ErrorContains(t, err, "unable to replace local state: IPSec key missing")
   271  }
   272  
   273  func TestUpdateExistingIPSecEndpoint(t *testing.T) {
   274  	log := setupIPSecSuitePrivileged(t)
   275  
   276  	_, local, err := net.ParseCIDR("1.1.3.4/16")
   277  	require.NoError(t, err)
   278  	_, remote, err := net.ParseCIDR("1.2.3.4/16")
   279  	require.NoError(t, err)
   280  
   281  	_, authKey, err := decodeIPSecKey("0123456789abcdef0123456789abcdef")
   282  	require.NoError(t, err)
   283  	_, cryptKey, err := decodeIPSecKey("0123456789abcdef0123456789abcdef")
   284  	require.NoError(t, err)
   285  	key := &ipSecKey{
   286  		Spi:   1,
   287  		ReqID: 1,
   288  		Auth:  &netlink.XfrmStateAlgo{Name: "hmac(sha256)", Key: authKey},
   289  		Crypt: &netlink.XfrmStateAlgo{Name: "cbc(aes)", Key: cryptKey},
   290  	}
   291  
   292  	ipSecKeysGlobal["1.1.3.4"] = key
   293  	ipSecKeysGlobal["1.2.3.4"] = key
   294  	ipSecKeysGlobal[""] = key
   295  
   296  	_, err = UpsertIPsecEndpoint(log, local, remote, local.IP, remote.IP, 0, "remote-boot-id", IPSecDirBoth, false, false, DefaultReqID)
   297  	require.NoError(t, err)
   298  
   299  	// test updateExisting (xfrm delete + add)
   300  	_, err = UpsertIPsecEndpoint(log, local, remote, local.IP, remote.IP, 0, "remote-boot-id", IPSecDirBoth, false, true, DefaultReqID)
   301  	require.NoError(t, err)
   302  }