mininet-wifi无线自组网实现原理及代码分析

 

mn_wifi\examples\double_xxx_xxxx.py

 

组网图:

 

 

 

无线自组网实现原理:

对每个node节点(所有的节点的基类都是Node)启动一个shell进程,记住进程的pid。通过导入的mac80211_hwsim模块,创建若干个(就是每个nodewlans参数的总和)无线网络设备(这里包括了phydevphy表示无线网络设备,dev表示接口,他们是一一对应的关系),把每个网络设备加入到名称为"某个进程id"的网络名字空间里面去。把接口改一下名称(比如ap1-wlan1改成ap1-mp1)。用hostapd这个程序把每个无线接口配置好,启动这个无线热点。addlink的时候,如果是station就使用iwconfig命令把当前的station加入到某个热点。如果是ap并且是mesh组网的话,使用类似于“iw dev ap1-mp2 mesh join mesh-ssid freq 2432”这样的命令把这个热点加入到mesh网络里面去。然后在controller.startap.start的时候,在对应的shell里面把相关的进程拉起来。这个就完成了整个网络的搭建过程。

 

代码分析:

前提,大部分和mininet-wifi自带的mesh组网的代码的流程相同。下面只讲差异点,mininet-wifi自带mesh组网代码分析见下面的连接:

https://www.cnblogs.com/matthew-2013/p/13043617.html

 

net.addParameters(c1, False, node_mode='master', **defaults)

疑问:为什么这里的wlans=1控制器的输入参数里面没有wlans参数啊,1是从哪里来的呢?

node.func.append('none')

node.phyID.append(0)

node.params['wlan']里面添加上'c1-wlan1'————这里只是给node分配wlan的名称,并没有实际创建或者分配wlan的接口。

node.params['ip'] = ['']

node.params['mac'] = ['']

把node.params 中的 'antennaGain=5.0', 'antennaHeight=1.0', 'txpower=14', 'channel=1', 'mode=g', 'freq=2.412' 参数,设置为默认值。

self.add_range_param(node, **params)  // 把range参数初始化为[0,0]

if node_mode == 'master':

node.params['associatedStations'] = []

node.params['stationsInRange'] = {}

node.params['mac'] = []

node.params['mac'].append('')

设置 node.params['ssid'] 为输入的参数。注意:无线自组网的ssid有可能是这样的 ['','2']

node.params['ssid']=['new-ssid']

 

net.configureWifiNodes()

nodes =这里包括3ap1controllor

module(nodes, self.n_radios, self.alt_module, **params)  // nodes=ap+stationn_radios=这个表示当前的网络里面总的无线网卡的数量,当前是7alt_module=none,

module::__init__

module::start

h = subprocess.check_output // subprocess模块用来创建新的进程,连接到其stdin、stdout、stderr管道并获取它们的返回码在子进程执行命令,以字符串形式返回执行结果的输出。如果子进程退出码   不是0,抛出subprocess.CalledProcessError异常,异常的output字段包含错误输出

cmd = ps -aux | grep -ic hostapd  //   统计hostapd进程的数量

process = Popen(stdout=PIPE, *popenargs, **kwargs) // PIPE=-1, popenargs = {tuple: 1} ps -aux | grep -ic 'hostapd', kwargs = {dict: 1} {'shell': True}

但是这个好像也没有执行这个命令。

output, unused_err = process.communicate()  // 在这里开启一个子进程,执行上述命令的。ps -aux | grep -ic 'hostapd'  == 统计hostapd进程的数量。

if h >= 2:  // 这里的h等于2.

os.system('pkill -f \'hostapd\'')   // kill掉 hostapd 相关进程。这里为什么要关闭hostapd进程呢?

physicalWlans = module::get_physical_wlan()

wlans = subprocess.check_output("iw dev 2>&1 | grep Interface | awk '{print $2}'", shell=True)).split("\n")

iw dev 2>&1 ————该命令列出了如下接口。

awk '{print $2}'  表示把上述内如输出到 标准错误 里面去。

wlans = ['']  // 这种组网模式下,获取的接口是空的,为什么呢?

返回wlans  // 这里返回的也是空的。

