wraith-wireless / PyRIC

Python wireless library for Linux
http://wraith-wireless.github.io/PyRIC
Other
92 stars 50 forks source link

Interface Combinations #48

Open fnoop opened 5 years ago

fnoop commented 5 years ago

There doesn't appear to be any way to extract interface combinations, crucial info to create virtual interfaces.

iw list returns (this is for a raspberry pi 3):

    valid interface combinations:
         * #{ managed } <= 1, #{ P2P-device } <= 1, #{ P2P-client, P2P-GO } <= 1,
           total <= 3, #channels <= 2
         * #{ managed } <= 1, #{ AP } <= 1, #{ P2P-client } <= 1, #{ P2P-device } <= 1,
           total <= 4, #channels <= 1

phyinfo() doesn't return this info. With a bit of wrangling it's possible like this:

import pyric
import pyric.pyw as pyw

import pyric.net.wireless.nl80211_h as nl80211h # nl80211 definition
import pyric.lib.libnl as nl                    # netlink (library) functions
import pyric.net.netlink_h as nlh               # netlink definition

def parse_combinations(cs):
    _combinations = []
    try:
       for _, combo in nl.nla_parse_nested(cs.encode()):
            _combodata = {'limits': []}
            for combofield, combovalue in nl.nla_parse_nested(combo):
                if combofield == nl80211h.NL80211_IFACE_COMB_MAXNUM:
                    _combodata['max'] = struct.unpack_from('I', combovalue, 0)[0]
                if combofield == nl80211h.NL80211_IFACE_COMB_NUM_CHANNELS:
                    _combodata['channels'] = struct.unpack_from('I', combovalue, 0)[0]
                if combofield == nl80211h.NL80211_IFACE_COMB_LIMITS:
                    for combo_option in nl.nla_parse_nested(combovalue):
                        combo_option_unpacked = nl.nla_parse_nested(combo_option[1])
                        for combo_option_fragment in combo_option_unpacked:
                            if combo_option_fragment[0] == 1:
                                option_max = struct.unpack_from('I', combo_option_fragment[1], 0)[0]
                            if combo_option_fragment[0] == 2:
                                option_types = [nl80211h.NL80211_IFTYPES[x[0]] for x in nl.nla_parse_nested(combo_option_fragment[1])]
                        _combodata['limits'].append((option_max, option_types))
            _combinations.append(_combodata)
    except Exception as e:
        print("Error: {}".format(repr(e)))
    return _combinations

def combinations(interface):
    card = pyw.getcard(interface)
    nlsock = None
    try:
        # Open netlink socket
        nlsock = nl.nl_socket_alloc(timeout=2)
        try:
            # Request physical device info through netlink
            msg = nl.nlmsg_new(nltype=pyw._familyid_(nlsock),
                               cmd=nl80211h.NL80211_CMD_GET_WIPHY,
                               flags=nlh.NLM_F_REQUEST | nlh.NLM_F_ACK)
            nl.nla_put_u32(msg, card.phy, nl80211h.NL80211_ATTR_WIPHY)
            nl.nl_sendmsg(nlsock, msg)
            rmsg = nl.nl_recvmsg(nlsock)
            # Parse the response and extract supported interface combinations
            _, cs, d = nl.nla_find(rmsg, nl80211h.NL80211_ATTR_INTERFACE_COMBINATIONS, False)
            if d != nlh.NLA_ERROR: return parse_combinations(cs)
            else: return None
        except AttributeError:
            raise pyric.error(pyric.EINVAL, "Invalid Card")
        except nl.error as e:
            raise pyric.error(e.errno, e.strerror)
    except nl.error as e:
        raise pyric.error(e.errno, pyric.strerror(e.errno))
    except pyric.error:
        raise
    finally:
        if nlsock: nl.nl_socket_free(nlsock)

PR to come to add this to phyinfo()