mn_wifi\examples\double_xxx_xxxx.py
組網圖:
無線自組網實現原理:
對每個node節點(所有的節點的基類都是Node)啟動一個shell進程,記住進程的pid。通過導入的mac80211_hwsim模塊,創建若干個(就是每個node的wlans參數的總和)無線網絡設備(這里包括了phy和dev,phy表示無線網絡設備,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.start和ap.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 =這里包括3個ap和1個controllor
module(nodes, self.n_radios, self.alt_module, **params) // nodes=ap+station;n_radios=這個表示當前的網絡里面總的無線網卡的數量,當前是7;alt_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'這些目錄都被創建了。注意,這里是dev和phy是同時創建好了的,並且是一一對應關系;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'],共計7個phy。
self.assign_iface(nodes, physicalWlans, phys, **params) # 這個函數的作用啟用該無線網絡設備,並分配網絡名字空間。
nodes=3個ap和1個controller
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分別分配成0、1、2…
無論是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啟用索引號為7的wifi設備。
os.system('iw phy %s set netns %s' % (phys[0], node.pid)) // 把這個無線設備放入不同的網絡名字空間。這個命令執行后,phy和dev在root的shell就查詢不到了,應該是加入到node的網絡名字空間了。應該從對應的node的shell里面查詢才能查詢的到了。
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-wlan1;wlan1改成ap1-wlan2;wlan2改成ap2-wlan1;wlan3改成ap2-wlan2;
self.configureWirelessLink() // 這里只對station的無線接口進行配置,所以這個組網不涉及
createVirtualIfaces // 對stations創建虛接口,這個也不涉及
AccessPoint(self.aps, self.driver, self.link)
aps = 3個ap+1個controller; driver = {str} 'nl80211'; link = {type} <class 'mn_wifi.link.wmediumd'>
AccessPoint::__init__
AccessPoint::configure
for ap in aps: // 對node進行遍歷,這里包括了ap和controller
for wlan in range(len(ap.params['wlan'])): // 對node的wlan進行遍歷
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進行遍歷,這里包括了ap和controller
for wlan in range(len(ap.params['wlan'])): // 對node的wlan進行遍歷
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))