self.load_module(n_radios, nodes, alt_module, **params)    // 初始化wifi模块 n_radios=6,nodes=2个station+2个ap

output_ = os.system('modprobe mac80211_hwsim radios=0 >/dev/null 2>&1')  // 导入 mac80211_hwsim 模块,这个就是wifi模块。

self.__create_hwsim_mgmt_devices(n_radios, nodes, **params)

cmd = 'find /sys/kernel/debug/ieee80211 -name hwsim | cut -d/ -f 6 | sort'    // 列出 phy

phys = subprocess.check_output(cmd, shell=True).split("\n")  // 执行cmd命令

返回['']  // 返回的phy为空,为什么?

对phys进行遍历

phy.startswith(self.prefix)  prefix=='mn00s'   Python startswith() 方法用于检查字符串是否是以指定子字符串开头,如果是则返回 True,否则返回 False

for i in range(0, n_radios): 当前的n_radios=7

分别执行如下命令,

cmd = ['hwsim_mgmt', '-c', '-n', 'mn00s00']   // 这些命令是使用导入的模块创建名称为'mn00s00'mac80211_hwsim 设备。这些命令执行之后,在/sys/kernel/debug/ieee80211/目录下可以看到'mn00s00'这些目录都被创建了。注意,这里是devphy是同时创建好了的,并且是一一对应关系;phy应该是表示无线网络设备,而dev表示无线网络接口。

...

cmd = ['hwsim_mgmt', '-c', '-n', 'mn00s06']

 

module::get_phy

