Home | History | Annotate | Download | only in test
      1 #!/usr/bin/python
      2 #
      3 # Copyright 2016 The Android Open Source Project
      4 #
      5 # Licensed under the Apache License, Version 2.0 (the "License");
      6 # you may not use this file except in compliance with the License.
      7 # You may obtain a copy of the License at
      8 #
      9 # http://www.apache.org/licenses/LICENSE-2.0
     10 #
     11 # Unless required by applicable law or agreed to in writing, software
     12 # distributed under the License is distributed on an "AS IS" BASIS,
     13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14 # See the License for the specific language governing permissions and
     15 # limitations under the License.
     16 
     17 """Partial implementation of xfrm netlink code and socket options."""
     18 
     19 # pylint: disable=g-bad-todo
     20 
     21 import os
     22 from socket import *  # pylint: disable=wildcard-import
     23 
     24 import cstruct
     25 import netlink
     26 
     27 # Base netlink constants. See include/uapi/linux/netlink.h.
     28 NETLINK_XFRM = 6
     29 
     30 # Netlink constants. See include/uapi/linux/xfrm.h.
     31 # Message types.
     32 XFRM_MSG_NEWSA = 16
     33 XFRM_MSG_DELSA = 17
     34 XFRM_MSG_GETSA = 18
     35 XFRM_MSG_NEWPOLICY = 19
     36 XFRM_MSG_DELPOLICY = 20
     37 XFRM_MSG_GETPOLICY = 21
     38 XFRM_MSG_ALLOCSPI = 22
     39 XFRM_MSG_ACQUIRE = 23
     40 XFRM_MSG_EXPIRE = 24
     41 XFRM_MSG_UPDPOLICY = 25
     42 XFRM_MSG_UPDSA = 26
     43 XFRM_MSG_POLEXPIRE = 27
     44 XFRM_MSG_FLUSHSA = 28
     45 XFRM_MSG_FLUSHPOLICY = 29
     46 XFRM_MSG_NEWAE = 30
     47 XFRM_MSG_GETAE = 31
     48 XFRM_MSG_REPORT = 32
     49 XFRM_MSG_MIGRATE = 33
     50 XFRM_MSG_NEWSADINFO = 34
     51 XFRM_MSG_GETSADINFO = 35
     52 XFRM_MSG_NEWSPDINFO = 36
     53 XFRM_MSG_GETSPDINFO = 37
     54 XFRM_MSG_MAPPING = 38
     55 
     56 # Attributes.
     57 XFRMA_UNSPEC = 0
     58 XFRMA_ALG_AUTH = 1
     59 XFRMA_ALG_CRYPT = 2
     60 XFRMA_ALG_COMP = 3
     61 XFRMA_ENCAP = 4
     62 XFRMA_TMPL = 5
     63 XFRMA_SA = 6
     64 XFRMA_POLICY = 7
     65 XFRMA_SEC_CTX = 8
     66 XFRMA_LTIME_VAL = 9
     67 XFRMA_REPLAY_VAL = 10
     68 XFRMA_REPLAY_THRESH = 11
     69 XFRMA_ETIMER_THRESH = 12
     70 XFRMA_SRCADDR = 13
     71 XFRMA_COADDR = 14
     72 XFRMA_LASTUSED = 15
     73 XFRMA_POLICY_TYPE = 16
     74 XFRMA_MIGRATE = 17
     75 XFRMA_ALG_AEAD = 18
     76 XFRMA_KMADDRESS = 19
     77 XFRMA_ALG_AUTH_TRUNC = 20
     78 XFRMA_MARK = 21
     79 XFRMA_TFCPAD = 22
     80 XFRMA_REPLAY_ESN_VAL = 23
     81 XFRMA_SA_EXTRA_FLAGS = 24
     82 XFRMA_PROTO = 25
     83 XFRMA_ADDRESS_FILTER = 26
     84 XFRMA_PAD = 27
     85 
     86 # Other netlink constants. See include/uapi/linux/xfrm.h.
     87 
     88 # Directions.
     89 XFRM_POLICY_IN = 0
     90 XFRM_POLICY_OUT = 1
     91 XFRM_POLICY_FWD = 2
     92 XFRM_POLICY_MASK = 3
     93 
     94 # Policy sharing.
     95 XFRM_SHARE_ANY     = 0  #  /* No limitations */
     96 XFRM_SHARE_SESSION = 1  #  /* For this session only */
     97 XFRM_SHARE_USER    = 2  #  /* For this user only */
     98 XFRM_SHARE_UNIQUE  = 3  #  /* Use once */
     99 
    100 # Modes.
    101 XFRM_MODE_TRANSPORT = 0
    102 XFRM_MODE_TUNNEL = 1
    103 XFRM_MODE_ROUTEOPTIMIZATION = 2
    104 XFRM_MODE_IN_TRIGGER = 3
    105 XFRM_MODE_BEET = 4
    106 XFRM_MODE_MAX = 5
    107 
    108 # Actions.
    109 XFRM_POLICY_ALLOW = 0
    110 XFRM_POLICY_BLOCK = 1
    111 
    112 # Flags.
    113 XFRM_POLICY_LOCALOK = 1
    114 XFRM_POLICY_ICMP = 2
    115 
    116 # Data structure formats.
    117 # These aren't constants, they're classes. So, pylint: disable=invalid-name
    118 XfrmSelector = cstruct.Struct(
    119     "XfrmSelector", "=16s16sHHHHHBBBxxxiI",
    120     "daddr saddr dport dport_mask sport sport_mask "
    121     "family prefixlen_d prefixlen_s proto ifindex user")
    122 
    123 XfrmLifetimeCfg = cstruct.Struct(
    124     "XfrmLifetimeCfg", "=QQQQQQQQ",
    125     "soft_byte hard_byte soft_packet hard_packet "
    126     "soft_add_expires hard_add_expires soft_use_expires hard_use_expires")
    127 
    128 XfrmLifetimeCur = cstruct.Struct(
    129     "XfrmLifetimeCur", "=QQQQ", "bytes packets add_time use_time")
    130 
    131 XfrmAlgo = cstruct.Struct("XfrmAlgo", "=64AI", "name key_len")
    132 
    133 XfrmAlgoAuth = cstruct.Struct("XfrmAlgoAuth", "=64AII",
    134                               "name key_len trunc_len")
    135 
    136 XfrmAlgoAead = cstruct.Struct("XfrmAlgoAead", "=64AII", "name key_len icv_len")
    137 
    138 XfrmStats = cstruct.Struct(
    139     "XfrmStats", "=III", "replay_window replay integrity_failed")
    140 
    141 XfrmId = cstruct.Struct("XfrmId", "=16sIBxxx", "daddr spi proto")
    142 
    143 XfrmUserTmpl = cstruct.Struct(
    144     "XfrmUserTmpl", "=SHxx16sIBBBxIII",
    145     "id family saddr reqid mode share optional aalgos ealgos calgos",
    146     [XfrmId])
    147 
    148 XfrmEncapTmpl = cstruct.Struct(
    149     "XfrmEncapTmpl", "=HHHxx16s", "type sport dport oa")
    150 
    151 XfrmUsersaInfo = cstruct.Struct(
    152     "XfrmUsersaInfo", "=SS16sSSSIIHBBB7x",
    153     "sel id saddr lft curlft stats seq reqid family mode replay_window flags",
    154     [XfrmSelector, XfrmId, XfrmLifetimeCfg, XfrmLifetimeCur, XfrmStats])
    155 
    156 XfrmUserSpiInfo = cstruct.Struct(
    157     "XfrmUserSpiInfo", "=SII", "info min max", [XfrmUsersaInfo])
    158 
    159 XfrmUsersaId = cstruct.Struct(
    160     "XfrmUsersaInfo", "=16sIHBx", "daddr spi family proto")
    161 
    162 XfrmUserpolicyInfo = cstruct.Struct(
    163     "XfrmUserpolicyInfo", "=SSSIIBBBBxxxx",
    164     "sel lft curlft priority index dir action flags share",
    165     [XfrmSelector, XfrmLifetimeCfg, XfrmLifetimeCur])
    166 
    167 XfrmUsersaFlush = cstruct.Struct("XfrmUsersaFlush", "=B", "proto")
    168 
    169 # Socket options. See include/uapi/linux/in.h.
    170 IP_IPSEC_POLICY = 16
    171 IP_XFRM_POLICY = 17
    172 IPV6_IPSEC_POLICY = 34
    173 IPV6_XFRM_POLICY = 35
    174 
    175 # UDP encapsulation constants. See include/uapi/linux/udp.h.
    176 UDP_ENCAP = 100
    177 UDP_ENCAP_ESPINUDP_NON_IKE = 1
    178 UDP_ENCAP_ESPINUDP = 2
    179 
    180 _INF = 2 ** 64 -1
    181 NO_LIFETIME_CFG = XfrmLifetimeCfg((_INF, _INF, _INF, _INF, 0, 0, 0, 0))
    182 NO_LIFETIME_CUR = "\x00" * len(XfrmLifetimeCur)
    183 
    184 # IPsec constants.
    185 IPSEC_PROTO_ANY	= 255
    186 
    187 
    188 def RawAddress(addr):
    189   """Converts an IP address string to binary format."""
    190   family = AF_INET6 if ":" in addr else AF_INET
    191   return inet_pton(family, addr)
    192 
    193 
    194 def PaddedAddress(addr):
    195   """Converts an IP address string to binary format for InetDiagSockId."""
    196   padded = RawAddress(addr)
    197   if len(padded) < 16:
    198     padded += "\x00" * (16 - len(padded))
    199   return padded
    200 
    201 
    202 class Xfrm(netlink.NetlinkSocket):
    203   """Netlink interface to xfrm."""
    204 
    205   FAMILY = NETLINK_XFRM
    206   DEBUG = False
    207 
    208   def __init__(self):
    209     super(Xfrm, self).__init__()
    210 
    211   def _GetConstantName(self, value, prefix):
    212     return super(Xfrm, self)._GetConstantName(__name__, value, prefix)
    213 
    214   def MaybeDebugCommand(self, command, flags, data):
    215     if "ALL" not in self.NL_DEBUG and "XFRM" not in self.NL_DEBUG:
    216       return
    217 
    218     if command == XFRM_MSG_GETSA:
    219       if flags & netlink.NLM_F_DUMP:
    220         struct_type = XfrmUsersaInfo
    221       else:
    222         struct_type = XfrmUsersaId
    223     elif command == XFRM_MSG_DELSA:
    224       struct_type = XfrmUsersaId
    225     elif command == XFRM_MSG_ALLOCSPI:
    226       struct_type = XfrmUserSpiInfo
    227     else:
    228       struct_type = None
    229 
    230     cmdname = self._GetConstantName(command, "XFRM_MSG_")
    231     if struct_type:
    232       print "%s %s" % (cmdname, str(self._ParseNLMsg(data, struct_type)))
    233     else:
    234       print "%s" % cmdname
    235 
    236   def _Decode(self, command, unused_msg, nla_type, nla_data):
    237     """Decodes netlink attributes to Python types."""
    238     name = self._GetConstantName(nla_type, "XFRMA_")
    239 
    240     if name in ["XFRMA_ALG_CRYPT", "XFRMA_ALG_AUTH"]:
    241       data = cstruct.Read(nla_data, XfrmAlgo)[0]
    242     elif name == "XFRMA_ALG_AUTH_TRUNC":
    243       data = cstruct.Read(nla_data, XfrmAlgoAuth)[0]
    244     elif name == "XFRMA_ENCAP":
    245       data = cstruct.Read(nla_data, XfrmEncapTmpl)[0]
    246     else:
    247       data = nla_data
    248 
    249     return name, data
    250 
    251   def AddSaInfo(self, selector, xfrm_id, saddr, lifetimes, reqid, family, mode,
    252                 replay_window, flags, nlattrs):
    253     # The kernel ignores these on input.
    254     cur = "\x00" * len(XfrmLifetimeCur)
    255     stats = "\x00" * len(XfrmStats)
    256     seq = 0
    257     sa = XfrmUsersaInfo((selector, xfrm_id, saddr, lifetimes, cur, stats, seq,
    258                          reqid, family, mode, replay_window, flags))
    259     msg = sa.Pack() + nlattrs
    260     flags = netlink.NLM_F_REQUEST | netlink.NLM_F_ACK
    261     self._SendNlRequest(XFRM_MSG_NEWSA, msg, flags)
    262 
    263   def AddMinimalSaInfo(self, src, dst, spi, proto, mode, reqid,
    264                        encryption, encryption_key,
    265                        auth_trunc, auth_trunc_key, encap):
    266     selector = XfrmSelector("\x00" * len(XfrmSelector))
    267     xfrm_id = XfrmId((PaddedAddress(dst), spi, proto))
    268     family = AF_INET6 if ":" in dst else AF_INET
    269     nlattrs = self._NlAttr(XFRMA_ALG_CRYPT,
    270                            encryption.Pack() + encryption_key)
    271     nlattrs += self._NlAttr(XFRMA_ALG_AUTH_TRUNC,
    272                             auth_trunc.Pack() + auth_trunc_key)
    273     if encap is not None:
    274       nlattrs += self._NlAttr(XFRMA_ENCAP, encap.Pack())
    275     self.AddSaInfo(selector, xfrm_id, PaddedAddress(src), NO_LIFETIME_CFG,
    276                    reqid, family, mode, 4, 0, nlattrs)
    277 
    278   def DeleteSaInfo(self, daddr, spi, proto):
    279     # TODO: deletes take a mark as well.
    280     family = AF_INET6 if ":" in daddr else AF_INET
    281     usersa_id = XfrmUsersaId((PaddedAddress(daddr), spi, family, proto))
    282     flags = netlink.NLM_F_REQUEST | netlink.NLM_F_ACK
    283     self._SendNlRequest(XFRM_MSG_DELSA, usersa_id.Pack(), flags)
    284 
    285   def AllocSpi(self, dst, proto, min_spi, max_spi):
    286     """Allocate (reserve) an SPI.
    287 
    288     This sends an XFRM_MSG_ALLOCSPI message and returns the resulting
    289     XfrmUsersaInfo struct.
    290     """
    291     spi = XfrmUserSpiInfo("\x00" * len(XfrmUserSpiInfo))
    292     spi.min = min_spi
    293     spi.max = max_spi
    294     spi.info.id.daddr = PaddedAddress(dst)
    295     spi.info.id.proto = proto
    296 
    297     msg = spi.Pack()
    298     flags = netlink.NLM_F_REQUEST
    299     self._SendNlRequest(XFRM_MSG_ALLOCSPI, msg, flags)
    300     # Read the response message.
    301     data = self._Recv()
    302     nl_hdr, data = cstruct.Read(data, netlink.NLMsgHdr)
    303     if nl_hdr.type == XFRM_MSG_NEWSA:
    304       return XfrmUsersaInfo(data)
    305     if nl_hdr.type == netlink.NLMSG_ERROR:
    306       error = netlink.NLMsgErr(data).error
    307       raise IOError(error, os.strerror(-error))
    308     raise ValueError("Unexpected netlink message type: %d" % nl_hdr.type)
    309 
    310   def DumpSaInfo(self):
    311     return self._Dump(XFRM_MSG_GETSA, None, XfrmUsersaInfo, "")
    312 
    313   def FindSaInfo(self, spi):
    314     sainfo = [sa for sa, attrs in self.DumpSaInfo() if sa.id.spi == spi]
    315     return sainfo[0] if sainfo else None
    316 
    317   def FlushSaInfo(self):
    318     usersa_flush = XfrmUsersaFlush((IPSEC_PROTO_ANY,))
    319     flags = netlink.NLM_F_REQUEST | netlink.NLM_F_ACK
    320     self._SendNlRequest(XFRM_MSG_FLUSHSA, usersa_flush.Pack(), flags)
    321 
    322 
    323 if __name__ == "__main__":
    324   x = Xfrm()
    325   print x.DumpSaInfo()
    326