1# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import logging, socket
6from collections import namedtuple
7
8from autotest_lib.client.bin import test
9from autotest_lib.client.common_lib import error
10
11PROTO_FILE = '/proc/net/protocols'
12
13# Defines that python's socket lacks.
14IPPROTO_UDPLITE = 193
15AF_BLUETOOTH = 31
16BTPROTO_L2CAP = 0
17BTPROTO_HCI = 1
18BTPROTO_SCO = 2
19BTPROTO_RFCOMM = 3
20
21# Contains information needed to create a socket using a particular
22# protocol.
23Protocol = namedtuple('Protocol', ['name', 'domain', 'socket_type',
24                                   'proto_num'])
25REQUIRED = set([
26        Protocol('RFCOMM', AF_BLUETOOTH, socket.SOCK_STREAM, BTPROTO_RFCOMM),
27        Protocol('RFCOMM', AF_BLUETOOTH, socket.SOCK_SEQPACKET, BTPROTO_SCO),
28        Protocol('L2CAP', AF_BLUETOOTH, socket.SOCK_STREAM, BTPROTO_L2CAP),
29        Protocol('HCI', AF_BLUETOOTH, socket.SOCK_RAW, BTPROTO_HCI),
30        Protocol('PACKET', socket.AF_PACKET, socket.SOCK_DGRAM, 0),
31        Protocol('RAWv6', socket.AF_INET6, socket.SOCK_RAW, 0),
32        Protocol('UDPLITEv6', socket.AF_INET6, socket.SOCK_DGRAM,
33                 IPPROTO_UDPLITE),
34        Protocol('UDPv6', socket.AF_INET6, socket.SOCK_DGRAM, 0),
35        Protocol('TCPv6', socket.AF_INET6, socket.SOCK_STREAM, 0),
36        Protocol('UNIX', socket.AF_UNIX, socket.SOCK_STREAM, 0),
37        Protocol('UDP-Lite', socket.AF_INET, socket.SOCK_DGRAM,
38                 IPPROTO_UDPLITE),
39        Protocol('PING', socket.AF_INET, socket.SOCK_DGRAM,
40                 socket.IPPROTO_ICMP),
41        Protocol('RAW', socket.AF_INET, socket.SOCK_RAW, 0),
42        Protocol('UDP', socket.AF_INET, socket.SOCK_DGRAM, 0),
43        Protocol('TCP', socket.AF_INET, socket.SOCK_STREAM, 0),
44        Protocol('NETLINK', socket.AF_NETLINK, socket.SOCK_DGRAM, 0),
45        ])
46
47class kernel_ProtocolCheck(test.test):
48    version = 1
49
50    def _try_protocol(self, proto):
51        """
52        Try to create a socket with the specified protocol.
53
54        @param proto Protocol to use to create a socket.
55        """
56        try:
57            sock = socket.socket(proto.domain, proto.socket_type,
58                                 proto.proto_num)
59            sock.close()
60            logging.info('created socket with protocol %s' % (proto.name))
61        except socket.error:
62            # We don't really care if it fails, any required module should've
63            # been loaded anyways.
64            logging.info('failed to create socket with protocol %s' %
65                         (proto.name))
66
67    def _get_supported_protocols(self):
68        """
69        Returns the set of supported protocols from /proc/net/protocols.
70        """
71        f = open(PROTO_FILE)
72        if not f:
73            raise error.TestError('failed to open %s' % (PROTO_FILE))
74        lines = f.readlines()[1:]
75        supported = set(line.split()[0] for line in lines)
76        f.close()
77        return supported
78
79    def run_once(self):
80        """
81        Check that the kernel supports all required network protocols.
82        """
83        for proto in REQUIRED:
84            # Opening a socket with a protocol should ensure that all necessary
85            # modules get loaded.
86            self._try_protocol(proto)
87
88        supported = self._get_supported_protocols()
89
90        # Check that each required protocol is supported.
91        required = set(proto.name for proto in REQUIRED)
92        failures = required - supported
93
94        # Fail if any protocols were unsupported.
95        if failures:
96            raise error.TestFail('required protocols are unsupported: %s' %
97                                 (", ".join(failures)))
98