phy = subprocess.check_output("find /sys/kernel/debug/ieee80211 -name hwsim | cut -d/ -f 6 | sort"

返回 ['mn00s00' ~ 'mn00s06'],共计7phy

self.assign_iface(nodes, physicalWlans, phys, **params)  # 这个函数的作用启用该无线网络设备,并分配网络名字空间。

nodes=3个ap1controller

physicalWlans = []  // 上面获取的physicalWlans为空。

wlan_list = self.get_wlan_iface(physicalWlans)// 查询到 wlan0~wlan6 这7个接口

下发 iw dev 2>&1 | grep Interface | awk '{print $2}  命令查询所有的接口

返回 [wlan0~wlan6 ]7个接口。

for node in nodes:   // 对nodes进行遍历

for wlan in range(0, len(node.params['wlan'])):   // 对当前节点的wlan参数进行遍历。

node.phyID[wlan] =0  // 把该节点的物理id分别分配成012…

无论是ap还是controller都做如下操作:

rfkill = subprocess.check_output(rfkill list | grep mn00s00 | awk '{print $1}'| tr -d ":")  // rfkill list  #列出所有可用wifi设备,返回的是rfkill list的前面的哪个序号。

mn00s00前面的索引号是7,所以返回的是 ['7','']

os.system('rfkill unblock %s' % rfkill[0])// rfkill unblock启用索引号为7wifi设备。

os.system('iw phy %s set netns %s' % (phys[0], node.pid))    // 把这个无线设备放入不同的网络名字空间。这个命令执行后,phydevrootshell就查询不到了,应该是加入到node的网络名字空间了。应该从对应的nodeshell里面查询才能查询的到了。

node.cmd('ip link set %s down' % wlan_list[0])   // 关闭wlan0这个无线网络接口。

node.cmd('ip link set %s name %s' % (wlan_list[0], node.params['wlan'][wlan]))  // 改名,wlan0改成ap1-wlan1wlan1改成ap1-wlan2wlan2改成ap2-wlan1wlan3改成ap2-wlan2

self.configureWirelessLink()  // 这里只对station的无线接口进行配置,所以这个组网不涉及

createVirtualIfaces  // 对stations创建虚接口,这个也不涉及

AccessPoint(self.aps, self.driver, self.link)

aps = 3个ap+1controller; driver = {str} 'nl80211'; link = {type} <class 'mn_wifi.link.wmediumd'>

AccessPoint::__init__

AccessPoint::configure

for ap in aps:    // node进行遍历,这里包括了apcontroller

for wlan in range(len(ap.params['wlan'])):  // nodewlan进行遍历

cls.configAP(ap, wlan)  // 配置ap的wlan参数

TCLinkWirelessAP::__init__

WirelessLinkAP.__init__

params1[ 'port' ] = node1.newPort()  // 这里只是分配一个int类型的端口号。

intf1 = IntfWireless::__init__()   // 创建一个名称叫 'ap1-wlan1'的接口(接口这个类只是mininet的一个类),给这个接口分配一个int类型的端口号。

node.addIntf(self, port=port)

// 把端口1和接口'ap1-wlan1'建立映射关系

cls.setIPMAC(node, wlan) 

// 使用命令  ip addr show ap1-wlan1  mac地址:02:00:00:00:02:00

checkNetworkManager(cls, mac)  // add mac address into /etc/NetworkManager/NetworkManager.conf                                                       

restartNetworkManager()

nm_is_running = os.system('service network-manager status 2>&1 | grep -ic running >/dev/null 2>&1')  // 查询network-manager服务的状态,这里没有做什么。

for ap in aps: // node进行遍历,这里包括了apcontroller

for wlan in range(len(ap.params['wlan'])):  // nodewlan进行遍历

cls.setConfig(ap, aps, wlan, link)

cls.setHostapdConfig(ap, wlan, aplist, link)

cls.APConfigFile(cmd, ap, wlan)

ap = {UserAP} ap1; wlan = {int} 0

cmd = {str} 'echo \'interface=ap1-wlan1\ndriver=nl80211\nssid=ssid91\nwds_sta=1\nhw_mode=g\nchannel=1\nctrl_interface=/var/run/hostapd\nctrl_interface_group=0'

content = 'echo \'interface=ap1-wlan1\ndriver=nl80211\nssid=ssid91\nwds_sta=1\nhw_mode=g\nchannel=1\nctrl_interface=/var/run/hostapd\nctrl_interface_group=0'  > mn10379_ap1-wlan1.apconf'

ap.cmd(content)  // 把 上面的语句写入 mn10379_ap1-wlan1.apconf 文件

cmd = hostapd -B mn10379_ap1-wlan1.apconf    // 启动 hostapd; hostapd是一个带加密功能的无线接入点程序,通过这个配置配置这个无线接入点。

ap.cmd(cmd)  // 执行上面的命令

self.configureWmediumd()   //当前配置的无线模式是干扰模式,也就是可以人为插入噪音的

mob.wmediumd_mode = 3

MyWmediumd::__init__ ()  // 这里的参数有噪音门限等。这个类是自己实现的类。

MyWmediumd::configureWmediumd 下面是对ap和controller都操作了的

设置 wmediumd 的位置和发射功率

start_wmediumd(intfrefs, wmediumd.links, wmediumd.positions,fading_coefficient, noise_threshold,wmediumd.txpowers, isnodeaps, propagation_model,maclist)

w_starter::start

w_starter::initialize

cmdline = ['wmediumd', '-l', '4', '-s', '-c', '/tmp/mn_wmd_config_p_sV91.cfg', '-x', '/home/zhangmeng/mininet-wifi/mn_wifi/data/signal_table_ieee80211']

执行这个命令

w_server.connect()

启动一个socket,并连接到 '/var/run/wmediumd.sock' 上面去。

for node in nodes:

for wlan in range(0, len(node.params['wlan'])):

node.params['range'][wlan] = node.getRange(intf=intf) // 设置node的range参数。

node.setTxPower(node.params['txpower'][wlan],intf=node.params['wlan'][wlan],setParam=setParam)  // 根据node的 txpower 参数信息,设置节点的发射功率

cmd = iw dev ap1-wlan1 set txpower fixed 1400

执行上面的命令,设置发射功率

node.setAntennaGain(node.params['antennaGain'][wlan],intf=node.params['wlan'][wlan],setParam=setParam)  // 根据node的 antennaGain 参数信息,设置节点的天线增益

setGainWmediumd

w_server.update_gain(w_gain(self.wmIface[wlan], int(gain_)))

ret = w_server.send_gain_update(gain)

cls.sock.send(cls.__create_gain_update_request(gain))

 

posted @ 2020-06-06 17:21  matthew2015  阅读(3127)  评论(1编辑  收藏  举报