Home | History | Annotate | Download | only in commands
      1 #   Copyright 2016 - The Android Open Source Project
      2 #
      3 #   Licensed under the Apache License, Version 2.0 (the "License");
      4 #   you may not use this file except in compliance with the License.
      5 #   You may obtain a copy of the License at
      6 #
      7 #       http://www.apache.org/licenses/LICENSE-2.0
      8 #
      9 #   Unless required by applicable law or agreed to in writing, software
     10 #   distributed under the License is distributed on an "AS IS" BASIS,
     11 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 #   See the License for the specific language governing permissions and
     13 #   limitations under the License.
     14 
     15 import ipaddress
     16 import re
     17 
     18 from acts.controllers.utils_lib.ssh import connection
     19 
     20 
     21 class Error(Exception):
     22     """Exception thrown when a valid ip command experiences errors."""
     23 
     24 
     25 class NetworkInterfaceDown(Error):
     26     """Exception thrown when a network interface is down."""
     27 
     28 
     29 class LinuxRouteCommand(object):
     30     """Interface for doing standard ip route commands on a linux system."""
     31 
     32     DEFAULT_ROUTE = 'default'
     33 
     34     def __init__(self, runner):
     35         """
     36         Args:
     37             runner: Object that can take unix commands and run them in an
     38                     enviroment.
     39         """
     40         self._runner = runner
     41 
     42     def add_route(self, net_interface, address):
     43         """Add an entry to the ip routing table.
     44 
     45         Will add a route for either a specific ip address, or a network.
     46 
     47         Args:
     48             net_interface: string, Any packet that sends through this route
     49                            will be sent using this network interface
     50                            (eg. wlan0).
     51             address: ipaddress.IPv4Address, ipaddress.IPv4Network,
     52                      or DEFAULT_ROUTE. The address to use. If a network
     53                      is given then the entire subnet will be routed.
     54                      If DEFAULT_ROUTE is given then this will set the
     55                      default route.
     56 
     57         Raises:
     58             NetworkInterfaceDown: Raised when the network interface is down.
     59         """
     60         try:
     61             self._runner.run('ip route add %s dev %s' %
     62                              (address, net_interface))
     63         except connection.CommandError as e:
     64             if 'File exists' in e.result.stderr:
     65                 raise Error('Route already exists.')
     66             if 'Network is down' in e.result.stderr:
     67                 raise NetworkInterfaceDown(
     68                     'Device must be up for adding a route.')
     69             raise
     70 
     71     def get_routes(self, net_interface=None):
     72         """Get the routes in the ip routing table.
     73 
     74         Args:
     75             net_interface: string, If given, only retrive routes that have
     76                            been registered to go through this network
     77                            interface (eg. wlan0).
     78 
     79         Returns: An iterator that returns a tuple of (address, net_interface).
     80                  If it is the default route then address
     81                  will be the DEFAULT_ROUTE. If the route is a subnet then
     82                  it will be a ipaddress.IPv4Network otherwise it is a
     83                  ipaddress.IPv4Address.
     84         """
     85         result = self._runner.run('ip route show')
     86 
     87         lines = result.stdout.splitlines()
     88 
     89         # Scan through each line for valid route entries
     90         # Example output:
     91         # default via 192.168.1.254 dev eth0  proto static
     92         # 192.168.1.0/24 dev eth0  proto kernel  scope link  src 172.22.100.19  metric 1
     93         # 192.168.2.1 dev eth2 proto kernel scope link metric 1
     94         for line in lines:
     95             if not 'dev' in line:
     96                 continue
     97 
     98             if line.startswith(self.DEFAULT_ROUTE):
     99                 # The default route entry is formatted differently.
    100                 match = re.search('dev (?P<net_interface>.*)', line)
    101                 pair = None
    102                 if match:
    103                     # When there is a match for the route entry pattern create
    104                     # A pair to hold the info.
    105                     pair = (self.DEFAULT_ROUTE,
    106                             match.groupdict()['net_interface'])
    107             else:
    108                 # Test the normal route entry pattern.
    109                 match = re.search(
    110                     '(?P<address>[^\s]*) dev (?P<net_interface>[^\s]*)', line)
    111                 pair = None
    112                 if match:
    113                     # When there is a match for the route entry pattern create
    114                     # A pair to hold the info.
    115                     d = match.groupdict()
    116                     # Route can be either a network or specific address
    117                     try:
    118                         address = ipaddress.IPv4Address(d['address'])
    119                     except ipaddress.AddressValueError:
    120                         address = ipaddress.IPv4Network(d['address'])
    121 
    122                     pair = (address, d['net_interface'])
    123 
    124             # No pair means no pattern was found.
    125             if not pair:
    126                 continue
    127 
    128             if net_interface:
    129                 # If a net_interface was passed in then only give the pair when it is
    130                 # The correct net_interface.
    131                 if pair[1] == net_interface:
    132                     yield pair
    133             else:
    134                 # No net_interface given give all valid route entries.
    135                 yield pair
    136 
    137     def is_route(self, address, net_interface=None):
    138         """Checks to see if a route exists.
    139 
    140         Args:
    141             address: ipaddress.IPv4Address, ipaddress.IPv4Network,
    142                      or DEFAULT_ROUTE, The address to use.
    143             net_interface: string, If specified, the route must be
    144                            registered to go through this network interface
    145                            (eg. wlan0).
    146 
    147         Returns: True if the route is found, False otherwise.
    148         """
    149         for route, _ in self.get_routes(net_interface):
    150             if route == address:
    151                 return True
    152 
    153         return False
    154 
    155     def remove_route(self, address, net_interface=None):
    156         """Removes a route from the ip routing table.
    157 
    158         Removes a route from the ip routing table. If the route does not exist
    159         nothing is done.
    160 
    161         Args:
    162             address: ipaddress.IPv4Address, ipaddress.IPv4Network,
    163                      or DEFAULT_ROUTE, The address of the route to remove.
    164             net_interface: string, If specified the route being removed is
    165                            registered to go through this network interface
    166                            (eg. wlan0)
    167         """
    168         try:
    169             if net_interface:
    170                 self._runner.run('ip route del %s dev %s' %
    171                                  (address, net_interface))
    172             else:
    173                 self._runner.run('ip route del %s' % address)
    174         except connection.CommandError as e:
    175             if 'No such process' in e.result.stderr:
    176                 # The route didn't exist.
    177                 return
    178             raise
    179 
    180     def clear_routes(self, net_interface=None):
    181         """Clears all routes.
    182 
    183         Args:
    184             net_interface: The network interface to clear routes on.
    185             If not given then all routes will be removed on all network
    186             interfaces (eg. wlan0).
    187         """
    188         routes = self.get_routes(net_interface)
    189 
    190         for a, d in routes:
    191             self.remove_route(a, d)
    192