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 }