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 21import os 22from socket import * # pylint: disable=wildcard-import 23import struct 24 25import net_test 26import csocket 27import cstruct 28import netlink 29 30 31# Netlink constants. See include/uapi/linux/xfrm.h. 32# Message types. 33XFRM_MSG_NEWSA = 16 34XFRM_MSG_DELSA = 17 35XFRM_MSG_GETSA = 18 36XFRM_MSG_NEWPOLICY = 19 37XFRM_MSG_DELPOLICY = 20 38XFRM_MSG_GETPOLICY = 21 39XFRM_MSG_ALLOCSPI = 22 40XFRM_MSG_ACQUIRE = 23 41XFRM_MSG_EXPIRE = 24 42XFRM_MSG_UPDPOLICY = 25 43XFRM_MSG_UPDSA = 26 44XFRM_MSG_POLEXPIRE = 27 45XFRM_MSG_FLUSHSA = 28 46XFRM_MSG_FLUSHPOLICY = 29 47XFRM_MSG_NEWAE = 30 48XFRM_MSG_GETAE = 31 49XFRM_MSG_REPORT = 32 50XFRM_MSG_MIGRATE = 33 51XFRM_MSG_NEWSADINFO = 34 52XFRM_MSG_GETSADINFO = 35 53XFRM_MSG_NEWSPDINFO = 36 54XFRM_MSG_GETSPDINFO = 37 55XFRM_MSG_MAPPING = 38 56 57# Attributes. 58XFRMA_UNSPEC = 0 59XFRMA_ALG_AUTH = 1 60XFRMA_ALG_CRYPT = 2 61XFRMA_ALG_COMP = 3 62XFRMA_ENCAP = 4 63XFRMA_TMPL = 5 64XFRMA_SA = 6 65XFRMA_POLICY = 7 66XFRMA_SEC_CTX = 8 67XFRMA_LTIME_VAL = 9 68XFRMA_REPLAY_VAL = 10 69XFRMA_REPLAY_THRESH = 11 70XFRMA_ETIMER_THRESH = 12 71XFRMA_SRCADDR = 13 72XFRMA_COADDR = 14 73XFRMA_LASTUSED = 15 74XFRMA_POLICY_TYPE = 16 75XFRMA_MIGRATE = 17 76XFRMA_ALG_AEAD = 18 77XFRMA_KMADDRESS = 19 78XFRMA_ALG_AUTH_TRUNC = 20 79XFRMA_MARK = 21 80XFRMA_TFCPAD = 22 81XFRMA_REPLAY_ESN_VAL = 23 82XFRMA_SA_EXTRA_FLAGS = 24 83XFRMA_PROTO = 25 84XFRMA_ADDRESS_FILTER = 26 85XFRMA_PAD = 27 86XFRMA_OFFLOAD_DEV = 28 87XFRMA_OUTPUT_MARK = 29 88XFRMA_INPUT_MARK = 30 89XFRMA_IF_ID = 31 90 91# Other netlink constants. See include/uapi/linux/xfrm.h. 92 93# Directions. 94XFRM_POLICY_IN = 0 95XFRM_POLICY_OUT = 1 96XFRM_POLICY_FWD = 2 97XFRM_POLICY_MASK = 3 98 99# Policy sharing. 100XFRM_SHARE_ANY = 0 # /* No limitations */ 101XFRM_SHARE_SESSION = 1 # /* For this session only */ 102XFRM_SHARE_USER = 2 # /* For this user only */ 103XFRM_SHARE_UNIQUE = 3 # /* Use once */ 104 105# Modes. 106XFRM_MODE_TRANSPORT = 0 107XFRM_MODE_TUNNEL = 1 108XFRM_MODE_ROUTEOPTIMIZATION = 2 109XFRM_MODE_IN_TRIGGER = 3 110XFRM_MODE_BEET = 4 111XFRM_MODE_MAX = 5 112 113# Actions. 114XFRM_POLICY_ALLOW = 0 115XFRM_POLICY_BLOCK = 1 116 117# Policy flags. 118XFRM_POLICY_LOCALOK = 1 119XFRM_POLICY_ICMP = 2 120 121# State flags. 122XFRM_STATE_AF_UNSPEC = 32 123 124# XFRM algorithm names, as defined in net/xfrm/xfrm_algo.c. 125XFRM_EALG_CBC_AES = "cbc(aes)" 126XFRM_AALG_HMAC_MD5 = "hmac(md5)" 127XFRM_AALG_HMAC_SHA1 = "hmac(sha1)" 128XFRM_AALG_HMAC_SHA256 = "hmac(sha256)" 129XFRM_AALG_HMAC_SHA384 = "hmac(sha384)" 130XFRM_AALG_HMAC_SHA512 = "hmac(sha512)" 131XFRM_AEAD_GCM_AES = "rfc4106(gcm(aes))" 132 133# Data structure formats. 134# These aren't constants, they're classes. So, pylint: disable=invalid-name 135XfrmSelector = cstruct.Struct( 136 "XfrmSelector", "=16s16sHHHHHBBBxxxiI", 137 "daddr saddr dport dport_mask sport sport_mask " 138 "family prefixlen_d prefixlen_s proto ifindex user") 139 140XfrmLifetimeCfg = cstruct.Struct( 141 "XfrmLifetimeCfg", "=QQQQQQQQ", 142 "soft_byte hard_byte soft_packet hard_packet " 143 "soft_add_expires hard_add_expires soft_use_expires hard_use_expires") 144 145XfrmLifetimeCur = cstruct.Struct( 146 "XfrmLifetimeCur", "=QQQQ", "bytes packets add_time use_time") 147 148XfrmAlgo = cstruct.Struct("XfrmAlgo", "=64AI", "name key_len") 149 150XfrmAlgoAuth = cstruct.Struct("XfrmAlgoAuth", "=64AII", 151 "name key_len trunc_len") 152 153XfrmAlgoAead = cstruct.Struct("XfrmAlgoAead", "=64AII", "name key_len icv_len") 154 155XfrmStats = cstruct.Struct( 156 "XfrmStats", "=III", "replay_window replay integrity_failed") 157 158XfrmId = cstruct.Struct("XfrmId", "!16sIBxxx", "daddr spi proto") 159 160XfrmUserTmpl = cstruct.Struct( 161 "XfrmUserTmpl", "=SHxx16sIBBBxIII", 162 "id family saddr reqid mode share optional aalgos ealgos calgos", 163 [XfrmId]) 164 165XfrmEncapTmpl = cstruct.Struct( 166 "XfrmEncapTmpl", "=HHHxx16s", "type sport dport oa") 167 168XfrmUsersaInfo = cstruct.Struct( 169 "XfrmUsersaInfo", "=SS16sSSSIIHBBB7x", 170 "sel id saddr lft curlft stats seq reqid family mode replay_window flags", 171 [XfrmSelector, XfrmId, XfrmLifetimeCfg, XfrmLifetimeCur, XfrmStats]) 172 173XfrmUserSpiInfo = cstruct.Struct( 174 "XfrmUserSpiInfo", "=SII", "info min max", [XfrmUsersaInfo]) 175 176# Technically the family is a 16-bit field, but only a few families are in use, 177# and if we pretend it's 8 bits (i.e., use "Bx" instead of "H") we can think 178# of the whole structure as being in network byte order. 179XfrmUsersaId = cstruct.Struct( 180 "XfrmUsersaId", "!16sIBxBx", "daddr spi family proto") 181 182# xfrm.h - struct xfrm_userpolicy_info 183XfrmUserpolicyInfo = cstruct.Struct( 184 "XfrmUserpolicyInfo", "=SSSIIBBBBxxxx", 185 "sel lft curlft priority index dir action flags share", 186 [XfrmSelector, XfrmLifetimeCfg, XfrmLifetimeCur]) 187 188XfrmUserpolicyId = cstruct.Struct( 189 "XfrmUserpolicyId", "=SIBxxx", "sel index dir", [XfrmSelector]) 190 191XfrmUsersaFlush = cstruct.Struct("XfrmUsersaFlush", "=B", "proto") 192 193XfrmMark = cstruct.Struct("XfrmMark", "=II", "mark mask") 194 195# Socket options. See include/uapi/linux/in.h. 196IP_IPSEC_POLICY = 16 197IP_XFRM_POLICY = 17 198IPV6_IPSEC_POLICY = 34 199IPV6_XFRM_POLICY = 35 200 201# UDP encapsulation constants. See include/uapi/linux/udp.h. 202UDP_ENCAP = 100 203UDP_ENCAP_ESPINUDP_NON_IKE = 1 204UDP_ENCAP_ESPINUDP = 2 205 206_INF = 2 ** 64 -1 207NO_LIFETIME_CFG = XfrmLifetimeCfg((_INF, _INF, _INF, _INF, 0, 0, 0, 0)) 208NO_LIFETIME_CUR = "\x00" * len(XfrmLifetimeCur) 209 210# IPsec constants. 211IPSEC_PROTO_ANY = 255 212 213# ESP header, not technically XFRM but we need a place for a protocol 214# header and this is the only one we have. 215# TODO: move this somewhere more appropriate when possible 216EspHdr = cstruct.Struct("EspHdr", "!II", "spi seqnum") 217 218# Local constants. 219_DEFAULT_REPLAY_WINDOW = 4 220ALL_ALGORITHMS = 0xffffffff 221 222# Policy-SA match method (for VTI/XFRM-I). 223MATCH_METHOD_ALL = "all" 224MATCH_METHOD_MARK = "mark" 225MATCH_METHOD_IFID = "ifid" 226 227 228def RawAddress(addr): 229 """Converts an IP address string to binary format.""" 230 family = AF_INET6 if ":" in addr else AF_INET 231 return inet_pton(family, addr) 232 233 234def PaddedAddress(addr): 235 """Converts an IP address string to binary format for InetDiagSockId.""" 236 padded = RawAddress(addr) 237 if len(padded) < 16: 238 padded += "\x00" * (16 - len(padded)) 239 return padded 240 241 242XFRM_ADDR_ANY = PaddedAddress("::") 243 244 245def EmptySelector(family): 246 """A selector that matches all packets of the specified address family.""" 247 return XfrmSelector(family=family) 248 249 250def SrcDstSelector(src, dst): 251 """A selector that matches packets between the specified IP addresses.""" 252 srcver = csocket.AddressVersion(src) 253 dstver = csocket.AddressVersion(dst) 254 if srcver != dstver: 255 raise ValueError("Cross-address family selector specified: %s -> %s" % 256 (src, dst)) 257 prefixlen = net_test.AddressLengthBits(srcver) 258 family = net_test.GetAddressFamily(srcver) 259 return XfrmSelector(saddr=PaddedAddress(src), daddr=PaddedAddress(dst), 260 prefixlen_s=prefixlen, prefixlen_d=prefixlen, family=family) 261 262 263def UserPolicy(direction, selector): 264 """Create an IPsec policy. 265 266 Args: 267 direction: XFRM_POLICY_IN or XFRM_POLICY_OUT 268 selector: An XfrmSelector, the packets to transform. 269 270 Return: a XfrmUserpolicyInfo cstruct. 271 """ 272 # Create a user policy that specifies that all packets in the specified 273 # direction matching the selector should be encrypted. 274 return XfrmUserpolicyInfo( 275 sel=selector, 276 lft=NO_LIFETIME_CFG, 277 curlft=NO_LIFETIME_CUR, 278 dir=direction, 279 action=XFRM_POLICY_ALLOW, 280 flags=XFRM_POLICY_LOCALOK, 281 share=XFRM_SHARE_UNIQUE) 282 283 284def UserTemplate(family, spi, reqid, tun_addrs): 285 """Create an ESP policy and template. 286 287 Args: 288 spi: 32-bit SPI in host byte order 289 reqid: 32-bit ID matched against SAs 290 tun_addrs: A tuple of (local, remote) addresses for tunnel mode, or None 291 to request a transport mode SA. 292 293 Return: a tuple of XfrmUserpolicyInfo, XfrmUserTmpl 294 """ 295 # For transport mode, set template source and destination are empty. 296 # For tunnel mode, explicitly specify source and destination addresses. 297 if tun_addrs is None: 298 mode = XFRM_MODE_TRANSPORT 299 saddr = XFRM_ADDR_ANY 300 daddr = XFRM_ADDR_ANY 301 else: 302 mode = XFRM_MODE_TUNNEL 303 saddr = PaddedAddress(tun_addrs[0]) 304 daddr = PaddedAddress(tun_addrs[1]) 305 306 # Create a template that specifies the SPI and the protocol. 307 xfrmid = XfrmId(daddr=daddr, spi=spi, proto=IPPROTO_ESP) 308 template = XfrmUserTmpl( 309 id=xfrmid, 310 family=family, 311 saddr=saddr, 312 reqid=reqid, 313 mode=mode, 314 share=XFRM_SHARE_UNIQUE, 315 optional=0, #require 316 aalgos=ALL_ALGORITHMS, 317 ealgos=ALL_ALGORITHMS, 318 calgos=ALL_ALGORITHMS) 319 320 return template 321 322 323def ExactMatchMark(mark): 324 """An XfrmMark that matches only the specified mark.""" 325 return XfrmMark((mark, 0xffffffff)) 326 327 328class Xfrm(netlink.NetlinkSocket): 329 """Netlink interface to xfrm.""" 330 331 DEBUG = False 332 333 def __init__(self): 334 super(Xfrm, self).__init__(netlink.NETLINK_XFRM) 335 336 def _GetConstantName(self, value, prefix): 337 return super(Xfrm, self)._GetConstantName(__name__, value, prefix) 338 339 def MaybeDebugCommand(self, command, flags, data): 340 if "ALL" not in self.NL_DEBUG and "XFRM" not in self.NL_DEBUG: 341 return 342 343 if command == XFRM_MSG_GETSA: 344 if flags & netlink.NLM_F_DUMP: 345 struct_type = XfrmUsersaInfo 346 else: 347 struct_type = XfrmUsersaId 348 elif command == XFRM_MSG_DELSA: 349 struct_type = XfrmUsersaId 350 elif command == XFRM_MSG_ALLOCSPI: 351 struct_type = XfrmUserSpiInfo 352 elif command == XFRM_MSG_NEWPOLICY: 353 struct_type = XfrmUserpolicyInfo 354 else: 355 struct_type = None 356 357 cmdname = self._GetConstantName(command, "XFRM_MSG_") 358 if struct_type: 359 print "%s %s" % (cmdname, str(self._ParseNLMsg(data, struct_type))) 360 else: 361 print "%s" % cmdname 362 363 def _Decode(self, command, unused_msg, nla_type, nla_data): 364 """Decodes netlink attributes to Python types.""" 365 name = self._GetConstantName(nla_type, "XFRMA_") 366 367 if name in ["XFRMA_ALG_CRYPT", "XFRMA_ALG_AUTH"]: 368 data = cstruct.Read(nla_data, XfrmAlgo)[0] 369 elif name == "XFRMA_ALG_AUTH_TRUNC": 370 data = cstruct.Read(nla_data, XfrmAlgoAuth)[0] 371 elif name == "XFRMA_ENCAP": 372 data = cstruct.Read(nla_data, XfrmEncapTmpl)[0] 373 elif name == "XFRMA_MARK": 374 data = cstruct.Read(nla_data, XfrmMark)[0] 375 elif name == "XFRMA_OUTPUT_MARK": 376 data = struct.unpack("=I", nla_data)[0] 377 elif name == "XFRMA_TMPL": 378 data = cstruct.Read(nla_data, XfrmUserTmpl)[0] 379 elif name == "XFRMA_IF_ID": 380 data = struct.unpack("=I", nla_data)[0] 381 else: 382 data = nla_data 383 384 return name, data 385 386 def _UpdatePolicyInfo(self, msg, policy, tmpl, mark, xfrm_if_id): 387 """Send a policy to the Security Policy Database""" 388 nlattrs = [] 389 if tmpl is not None: 390 nlattrs.append((XFRMA_TMPL, tmpl)) 391 if mark is not None: 392 nlattrs.append((XFRMA_MARK, mark)) 393 if xfrm_if_id is not None: 394 nlattrs.append((XFRMA_IF_ID, struct.pack("=I", xfrm_if_id))) 395 self.SendXfrmNlRequest(msg, policy, nlattrs) 396 397 def AddPolicyInfo(self, policy, tmpl, mark, xfrm_if_id=None): 398 """Add a new policy to the Security Policy Database 399 400 If the policy exists, then return an error (EEXIST). 401 402 Args: 403 policy: an unpacked XfrmUserpolicyInfo 404 tmpl: an unpacked XfrmUserTmpl 405 mark: an unpacked XfrmMark 406 xfrm_if_id: the XFRM interface ID as an integer, or None 407 """ 408 self._UpdatePolicyInfo(XFRM_MSG_NEWPOLICY, policy, tmpl, mark, xfrm_if_id) 409 410 def UpdatePolicyInfo(self, policy, tmpl, mark, xfrm_if_id): 411 """Update an existing policy in the Security Policy Database 412 413 If the policy does not exist, then create it; otherwise, update the 414 existing policy record. 415 416 Args: 417 policy: an unpacked XfrmUserpolicyInfo 418 tmpl: an unpacked XfrmUserTmpl to update 419 mark: an unpacked XfrmMark to match the existing policy or None 420 xfrm_if_id: an XFRM interface ID or None 421 """ 422 self._UpdatePolicyInfo(XFRM_MSG_UPDPOLICY, policy, tmpl, mark, xfrm_if_id) 423 424 def DeletePolicyInfo(self, selector, direction, mark, xfrm_if_id=None): 425 """Delete a policy from the Security Policy Database 426 427 Args: 428 selector: an XfrmSelector matching the policy to delete 429 direction: policy direction 430 mark: an unpacked XfrmMark to match the policy or None 431 """ 432 nlattrs = [] 433 if mark is not None: 434 nlattrs.append((XFRMA_MARK, mark)) 435 if xfrm_if_id is not None: 436 nlattrs.append((XFRMA_IF_ID, struct.pack("=I", xfrm_if_id))) 437 self.SendXfrmNlRequest(XFRM_MSG_DELPOLICY, 438 XfrmUserpolicyId(sel=selector, dir=direction), 439 nlattrs) 440 441 # TODO: this function really needs to be in netlink.py 442 def SendXfrmNlRequest(self, msg_type, req, nlattrs=None, 443 flags=netlink.NLM_F_ACK|netlink.NLM_F_REQUEST): 444 """Sends a netlink request message 445 446 Args: 447 msg_type: an XFRM_MSG_* type 448 req: an unpacked netlink request message body cstruct 449 nlattrs: an unpacked list of two-tuples of (NLATTR_* type, body) where 450 the body is an unpacked cstruct 451 flags: a list of flags for the expected handling; if no flags are 452 provided, an ACK response is assumed. 453 """ 454 msg = req.Pack() 455 if nlattrs is None: 456 nlattrs = [] 457 for attr_type, attr_msg in nlattrs: 458 # TODO: find a better way to deal with the fact that many XFRM messages 459 # use nlattrs that aren't cstructs. 460 # 461 # This code allows callers to pass in either something that has a Pack() 462 # method or a packed netlink attr, but not other types of attributes. 463 # Alternatives include: 464 # 465 # 1. Require callers to marshal netlink attributes themselves and call 466 # _SendNlRequest directly. Delete this method. 467 # 2. Rename this function to _SendXfrmNlRequestCstructOnly (or other name 468 # that makes it clear that this only takes cstructs). Switch callers 469 # that need non-cstruct elements to calling _SendNlRequest directly. 470 # 3. Make this function somehow automatically detect what to do for 471 # all types of XFRM attributes today and in the future. This may be 472 # feasible because all XFRM attributes today occupy the same number 473 # space, but what about nested attributes? It is unlikley feasible via 474 # things like "if isinstance(attr_msg, str): ...", because that would 475 # not be able to determine the right size or byte order for non-struct 476 # types such as int. 477 # 4. Define fictitious cstructs which have no correspondence to actual 478 # kernel structs such as the following to represent a raw integer. 479 # XfrmAttrOutputMark = cstruct.Struct("=I", mark) 480 if hasattr(attr_msg, "Pack"): 481 attr_msg = attr_msg.Pack() 482 msg += self._NlAttr(attr_type, attr_msg) 483 return self._SendNlRequest(msg_type, msg, flags) 484 485 def AddSaInfo(self, src, dst, spi, mode, reqid, encryption, auth_trunc, aead, 486 encap, mark, output_mark, is_update=False, xfrm_if_id=None): 487 """Adds an IPsec security association. 488 489 Args: 490 src: A string, the source IP address. May be a wildcard in transport mode. 491 dst: A string, the destination IP address. Forms part of the XFRM ID, and 492 must match the destination address of the packets sent by this SA. 493 spi: An integer, the SPI. 494 mode: An IPsec mode such as XFRM_MODE_TRANSPORT. 495 reqid: A request ID. Can be used in policies to match the SA. 496 encryption: A tuple of an XfrmAlgo and raw key bytes, or None. 497 auth_trunc: A tuple of an XfrmAlgoAuth and raw key bytes, or None. 498 aead: A tuple of an XfrmAlgoAead and raw key bytes, or None. 499 encap: An XfrmEncapTmpl structure, or None. 500 mark: A mark match specifier, such as returned by ExactMatchMark(), or 501 None for an SA that matches all possible marks. 502 output_mark: An integer, the output mark. 0 means unset. 503 is_update: If true, update an existing SA otherwise create a new SA. For 504 compatibility reasons, this value defaults to False. 505 xfrm_if_id: The XFRM interface ID, or None. 506 """ 507 proto = IPPROTO_ESP 508 xfrm_id = XfrmId((PaddedAddress(dst), spi, proto)) 509 family = AF_INET6 if ":" in dst else AF_INET 510 511 nlattrs = "" 512 if encryption is not None: 513 enc, key = encryption 514 nlattrs += self._NlAttr(XFRMA_ALG_CRYPT, enc.Pack() + key) 515 516 if auth_trunc is not None: 517 auth, key = auth_trunc 518 nlattrs += self._NlAttr(XFRMA_ALG_AUTH_TRUNC, auth.Pack() + key) 519 520 if aead is not None: 521 aead_alg, key = aead 522 nlattrs += self._NlAttr(XFRMA_ALG_AEAD, aead_alg.Pack() + key) 523 524 # if a user provides either mark or mask, then we send the mark attribute 525 if mark is not None: 526 nlattrs += self._NlAttr(XFRMA_MARK, mark.Pack()) 527 if encap is not None: 528 nlattrs += self._NlAttr(XFRMA_ENCAP, encap.Pack()) 529 if output_mark is not None: 530 nlattrs += self._NlAttrU32(XFRMA_OUTPUT_MARK, output_mark) 531 if xfrm_if_id is not None: 532 nlattrs += self._NlAttrU32(XFRMA_IF_ID, xfrm_if_id) 533 534 # The kernel ignores these on input, so make them empty. 535 cur = XfrmLifetimeCur() 536 stats = XfrmStats() 537 seq = 0 538 replay = _DEFAULT_REPLAY_WINDOW 539 540 # The XFRM_STATE_AF_UNSPEC flag determines how AF_UNSPEC selectors behave. 541 # 542 # - If the flag is not set, an AF_UNSPEC selector has its family changed to 543 # the SA family, which in our case is the address family of dst. 544 # - If the flag is set, an AF_UNSPEC selector is left as is. In transport 545 # mode this fails with EPROTONOSUPPORT, but in tunnel mode, it results in 546 # a dual-stack SA that can tunnel both IPv4 and IPv6 packets. 547 # 548 # This allows us to pass an empty selector to the kernel regardless of which 549 # mode we're in: when creating transport mode SAs, the kernel will pick the 550 # selector family based on the SA family, and when creating tunnel mode SAs, 551 # we'll just create SAs that select both IPv4 and IPv6 traffic, and leave it 552 # up to the policy selectors to determine what traffic we actually want to 553 # transform. 554 flags = XFRM_STATE_AF_UNSPEC if mode == XFRM_MODE_TUNNEL else 0 555 selector = EmptySelector(AF_UNSPEC) 556 557 sa = XfrmUsersaInfo((selector, xfrm_id, PaddedAddress(src), NO_LIFETIME_CFG, 558 cur, stats, seq, reqid, family, mode, replay, flags)) 559 msg = sa.Pack() + nlattrs 560 flags = netlink.NLM_F_REQUEST | netlink.NLM_F_ACK 561 nl_msg_type = XFRM_MSG_UPDSA if is_update else XFRM_MSG_NEWSA 562 self._SendNlRequest(nl_msg_type, msg, flags) 563 564 def DeleteSaInfo(self, dst, spi, proto, mark=None, xfrm_if_id=None): 565 """Delete an SA from the SAD 566 567 Args: 568 dst: A string, the destination IP address. Forms part of the XFRM ID, and 569 must match the destination address of the packets sent by this SA. 570 spi: An integer, the SPI. 571 proto: The protocol DB of the SA, such as IPPROTO_ESP. 572 mark: A mark match specifier, such as returned by ExactMatchMark(), or 573 None for an SA without a Mark attribute. 574 """ 575 family = AF_INET6 if ":" in dst else AF_INET 576 usersa_id = XfrmUsersaId((PaddedAddress(dst), spi, family, proto)) 577 nlattrs = [] 578 if mark is not None: 579 nlattrs.append((XFRMA_MARK, mark)) 580 if xfrm_if_id is not None: 581 nlattrs.append((XFRMA_IF_ID, struct.pack("=I", xfrm_if_id))) 582 self.SendXfrmNlRequest(XFRM_MSG_DELSA, usersa_id, nlattrs) 583 584 def AllocSpi(self, dst, proto, min_spi, max_spi): 585 """Allocate (reserve) an SPI. 586 587 This sends an XFRM_MSG_ALLOCSPI message and returns the resulting 588 XfrmUsersaInfo struct. 589 590 Args: 591 dst: A string, the destination IP address. Forms part of the XFRM ID, and 592 must match the destination address of the packets sent by this SA. 593 proto: the protocol DB of the SA, such as IPPROTO_ESP. 594 min_spi: The minimum value of the acceptable SPI range (inclusive). 595 max_spi: The maximum value of the acceptable SPI range (inclusive). 596 """ 597 spi = XfrmUserSpiInfo("\x00" * len(XfrmUserSpiInfo)) 598 spi.min = min_spi 599 spi.max = max_spi 600 spi.info.id.daddr = PaddedAddress(dst) 601 spi.info.id.proto = proto 602 spi.info.family = AF_INET6 if ":" in dst else AF_INET 603 604 msg = spi.Pack() 605 flags = netlink.NLM_F_REQUEST 606 self._SendNlRequest(XFRM_MSG_ALLOCSPI, msg, flags) 607 # Read the response message. 608 data = self._Recv() 609 nl_hdr, data = cstruct.Read(data, netlink.NLMsgHdr) 610 if nl_hdr.type == XFRM_MSG_NEWSA: 611 return XfrmUsersaInfo(data) 612 if nl_hdr.type == netlink.NLMSG_ERROR: 613 error = netlink.NLMsgErr(data).error 614 raise IOError(error, os.strerror(-error)) 615 raise ValueError("Unexpected netlink message type: %d" % nl_hdr.type) 616 617 def DumpSaInfo(self): 618 return self._Dump(XFRM_MSG_GETSA, None, XfrmUsersaInfo, "") 619 620 def DumpPolicyInfo(self): 621 return self._Dump(XFRM_MSG_GETPOLICY, None, XfrmUserpolicyInfo, "") 622 623 def FindSaInfo(self, spi): 624 sainfo = [sa for sa, attrs in self.DumpSaInfo() if sa.id.spi == spi] 625 return sainfo[0] if sainfo else None 626 627 def FlushPolicyInfo(self): 628 """Send a Netlink Request to Flush all records from the SPD""" 629 flags = netlink.NLM_F_REQUEST | netlink.NLM_F_ACK 630 self._SendNlRequest(XFRM_MSG_FLUSHPOLICY, "", flags) 631 632 def FlushSaInfo(self): 633 usersa_flush = XfrmUsersaFlush((IPSEC_PROTO_ANY,)) 634 flags = netlink.NLM_F_REQUEST | netlink.NLM_F_ACK 635 self._SendNlRequest(XFRM_MSG_FLUSHSA, usersa_flush.Pack(), flags) 636 637 def CreateTunnel(self, direction, selector, src, dst, spi, encryption, 638 auth_trunc, mark, output_mark, xfrm_if_id, match_method): 639 """Create an XFRM Tunnel Consisting of a Policy and an SA. 640 641 Create a unidirectional XFRM tunnel, which entails one Policy and one 642 security association. 643 644 Args: 645 direction: XFRM_POLICY_IN or XFRM_POLICY_OUT 646 selector: An XfrmSelector that specifies the packets to be transformed. 647 This is only applied to the policy; the selector in the SA is always 648 empty. If the passed-in selector is None, then the tunnel is made 649 dual-stack. This requires two policies, one for IPv4 and one for IPv6. 650 src: The source address of the tunneled packets 651 dst: The destination address of the tunneled packets 652 spi: The SPI for the IPsec SA that encapsulates the tunneled packet 653 encryption: A tuple (XfrmAlgo, key), the encryption parameters. 654 auth_trunc: A tuple (XfrmAlgoAuth, key), the authentication parameters. 655 mark: An XfrmMark, the mark used for selecting packets to be tunneled, and 656 for matching the security policy. None means unspecified. 657 output_mark: The mark used to select the underlying network for packets 658 outbound from xfrm. None means unspecified. 659 xfrm_if_id: The ID of the XFRM interface to use or None. 660 match_method: One of MATCH_METHOD_[MARK | ALL | IFID]. This determines how 661 SAs and policies are matched. 662 """ 663 outer_family = net_test.GetAddressFamily(net_test.GetAddressVersion(dst)) 664 665 # SA mark is currently unused due to UPDSA not updating marks. 666 # Kept as documentation of ideal/desired behavior. 667 if match_method == MATCH_METHOD_MARK: 668 # sa_mark = mark 669 tmpl_spi = 0 670 if_id = None 671 elif match_method == MATCH_METHOD_ALL: 672 # sa_mark = mark 673 tmpl_spi = spi 674 if_id = xfrm_if_id 675 elif match_method == MATCH_METHOD_IFID: 676 # sa_mark = None 677 tmpl_spi = 0 678 if_id = xfrm_if_id 679 else: 680 raise ValueError("Unknown match_method supplied: %s" % match_method) 681 682 # Device code does not use mark; during AllocSpi, the mark is unset, and 683 # UPDSA does not update marks at this time. Actual use case will have no 684 # mark set. Test this use case. 685 self.AddSaInfo(src, dst, spi, XFRM_MODE_TUNNEL, 0, encryption, auth_trunc, 686 None, None, None, output_mark, xfrm_if_id=xfrm_if_id) 687 688 if selector is None: 689 selectors = [EmptySelector(AF_INET), EmptySelector(AF_INET6)] 690 else: 691 selectors = [selector] 692 693 for selector in selectors: 694 policy = UserPolicy(direction, selector) 695 tmpl = UserTemplate(outer_family, tmpl_spi, 0, (src, dst)) 696 self.AddPolicyInfo(policy, tmpl, mark, xfrm_if_id=xfrm_if_id) 697 698 def DeleteTunnel(self, direction, selector, dst, spi, mark, xfrm_if_id): 699 if mark is not None: 700 mark = ExactMatchMark(mark) 701 702 self.DeleteSaInfo(dst, spi, IPPROTO_ESP, mark, xfrm_if_id) 703 if selector is None: 704 selectors = [EmptySelector(AF_INET), EmptySelector(AF_INET6)] 705 else: 706 selectors = [selector] 707 for selector in selectors: 708 self.DeletePolicyInfo(selector, direction, mark, xfrm_if_id) 709 710 711if __name__ == "__main__": 712 x = Xfrm() 713 print x.DumpSaInfo() 714 print x.DumpPolicyInfo() 715