import re from IPy import IP from sys import stderr from checkdesc.tools import IPmakenet # Network address regex NETWORK_REGEX = r'[0-9.]+/[0-9]{1,2}' # IP address or network address regex IP_OR_NETWORK_REGEX = r'[0-9.]+(?:/[0-9]{1,2})?' # Interface name regex (eg. "eth0") INTERFACE_REGEX = r'[a-z0-9]+' class TextParser: """ Very basic plain text parser useful to read one line after the other. It calls a different function for each line, and each function returns next function to be called for next line. Interresting methods and attributes: - line_number is the current line number of input file (starting at 1) - reset(): function called when parser is created - stop(): function called when the parser is done - parserError(): raise an exception with reason and line number """ def __init__(self, input, first_parser, filename=None): """ Parse input file object, first_parser is the first function used to parse the file content. """ self.input = input self.line_number = 0 self.filename = filename self.first_parser = first_parser self.reset() self.runParser() def _formatError(self, error, message): if self.filename: where = "in %s, line %s" % (self.filename, self.line_number) else: where = "at line %s" % self.line_number return "%s %s: %s" % (error, where, message) def parserWarning(self, message): print >>stderr, self._formatError("Warning", message) def parserError(self, message): raise SyntaxError(self._formatError("Error", message)) def reset(self): pass def stop(self): pass def runParser(self): parser = self.first_parser while True: line = self.input.readline() if len(line) == 0: break line = line.rstrip() self.line_number += 1 new_parser = parser(line) if new_parser: parser = new_parser self.stop() class Address: def __init__(self, ip, scope=None): self.ip = IP(ip.split('/', 1)[0]) self.network = IPmakenet(ip) self.scope = scope self.peer = None def __repr__(self): info = ['ip=%s' % self.ip, 'network=%s' % self.network] if self.scope: info.append('scope=%s' % self.scope) if self.peer: info.append('peer=%s' % self.peer) return "<Address %s>" % ' '.join(info) def __str__(self): return self.__repr__() def __eq__(self, addr): if self.network != addr.network: return False if self.scope != addr.scope: return False if self.peer != addr.peer: return False return True class IpAddrParser(TextParser): # 2: eth0: <BROADCAST,MULTICAST,PROMISC,UP,10000> mtu 1500 qdisc pfifo_fast qlen 1000 # 3: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,10000> mtu 1500 qdisc pfifo_fast qlen 100 HEADER_REGEX = re.compile(r"^[0-9]+: (%s): " % INTERFACE_REGEX) # link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 # link/[65534] LINK_REGEX = re.compile(r"^\s+link/([^ ]+)") # inet 192.168.0.1/24 brd 192.168.0.255 scope global eth0 # inet 10.8.0.66 peer 10.8.0.65/32 scope global tun0 INET_REGEX = re.compile(r"^\s+inet (?P<address>%s)" % IP_OR_NETWORK_REGEX) # "scope global" SCOPE_REGEX = re.compile(r"scope ([^ ]+)") # "peer 10.8.0.65/32" PEER_REGEX = re.compile(r"peer (%s)" % NETWORK_REGEX) def __init__(self, input, filename=None): self.interface = None self.interfaces = [] self.interfaces_dict = {} TextParser.__init__(self, input, self.parseHeader, filename) def saveInterface(self): if self.interface is None: return name = self.interface['name'] if 'addr' not in self.interface: self.parserWarning( "Unable to find IPv4 address of interface %s (skip this interface)" % name) elif name not in self.interfaces_dict: self.interfaces_dict[name] = self.interface self.interfaces.append(self.interface) self.interface = None def parseHeader(self, line): match = self.HEADER_REGEX.search(line) if match: self.saveInterface() name = match.group(1) try: self.interface = self.interfaces_dict[name] except KeyError: self.interface = {} self.interface['name'] = name return self.parseLink return self.parseHeader def parseLink(self, line): match = self.LINK_REGEX.search(line) if not match: self.parserError("Unable to parse link type: %r" % line) self.interface['link'] = match.group(1) return self.parseParameters def parseParameters(self, line): if self.HEADER_REGEX.search(line): return self.parseHeader(line) match = self.INET_REGEX.search(line) if match: addr = match.group('address') if '/' not in addr: addr += '/32' addr = Address(addr) match = self.PEER_REGEX.search(line) if match: addr.peer = IP(match.group(1)) match = self.SCOPE_REGEX.search(line) if match: addr.scope = match.group(1) try: self.interface['addr'].append(addr) except KeyError: self.interface['addr'] = [addr] return self.parseParameters def stop(self): self.saveInterface() class IfconfigParser(TextParser): # eth0 Lien encap:Ethernet HWaddr 00:15:C5:AA:35:38 # lo Lien encap:Boucle locale HEADER_REGEX = re.compile(r"^([a-z0-9]+)(:[0-9]+)?\s+") # inet adr:192.168.33.170 Bcast:192.168.33.255 Mask:255.255.255.128 # inet adr:127.0.0.1 Masque:255.0.0.0 INET_REGEX = re.compile(r"^\s+inet [^: ]+:([^ ]+) +(?:[^: ]+:[^ ]+ +){0,2}[^: ]+:([0-9.]+)$") def __init__(self, input, filename=None): self.interface = None self.interfaces = [] self.interfaces_dict = {} TextParser.__init__(self, input, self.parseHeader, filename) def saveInterface(self): if self.interface is None: return name = self.interface['name'] if 'addr' not in self.interface: self.parserWarning( "Unable to find IPv4 address of interface %s (skip this interface)" % name) elif name.endswith(":avah"): self.parserWarning( "Skip \"avahi\" interace: %s" % name) elif name not in self.interfaces_dict: self.interfaces_dict[name] = self.interface self.interfaces.append(self.interface) self.interface = None def parseHeader(self, line): match = self.HEADER_REGEX.search(line) if match: self.saveInterface() name = match.group(1) try: self.interface = self.interfaces_dict[name] except KeyError: self.interface = {} self.interface['name'] = name return self.parseParameters return self.parseHeader def parseParameters(self, line): if not line: return self.parseHeader match = self.INET_REGEX.match(line) if match: # Hack to be compatible with IpAddrParser self.interface['link'] = 'ether' addr = "%s/%s" % (match.group(1), match.group(2)) addr = Address(addr) try: self.interface['addr'].append(addr) except KeyError: self.interface['addr'] = [addr] return self.parseParameters def stop(self): self.saveInterface() class RouteParser(TextParser): IP_REGEX = r"[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}" ROUTE_REGEX = re.compile( # ip ip ip r"(?P<dest>%s)\s+(?P<gateway>%s)\s+(?P<genmask>%s)\s+" % (IP_REGEX, IP_REGEX, IP_REGEX) # UG 1000 0 0 eth0 + r"\S+\s+[0-9]+\s+[0-9]+\s+[0-9]+\s+(?P<interface>\S+)") def __init__(self, input, filename=None, ignore_interfaces=set()): self.routes = [] self.ignore_interfaces = ignore_interfaces TextParser.__init__(self, input, self.parseRoute, filename) def parseRoute(self, line): match = self.ROUTE_REGEX.match(line) if match: route = match.groupdict() if route['interface'] not in self.ignore_interfaces: self.routes.append(route) else: print >>stderr, "INFO: Skip route %r (ignore interface %r)" % ( line, route['interface']) return self.parseRoute