summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLorenzo Colitti <lorenzo@google.com>2017-05-10 21:43:54 +0900
committerLorenzo Colitti <lorenzo@google.com>2017-05-11 17:37:44 +0900
commitb1db0fad158c83a20a3a7ad3edc6cc744827a7d9 (patch)
tree555e2c425785fa198c2679025ea189eb6cf99eaf
parent2bff1d53542906a257a133366337a512c4408979 (diff)
downloadtests-b1db0fad158c83a20a3a7ad3edc6cc744827a7d9.tar.gz
Add code to use the PF_KEY interface.
Test: all_tests.sh passes on common and device kernels Bug: 34114242 Change-Id: I353a14aa816531718854311a0e1868ff3d82bce5
-rwxr-xr-xnet/test/pf_key.py327
-rwxr-xr-xnet/test/pf_key_test.py98
-rwxr-xr-xnet/test/run_net_test.sh2
3 files changed, 426 insertions, 1 deletions
diff --git a/net/test/pf_key.py b/net/test/pf_key.py
new file mode 100755
index 0000000..174ceb7
--- /dev/null
+++ b/net/test/pf_key.py
@@ -0,0 +1,327 @@
+#!/usr/bin/python
+#
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Partial implementation of the PFKEYv2 interface."""
+
+# pylint: disable=g-bad-todo,bad-whitespace
+
+import os
+from socket import * # pylint: disable=wildcard-import
+import sys
+
+import cstruct
+import net_test
+
+
+# AF_KEY socket type. See include/linux/socket.h.
+AF_KEY = 15
+
+# PFKEYv2 constants. See include/uapi/linux/pfkeyv2.h.
+PF_KEY_V2 = 2
+
+# IPsec constants. See include/uapi/linux/ipsec.h.
+IPSEC_MODE_ANY = 0
+IPSEC_MODE_TRANSPORT = 1
+IPSEC_MODE_TUNNEL = 2
+IPSEC_MODE_BEET = 3
+
+# Operation types.
+SADB_ADD = 3
+SADB_DELETE = 4
+SADB_DUMP = 10
+
+# SA types.
+SADB_TYPE_UNSPEC = 0
+SADB_TYPE_AH = 2
+SADB_TYPE_ESP = 3
+
+# SA states.
+SADB_SASTATE_LARVAL = 0
+SADB_SASTATE_MATURE = 1
+SADB_SASTATE_DYING = 2
+SADB_SASTATE_DEAD = 3
+
+# Authentication algorithms.
+SADB_AALG_NONE = 0
+SADB_AALG_MD5HMAC = 2
+SADB_AALG_SHA1HMAC = 3
+SADB_X_AALG_SHA2_256HMAC = 5
+SADB_X_AALG_SHA2_384HMAC = 6
+SADB_X_AALG_SHA2_512HMAC = 7
+SADB_X_AALG_RIPEMD160HMAC = 8
+SADB_X_AALG_AES_XCBC_MAC = 9
+SADB_X_AALG_NULL = 251
+
+# Encryption algorithms.
+SADB_EALG_NONE = 0
+SADB_EALG_DESCBC = 2
+SADB_EALG_3DESCBC = 3
+SADB_X_EALG_CASTCBC = 6
+SADB_X_EALG_BLOWFISHCBC = 7
+SADB_EALG_NULL = 11
+SADB_X_EALG_AESCBC = 12
+SADB_X_EALG_AESCTR = 13
+SADB_X_EALG_AES_CCM_ICV8 = 14
+SADB_X_EALG_AES_CCM_ICV12 = 15
+SADB_X_EALG_AES_CCM_ICV16 = 16
+SADB_X_EALG_AES_GCM_ICV8 = 18
+SADB_X_EALG_AES_GCM_ICV12 = 19
+SADB_X_EALG_AES_GCM_ICV16 = 20
+SADB_X_EALG_CAMELLIACBC = 22
+SADB_X_EALG_NULL_AES_GMAC = 23
+SADB_X_EALG_SERPENTCBC = 252
+SADB_X_EALG_TWOFISHCBC = 253
+
+# Extension Header values.
+SADB_EXT_RESERVED = 0
+SADB_EXT_SA = 1
+SADB_EXT_LIFETIME_CURRENT = 2
+SADB_EXT_LIFETIME_HARD = 3
+SADB_EXT_LIFETIME_SOFT = 4
+SADB_EXT_ADDRESS_SRC = 5
+SADB_EXT_ADDRESS_DST = 6
+SADB_EXT_ADDRESS_PROXY = 7
+SADB_EXT_KEY_AUTH = 8
+SADB_EXT_KEY_ENCRYPT = 9
+SADB_EXT_IDENTITY_SRC = 10
+SADB_EXT_IDENTITY_DST = 11
+SADB_EXT_SENSITIVITY = 12
+SADB_EXT_PROPOSAL = 13
+SADB_EXT_SUPPORTED_AUTH = 14
+SADB_EXT_SUPPORTED_ENCRYPT = 15
+SADB_EXT_SPIRANGE = 16
+SADB_X_EXT_KMPRIVATE = 17
+SADB_X_EXT_POLICY = 18
+SADB_X_EXT_SA2 = 19
+SADB_X_EXT_NAT_T_TYPE = 20
+SADB_X_EXT_NAT_T_SPORT = 21
+SADB_X_EXT_NAT_T_DPORT = 22
+SADB_X_EXT_NAT_T_OA = 23
+SADB_X_EXT_SEC_CTX = 24
+SADB_X_EXT_KMADDRESS = 25
+SADB_X_EXT_FILTER = 26
+
+# Data structure formats.
+# These aren't constants, they're classes. So, pylint: disable=invalid-name
+SadbMsg = cstruct.Struct(
+ "SadbMsg", "=BBBBHHII", "version type errno satype len reserved seq pid")
+
+# Fake struct containing the common beginning of all extension structs.
+SadbExt = cstruct.Struct("SadbExt", "=HH", "len exttype")
+
+SadbSa = cstruct.Struct(
+ "SadbSa", "=IBBBBI", "spi replay state auth encrypt flags")
+
+SadbLifetime = cstruct.Struct(
+ "SadbLifetime", "=IQQQ", "allocations bytes addtime usetime")
+
+SadbAddress = cstruct.Struct("SadbAddress", "=BB2x", "proto prefixlen")
+
+SadbKey = cstruct.Struct("SadbKey", "=H2x", "bits")
+
+SadbXSa2 = cstruct.Struct("SadbXSa2", "=B3xII", "mode sequence reqid")
+
+SadbXNatTType = cstruct.Struct("SadbXNatTType", "=B3x", "type")
+
+SadbXNatTPort = cstruct.Struct("SadbXNatTPort", "!H2x", "port")
+
+
+def _GetConstantName(value, prefix):
+ """Translates a number to a constant of the same value in this file."""
+ thismodule = sys.modules[__name__]
+ # Match shorter constant names first. This allows us to match SADB_DUMP and
+ # instead of, say, SADB_EXT_LIFETIME_HARD if we pass in a prefix of "SADB_"
+ # and a value of 3, and match SADB_EXT_LIFETIME_HARD just by specifying
+ # a longer prefix.
+ for name in sorted(dir(thismodule), key=len):
+ if (name.startswith(prefix) and
+ name.isupper() and getattr(thismodule, name) == value):
+ return name
+ return value
+
+
+def _GetMultiConstantName(value, prefixes):
+ for prefix in prefixes:
+ name = _GetConstantName(value, prefix)
+ try:
+ int(name)
+ continue
+ except ValueError:
+ return name
+
+
+# Converts extension blobs to a (name, struct, attrs) tuple.
+def ParseExtension(exttype, data):
+ struct_type = None
+ if exttype == SADB_EXT_SA:
+ struct_type = SadbSa
+ elif exttype in [SADB_EXT_LIFETIME_CURRENT, SADB_EXT_LIFETIME_HARD,
+ SADB_EXT_LIFETIME_SOFT]:
+ struct_type = SadbLifetime
+ elif exttype in [SADB_EXT_ADDRESS_SRC, SADB_EXT_ADDRESS_DST,
+ SADB_EXT_ADDRESS_PROXY]:
+ struct_type = SadbAddress
+ elif exttype in [SADB_EXT_KEY_AUTH, SADB_EXT_KEY_ENCRYPT]:
+ struct_type = SadbKey
+ elif exttype == SADB_X_EXT_SA2:
+ struct_type = SadbXSa2
+ elif exttype == SADB_X_EXT_NAT_T_TYPE:
+ struct_type = SadbXNatTType
+ elif exttype in [SADB_X_EXT_NAT_T_SPORT, SADB_X_EXT_NAT_T_DPORT]:
+ struct_type = SadbXNatTPort
+
+ if struct_type:
+ ext, attrs = cstruct.Read(data, struct_type)
+ else:
+ ext, attrs, = data, ""
+
+ return exttype, ext, attrs
+
+class PfKey(object):
+
+ """PF_KEY interface to kernel IPsec implementation."""
+
+ def __init__(self):
+ self.sock = socket(AF_KEY, SOCK_RAW, PF_KEY_V2)
+ net_test.SetNonBlocking(self.sock)
+ self.seq = 0
+
+ def Recv(self):
+ reply = self.sock.recv(4096)
+ msg = SadbMsg(reply)
+ # print "RECV:", self.DecodeSadbMsg(msg)
+ if msg.errno != 0:
+ raise OSError(msg.errno, os.strerror(msg.errno))
+ return reply
+
+ def SendAndRecv(self, msg, extensions):
+ self.seq += 1
+ msg.seq = self.seq
+ msg.pid = os.getpid()
+ msg.len = (len(SadbMsg) + len(extensions)) / 8
+ self.sock.send(msg.Pack() + extensions)
+ # print "SEND:", self.DecodeSadbMsg(msg)
+ return self.Recv()
+
+ def PackPfKeyExtensions(self, extlist):
+ extensions = ""
+ for exttype, extstruct, attrs in extlist:
+ extdata = extstruct.Pack()
+ ext = SadbExt(((len(extdata) + len(SadbExt) + len(attrs)) / 8, exttype))
+ extensions += ext.Pack() + extdata + attrs
+ return extensions
+
+ def MakeSadbMsg(self, msgtype, satype):
+ # errno is 0. seq, pid and len are filled in by SendAndRecv().
+ return SadbMsg((PF_KEY_V2, msgtype, 0, satype, 0, 0, 0, 0))
+
+ def MakeSadbExtAddr(self, exttype, addr):
+ prefixlen = {AF_INET: 32, AF_INET6: 128}[addr.family]
+ packed = addr.Pack()
+ padbytes = (len(SadbExt) + len(SadbAddress) + len(packed)) % 8
+ packed += "\x00" * padbytes
+ return (exttype, SadbAddress((0, prefixlen)), packed)
+
+ def AddSa(self, src, dst, spi, satype, mode, reqid, encryption,
+ encryption_key, auth, auth_key):
+ """Adds a security association."""
+ msg = self.MakeSadbMsg(SADB_ADD, satype)
+ replay = 4
+ extlist = [
+ (SADB_EXT_SA, SadbSa((spi, replay, SADB_SASTATE_MATURE,
+ auth, encryption, 0)), ""),
+ self.MakeSadbExtAddr(SADB_EXT_ADDRESS_SRC, src),
+ self.MakeSadbExtAddr(SADB_EXT_ADDRESS_DST, dst),
+ (SADB_X_EXT_SA2, SadbXSa2((mode, 0, reqid)), ""),
+ (SADB_EXT_KEY_AUTH, SadbKey((len(auth_key) * 8,)), auth_key),
+ (SADB_EXT_KEY_ENCRYPT, SadbKey((len(encryption_key) * 8,)),
+ encryption_key)
+ ]
+ self.SendAndRecv(msg, self.PackPfKeyExtensions(extlist))
+
+ def DelSa(self, src, dst, spi, satype):
+ """Deletes a security association."""
+ msg = self.MakeSadbMsg(SADB_DELETE, satype)
+ extlist = [
+ (SADB_EXT_SA, SadbSa((spi, 4, SADB_SASTATE_MATURE, 0, 0, 0)), ""),
+ self.MakeSadbExtAddr(SADB_EXT_ADDRESS_SRC, src),
+ self.MakeSadbExtAddr(SADB_EXT_ADDRESS_DST, dst),
+ ]
+ self.SendAndRecv(msg, self.PackPfKeyExtensions(extlist))
+
+ @staticmethod
+ def DecodeSadbMsg(msg):
+ msgtype = _GetConstantName(msg.type, "SADB_")
+ satype = _GetConstantName(msg.satype, "SADB_TYPE_")
+ return ("SadbMsg(version=%d, type=%s, errno=%d, satype=%s, "
+ "len=%d, reserved=%d, seq=%d, pid=%d)" % (
+ msg.version, msgtype, msg.errno, satype, msg.len,
+ msg.reserved, msg.seq, msg.pid))
+
+ @staticmethod
+ def DecodeSadbSa(sa):
+ state = _GetConstantName(sa.state, "SADB_SASTATE_")
+ auth = _GetMultiConstantName(sa.auth, ["SADB_AALG_", "SADB_X_AALG"])
+ encrypt = _GetMultiConstantName(sa.encrypt, ["SADB_EALG_",
+ "SADB_X_EALG_"])
+ return ("SadbSa(spi=%x, replay=%d, state=%s, "
+ "auth=%s, encrypt=%s, flags=%x)" % (
+ sa.spi, sa.replay, state, auth, encrypt, sa.flags))
+
+ @staticmethod
+ def ExtensionsLength(msg, struct_type):
+ return (msg.len * 8) - len(struct_type)
+
+ @staticmethod
+ def ParseExtensions(data):
+ """Parses the extensions in a SADB message."""
+ extensions = []
+ while data:
+ ext, data = cstruct.Read(data, SadbExt)
+ datalen = PfKey.ExtensionsLength(ext, SadbExt)
+ extdata, data = data[:datalen], data[datalen:]
+ extensions.append(ParseExtension(ext.exttype, extdata))
+ return extensions
+
+ def DumpSaInfo(self):
+ """Returns a list of (SadbMsg, [(extension, attr), ...], ...) tuples."""
+ dump = []
+ msg = self.MakeSadbMsg(SADB_DUMP, SADB_TYPE_UNSPEC)
+ received = self.SendAndRecv(msg, "")
+ while received:
+ msg, data = cstruct.Read(received, SadbMsg)
+ extlen = self.ExtensionsLength(msg, SadbMsg)
+ extensions, data = data[:extlen], data[extlen:]
+ dump.append((msg, self.ParseExtensions(extensions)))
+ if msg.seq == 0: # End of dump.
+ break
+ received = self.Recv()
+ return dump
+
+ def PrintSaInfos(self, dump):
+ for msg, extensions in dump:
+ print self.DecodeSadbMsg(msg)
+ for exttype, ext, attrs in extensions:
+ exttype = _GetMultiConstantName(exttype, ["SADB_EXT", "SADB_X_EXT"])
+ if exttype == SADB_EXT_SA:
+ print " ", exttype, self.DecodeSadbSa(ext), attrs.encode("hex")
+ print " ", exttype, ext, attrs.encode("hex")
+ print
+
+
+if __name__ == "__main__":
+ p = PfKey()
+ p.DumpSaInfo()
diff --git a/net/test/pf_key_test.py b/net/test/pf_key_test.py
new file mode 100755
index 0000000..52e9ee9
--- /dev/null
+++ b/net/test/pf_key_test.py
@@ -0,0 +1,98 @@
+#!/usr/bin/python
+#
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# pylint: disable=g-bad-todo,g-bad-file-header,wildcard-import
+from socket import *
+import unittest
+
+import csocket
+import pf_key
+import xfrm
+
+ENCRYPTION_KEY = ("308146eb3bd84b044573d60f5a5fd159"
+ "57c7d4fe567a2120f35bae0f9869ec22".decode("hex"))
+AUTH_KEY = "af442892cdcd0ef650e9c299f9a8436a".decode("hex")
+
+
+class PfKeyTest(unittest.TestCase):
+
+ def setUp(self):
+ self.pf_key = pf_key.PfKey()
+ self.xfrm = xfrm.Xfrm()
+
+ def testAddDelSa(self):
+ src4 = csocket.Sockaddr(("192.0.2.1", 0))
+ dst4 = csocket.Sockaddr(("192.0.2.2", 1))
+ self.pf_key.AddSa(src4, dst4, 0xdeadbeef, pf_key.SADB_TYPE_ESP,
+ pf_key.IPSEC_MODE_TRANSPORT, 54321,
+ pf_key.SADB_X_EALG_AESCBC, ENCRYPTION_KEY,
+ pf_key.SADB_X_AALG_SHA2_256HMAC, ENCRYPTION_KEY)
+
+ src6 = csocket.Sockaddr(("2001:db8::1", 0))
+ dst6 = csocket.Sockaddr(("2001:db8::2", 0))
+ self.pf_key.AddSa(src6, dst6, 0xbeefdead, pf_key.SADB_TYPE_ESP,
+ pf_key.IPSEC_MODE_TRANSPORT, 12345,
+ pf_key.SADB_X_EALG_AESCBC, ENCRYPTION_KEY,
+ pf_key.SADB_X_AALG_SHA2_256HMAC, ENCRYPTION_KEY)
+
+ sainfos = self.xfrm.DumpSaInfo()
+ self.assertEquals(2, len(sainfos))
+ state4, attrs4 = [(s, a) for s, a in sainfos if s.family == AF_INET][0]
+ state6, attrs6 = [(s, a) for s, a in sainfos if s.family == AF_INET6][0]
+
+ pfkey_sainfos = self.pf_key.DumpSaInfo()
+ self.assertEquals(2, len(pfkey_sainfos))
+ self.assertTrue(all(msg.satype == pf_key.SDB_TYPE_ESP)
+ for msg, _ in pfkey_sainfos)
+
+ self.assertEquals(xfrm.IPPROTO_ESP, state4.id.proto)
+ self.assertEquals(xfrm.IPPROTO_ESP, state6.id.proto)
+ self.assertEquals(54321, state4.reqid)
+ self.assertEquals(12345, state6.reqid)
+ self.assertEquals(0xdeadbeef, state4.id.spi)
+ self.assertEquals(0xbeefdead, state6.id.spi)
+
+ self.assertEquals(xfrm.PaddedAddress("192.0.2.1"), state4.saddr)
+ self.assertEquals(xfrm.PaddedAddress("192.0.2.2"), state4.id.daddr)
+ self.assertEquals(xfrm.PaddedAddress("2001:db8::1"), state6.saddr)
+ self.assertEquals(xfrm.PaddedAddress("2001:db8::2"), state6.id.daddr)
+
+ # The algorithm names are null-terminated, but after that contain garbage.
+ # Kernel bug?
+ aes_name = "cbc(aes)\x00"
+ sha256_name = "hmac(sha256)\x00"
+ self.assertTrue(attrs4["XFRMA_ALG_CRYPT"].name.startswith(aes_name))
+ self.assertTrue(attrs6["XFRMA_ALG_CRYPT"].name.startswith(aes_name))
+ self.assertTrue(attrs4["XFRMA_ALG_AUTH"].name.startswith(sha256_name))
+ self.assertTrue(attrs6["XFRMA_ALG_AUTH"].name.startswith(sha256_name))
+
+ self.assertEquals(256, attrs4["XFRMA_ALG_CRYPT"].key_len)
+ self.assertEquals(256, attrs4["XFRMA_ALG_CRYPT"].key_len)
+ self.assertEquals(256, attrs6["XFRMA_ALG_AUTH"].key_len)
+ self.assertEquals(256, attrs6["XFRMA_ALG_AUTH"].key_len)
+ self.assertEquals(256, attrs6["XFRMA_ALG_AUTH_TRUNC"].key_len)
+ self.assertEquals(256, attrs6["XFRMA_ALG_AUTH_TRUNC"].key_len)
+
+ # TODO: check truncation lengths, once they are correct.
+
+ self.pf_key.DelSa(src4, dst4, 0xdeadbeef, pf_key.SADB_TYPE_ESP)
+ self.assertEquals(1, len(self.xfrm.DumpSaInfo()))
+ self.pf_key.DelSa(src6, dst6, 0xbeefdead, pf_key.SADB_TYPE_ESP)
+ self.assertEquals(0, len(self.xfrm.DumpSaInfo()))
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/net/test/run_net_test.sh b/net/test/run_net_test.sh
index ad25fe3..8d60bea 100755
--- a/net/test/run_net_test.sh
+++ b/net/test/run_net_test.sh
@@ -21,7 +21,7 @@ OPTIONS="$OPTIONS CONFIG_INET_UDP_DIAG CONFIG_INET_DIAG_DESTROY"
OPTIONS="$OPTIONS IP_SCTP INET_SCTP_DIAG"
OPTIONS="$OPTIONS CONFIG_IP_NF_TARGET_REJECT CONFIG_IP_NF_TARGET_REJECT_SKERR"
OPTIONS="$OPTIONS CONFIG_IP6_NF_TARGET_REJECT CONFIG_IP6_NF_TARGET_REJECT_SKERR"
-OPTIONS="$OPTIONS BPF_SYSCALL XFRM_USER CRYPTO_CBC CRYPTO_CTR"
+OPTIONS="$OPTIONS BPF_SYSCALL NET_KEY XFRM_USER CRYPTO_CBC CRYPTO_CTR"
OPTIONS="$OPTIONS CRYPTO_HMAC CRYPTO_AES CRYPTO_SHA1 CRYPTO_SHA256 CRYPTO_SHA12"
OPTIONS="$OPTIONS CRYPTO_USER INET_AH INET_ESP INET_XFRM_MODE"
OPTIONS="$OPTIONS TRANSPORT INET_XFRM_MODE_TUNNEL INET6_AH INET6_ESP"