diff --git a/nl80211.py b/nl80211.py index 8d7d184..a8f0eb9 100755 --- a/nl80211.py +++ b/nl80211.py @@ -1,17 +1,13 @@ #!/usr/bin/python2.7 import collections +import os +import random import socket import struct -sock = socket.socket(socket.AF_NETLINK, socket.SOCK_DGRAM, 16) -sock.bind((0,0)) -query = '\34\0\0\0\24\0\5\3\\7\252T\367\16\0\0\21\0\0\0\10\0\3\0\6\0\0\0' -sock.send(query) -data = sock.recv(1024) - -class ParsedData(object): +class Iterator(object): def __init__(self, data): self.data = data self.offset = 0 @@ -21,104 +17,142 @@ class ParsedData(object): return '(%d bytes): %r' % (len(data), data) def Advance(self, offset_incr): + assert self.offset + offset_incr <= len(self.data) self.offset += offset_incr + def Extract(self, length): + assert self.offset + length <= len(self.data) + ret = self.data[self.offset:self.offset + length] + self.Advance(length) + return ret + def AtEnd(self): return self.offset == len(self.data) -class Parser(struct.Struct): - def __init__(self, format, fields=None): - super(Parser, self).__init__(format) - self._fields = fields +class Accumulator(object): + def __init__(self): + self._parts = [] - def Parse(self, parsed_data, targetlen=None): + def __str__(self): + return ''.join(self._parts) + + def __len__(self): + return sum(len(part) for part in self._parts) + + def Append(self, value): + self._parts.append(value) + + +class SingleStructParser(struct.Struct): + def Unpack(self, iterator, targetlen=None): if targetlen is not None: assert self.size == targetlen, 'Actual bytes: %d, expected bytes: %d' % (targetlen, self.size) - values = self.unpack_from(parsed_data.data, parsed_data.offset) - parsed_data.Advance(self.size) - if self._fields: - return collections.OrderedDict(zip(self._fields, values)) - else: - assert len(values) == 1 - return values[0] + values = self.unpack_from(iterator.data, iterator.offset) + iterator.Advance(self.size) + assert len(values) == 1 + return values[0] + + def Pack(self, accumulator, value): + accumulator.Append(self.pack(value)) + + +class StructParser(struct.Struct): + def __init__(self, format, fields=None): + super(StructParser, self).__init__(format) + self._fields = fields + + def Unpack(self, iterator, targetlen=None): + if targetlen is not None: + assert self.size == targetlen, 'Actual bytes: %d, expected bytes: %d' % (targetlen, self.size) + values = self.unpack_from(iterator.data, iterator.offset) + iterator.Advance(self.size) + return collections.OrderedDict(zip(self._fields, values)) + + def Pack(self, accumulator, **values): + ordered_values = [] + for field in self._fields: + ordered_values.append(values[field]) + accumulator.Append(self.pack(*ordered_values)) class StringParser(object): - def Parse(self, parsed_data, targetlen): - ret = parsed_data.data[parsed_data.offset:parsed_data.offset + targetlen] - parsed_data.Advance(targetlen) - return ret + def Unpack(self, iterator, targetlen): + return iterator.Extract(targetlen) + + def Pack(self, accumulator, value): + accumulator.Append(value) class EmptyParser(object): - def Parse(self, parsed_data, targetlen=None): + def Unpack(self, iterator, targetlen=None): assert not targetlen return True + def Pack(self, accumulator, value=None): + pass -nlmsghdr = Parser('LHHLL', ('length', 'type', 'flags', 'seq', 'pid')) -genlmsghdr = Parser('BBH', ('cmd', 'version', 'reserved')) + +nlmsghdr = StructParser('LHHLL', ('length', 'type', 'flags', 'seq', 'pid')) +genlmsghdr = StructParser('BBH', ('cmd', 'version', 'reserved')) class Attribute(object): - _nlattr = Parser('HH', ('len', 'type')) + _nlattr = StructParser('HH', ('len', 'type')) def __init__(self, attributes): super(Attribute, self).__init__() self._attributes = attributes - def Parse(self, parsed_data, targetlen=None): - nlattr = self._nlattr.Parse(parsed_data) + def Unpack(self, iterator, targetlen=None): + nlattr = self._nlattr.Unpack(iterator) if targetlen is not None: assert nlattr['len'] == targetlen name, sub_parser = self._attributes.get(nlattr['type'], (None, None)) assert sub_parser, 'Unknown attribute type %d, len %d' % (nlattr['type'], nlattr['len']) sub_len = nlattr['len'] - self._nlattr.size ret = { - name: sub_parser.Parse(parsed_data, sub_len) + name: sub_parser.Unpack(iterator, sub_len) } padding = ((nlattr['len'] + 4 - 1) & ~3) - nlattr['len'] - parsed_data.Advance(padding) + iterator.Advance(padding) return ret + def Pack(self, accumulator, attrtype, **attrargs): + sub_parser = self._attributes[attrtype][1] + sub_accumulator = Accumulator() + sub_parser.Pack(sub_accumulator, **attrargs) + self._nlattr.Pack(accumulator, len=self._nlattr.size + len(sub_accumulator), type=attrtype) + accumulator.Append(str(sub_accumulator)) + class Attributes(object): - def __init__(self, attributes): super(Attributes, self).__init__() self._attribute = Attribute(attributes) - def Parse(self, parsed_data, targetlen=None): - if targetlen is None: - local_parsed_data = parsed_data - else: - local_parsed_data = ParsedData(parsed_data.data[parsed_data.offset:parsed_data.offset + targetlen]) - parsed_data.Advance(targetlen) + def Unpack(self, iterator, targetlen=None): + if targetlen is not None: + iterator = Iterator(iterator.Extract(targetlen)) ret = collections.OrderedDict() - while not local_parsed_data.AtEnd(): - ret.update(self._attribute.Parse(local_parsed_data)) + while not iterator.AtEnd(): + ret.update(self._attribute.Unpack(iterator)) return ret + def Pack(self, accumulator, attrtype, **attrargs): + return self._attribute.Pack(accumulator, attrtype, **attrargs) + flag = EmptyParser() string = StringParser() -u8 = Parser('B') -u16 = Parser('H') -u32 = Parser('L') -u64 = Parser('Q') +u8 = SingleStructParser('B') +u16 = SingleStructParser('H') +u32 = SingleStructParser('L') +u64 = SingleStructParser('Q') -sta_flag_authorized = 1 << 0 -sta_flag_short_preamble = 1 << 1 -sta_flag_wme = 1 << 2 -sta_flag_mfp = 1 << 3 -sta_flag_authenticated = 1 << 4 -sta_flag_tdls_peer = 1 << 5 -sta_flag_associated = 1 << 6 - rate_info = Attributes({ 1: ('bitrate', u16), 2: ('mcs', u8), @@ -149,7 +183,7 @@ sta_info = Attributes({ 14: ('rx_bitrate', rate_info), 15: ('bss_param', bss_param), 16: ('connected_time', u32), - 17: ('sta_flags', Parser('LL', ('mask', 'values'))), + 17: ('sta_flags', StructParser('LL', ('mask', 'values'))), 18: ('beacon_loss', u32), 23: ('rx_bytes_64', u64), 24: ('tx_bytes_64', u64), @@ -162,7 +196,54 @@ nl80211_attr = Attributes({ 46: ('generation', u32), }) -parsed_data = ParsedData(data) -print 'nlmsghdr: %s' % nlmsghdr.Parse(parsed_data) -print 'genlmsghdr: %s' % genlmsghdr.Parse(parsed_data) -print 'nl80211_attr: %s' % nl80211_attr.Parse(parsed_data) +F_REQUEST = 1 << 0 +F_MULTI = 1 << 1 +F_ACK = 1 << 2 +F_ECHO = 1 << 3 +F_DUMP_INTR = 1 << 4 + +F_ROOT = 1 << 8 +F_MATCH = 1 << 9 +F_ATOMIC = 1 << 10 +F_DUMP = F_ROOT | F_MATCH + +CMD_GET_STATION = 17 + +STA_FLAG_AUTHORIZED = 1 << 0 +STA_FLAG_SHORT_PREAMBLE = 1 << 1 +STA_FLAG_WME = 1 << 2 +STA_FLAG_MFP = 1 << 3 +STA_FLAG_AUTHENTICATED = 1 << 4 +STA_FLAG_TDLS_PEER = 1 << 5 +STA_FLAG_ASSOCIATED = 1 << 6 + +ATTR_IFINDEX = 3 + + +genquery = Accumulator() +nlmsghdr.Pack( + genquery, + length=28, # XXX + type=20, # XXX + flags=F_REQUEST | F_ACK | F_DUMP, + seq=random.randint(0, 2 ** 32 - 1), + pid=os.getpid()) +genlmsghdr.Pack( + genquery, + cmd=CMD_GET_STATION, + version=0, + reserved=0) +nl80211_attr.Pack( + genquery, + attrtype=ATTR_IFINDEX, + value=6) + +sock = socket.socket(socket.AF_NETLINK, socket.SOCK_DGRAM, 16) +sock.bind((0,0)) +sock.send(str(genquery)) +data = sock.recv(4096) + +iterator = Iterator(data) +print 'nlmsghdr: %s' % nlmsghdr.Unpack(iterator) +print 'genlmsghdr: %s' % genlmsghdr.Unpack(iterator) +print 'nl80211_attr: %s' % nl80211_attr.Unpack(iterator)