普通文本  |  124行  |  3.78 KB

#!/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.

"""Classes for generic netlink."""

import collections
from socket import *  # pylint: disable=wildcard-import
import struct

import cstruct
import netlink

### Generic netlink constants. See include/uapi/linux/genetlink.h.
# The generic netlink control family.
GENL_ID_CTRL = 16

# Commands.
CTRL_CMD_GETFAMILY = 3

# Attributes.
CTRL_ATTR_FAMILY_ID = 1
CTRL_ATTR_FAMILY_NAME = 2
CTRL_ATTR_VERSION = 3
CTRL_ATTR_HDRSIZE = 4
CTRL_ATTR_MAXATTR = 5
CTRL_ATTR_OPS = 6
CTRL_ATTR_MCAST_GROUPS = 7

# Attributes netsted inside CTRL_ATTR_OPS.
CTRL_ATTR_OP_ID = 1
CTRL_ATTR_OP_FLAGS = 2


# Data structure formats.
# These aren't constants, they're classes. So, pylint: disable=invalid-name
Genlmsghdr = cstruct.Struct("genlmsghdr", "BBxx", "cmd version")


class GenericNetlink(netlink.NetlinkSocket):
  """Base class for all generic netlink classes."""

  NL_DEBUG = []

  def __init__(self):
    super(GenericNetlink, self).__init__(netlink.NETLINK_GENERIC)

  def _SendCommand(self, family, command, version, data, flags):
    genlmsghdr = Genlmsghdr((command, version))
    self._SendNlRequest(family, genlmsghdr.Pack() + data, flags)

  def _Dump(self, family, command, version):
    msg = Genlmsghdr((command, version))
    return super(GenericNetlink, self)._Dump(family, msg, Genlmsghdr, "")


class GenericNetlinkControl(GenericNetlink):
  """Generic netlink control class.

  This interface is used to manage other generic netlink families. We currently
  use it only to find the family ID for address families of interest."""

  def _DecodeOps(self, data):
    ops = []
    Op = collections.namedtuple("Op", ["id", "flags"])
    while data:
      # Skip the nest marker.
      datalen, index, data = data[:2], data[2:4], data[4:]

      nla, nla_data, data = self._ReadNlAttr(data)
      if nla.nla_type != CTRL_ATTR_OP_ID:
        raise ValueError("Expected CTRL_ATTR_OP_ID, got %d" % nla.nla_type)
      op_id = struct.unpack("=I", nla_data)[0]

      nla, nla_data, data = self._ReadNlAttr(data)
      if nla.nla_type != CTRL_ATTR_OP_FLAGS:
        raise ValueError("Expected CTRL_ATTR_OP_FLAGS, got %d" % nla.type)
      op_flags = struct.unpack("=I", nla_data)[0]

      ops.append(Op(op_id, op_flags))
    return ops

  def _Decode(self, command, msg, nla_type, nla_data):
    """Decodes generic netlink control attributes to human-readable format."""

    name = self._GetConstantName(__name__, nla_type, "CTRL_ATTR_")

    if name == "CTRL_ATTR_FAMILY_ID":
      data = struct.unpack("=H", nla_data)[0]
    elif name == "CTRL_ATTR_FAMILY_NAME":
      data = nla_data.strip("\x00")
    elif name in ["CTRL_ATTR_VERSION", "CTRL_ATTR_HDRSIZE", "CTRL_ATTR_MAXATTR"]:
      data = struct.unpack("=I", nla_data)[0]
    elif name == "CTRL_ATTR_OPS":
      data = self._DecodeOps(nla_data)
    else:
      data = nla_data

    return name, data

  def GetFamily(self, name):
    """Returns the family ID for the specified family name."""
    data = self._NlAttrStr(CTRL_ATTR_FAMILY_NAME, name)
    self._SendCommand(GENL_ID_CTRL, CTRL_CMD_GETFAMILY, 0, data, netlink.NLM_F_REQUEST)
    hdr, attrs = self._GetMsg(Genlmsghdr)
    return attrs["CTRL_ATTR_FAMILY_ID"]


if __name__ == "__main__":
  g = GenericNetlinkControl()
  print g.GetFamily("tcp_metrics")