mininet-wifi自帶的mesh組網代碼分析


mn_wifi\examples\meshAP.py

 

net.addAccessPoint('ap1', wlans=2, ssid='ssid91,', position='10,10,0')

設置的默認值如下:{'ssid': 'ssid91,', 'listenPort': None, 'inNamespace': False, 'mode': 'g', 'wlans': 2, 'position': '10,10,0', 'channel': 1}

cls=<class 'mn_wifi.node.UserAP'>

UserAP::__init__

AP::__init__

Node_wifi::__init__

Node::checkSetup

UserAP::setup

判斷 /dev/net/tun 文件在沒在,當前是存在的。

存在,不做任何處理,返回。

cls.isSetup = True  // 把當前類的isSetup標志設置為True

Node_wifi.name = 'ap1'

self.intfs = {}    // 端口和接口有一一映射關系,這個映射關系,通過self.intfsself.ports兩個字典來分別維護。

self.ports = {}    // 端口和接口有一一映射關系,這個映射關系,通過self.intfsself.ports兩個字典來分別維護。

self.wlanports = -1    // wlan interfaces to port numbers

self.nameToIntf = {}   // 所有接口名稱

self.isStationary = True

Node::startShell()  // 啟動一個shell進程(該進程默認關閉描述符,並從tty上分離開來),等待接收傳入的命令,句柄被發送給self.shell上。

opts = '-cd'

cmd = ['mnexec', '-cd', 'env', 'PS1=\x7f', 'bash', '--norc', '--noediting', '-is', 'mininet:ap1']

self.master = 10, self.slave = 11  // 通過操作系統的openpty函數獲得 master為10,slave為11.openpty函數打開一個偽終端對,返回 pty 和 tty的文件描述符

self.shell = self._popen( cmd, stdin=self.slave, stdout=self.slave, stderr=self.slave, close_fds=False ) // 這個函數在創建交換機和控制器的時候,這里是創建一個shell進程,並沒有執行cmd的相關命令。

Popen::__init__()

Popen::_execute_child()   // 創建一個子進程,返回子進程的進程id

args = ['mnexec', '-cd', 'env', 'PS1=\x7f', 'bash', '--norc', '--noediting', '-is', 'mininet:ap1']

executable = 'mnexec'

errpipe_read, errpipe_write  = Popen::pipe_cloexec()

r, w = os.pipe()     // os.pipe() 方法用於創建一個管道, 返回一對文件描述符(r, w) 分別為讀和寫。

// 所謂管道,是指能夠連接一個寫進程和一個讀進程的、並允許它們以生產者—消費者方式進行通信的一個共享文件,又稱為pipe文件。

// 由寫進程從管道的寫入端(句柄1)將數據寫入管道,而讀進程則從管道的讀出端(句柄0)讀出數據。

r=12,w=13

self._set_cloexec_flag(r)  // 設置文件描述符標志,這里設置為1

self._set_cloexec_flag(w)  // 設置文件描述符標志,這里設置為1

gc_was_enabled = gc.isenabled() //gc模塊是python垃圾回收機制的接口模塊,可以通過該module啟停垃圾回收、調整回收觸發的閾值、設置調試選項

// 獲取當前的垃圾回收模塊的使能狀態

gc.disable()                    // 禁止垃圾回收

self.pid = os.fork()   // 創建子進程,並返回子進程的pid,self.pid=6108

self._child_created = true

gc.enable()                    // 打開垃圾回收開關

如果 self.pid = 0

執行接下來的很多操作

os.execvp  操作系統使用新的進程執行相關命令

os.close(errpipe_write)   // 關閉管道的寫文件描述符

os._exit(255)  // 用於在線程中退出

data = os.read(errpipe_read)  從上面管道的文件描述符里面讀取數據

如果data不為空

os.waitpid // 清除上面創建的子進程。

self.stdin = os.fdopen( self.master, 'r' )  該方法用於通過文件描述符 fd 創建一個文件對象,並返回這個文件對象

self.stdout = self.stdin

self.pid = self.shell.pid  // 把創建的shell的進程id,賦值給當前node的進程id

self.pollOut = select.poll()

self.pollOut.register( self.stdout )  // 注冊一個文件描述符,注冊后,可以通過poll()方法來檢查是否有對應的I/O事件發生

self.outToNode[ self.stdout.fileno() ] = self   // 這兩個的意思應該是后續的針對該node的輸入輸出就和當前的node對象關聯上了。

self.inToNode[ self.stdin.fileno() ] = self

用os.read讀取數據

self.cmd( 'unset HISTFILE; stty -echo; set +m' )  //  執行這個命令

args = unset HISTFILE; stty -echo; set +m  // unset HISTFILE 清除歷史記錄,stty -echo,禁止回顯,set +m,打開監視模式。

Node::sendCmd()

os.write( self.stdin.fileno(), encode( data ) )   // 調用操作系統的把數據寫入文件句柄。寫入的就是當前node對應的shell進程的stdin(標准輸入)

Node::mountPrivateDirs

self.dpid = self.defaultDpid(dpid)

nums = re.findall(r'\d+', self.name)  // 返回string中所有與pattern相匹配的全部字串,返回形式為數組.  nums=1

dpid = 1

返回值 = '100000000001'    // 總計12位的字符串

self.listenPort = None

if not self.inNamespace:     // 如果沒有名字空間,就分配一個控制器側的接口。

self.controlIntf = Intf('lo', self, port=0)   // 這個應該是分配當前AP的控制器側接口。分配的接口名稱是'lo'

Intf::__init__

ip = {str} '127.0.0.1'

link = {NoneType} None

mac = {NoneType} None

name = {str} 'lo'

node = {UserAP} ap1

prefixLen = {int} 8

Node_wifi::addIntf(self, intf, port=None, moveIntfFn=moveIntf)

self.intfs[port=0] = {intf} lo

self.ports[intf] = port = 0

self.nameToIntf[intf.name] = intf

Intf::config() // 這里根據參數配置接口,這里參數為空,沒有做任何配置。

pathCheck('ofdatapath', 'ofprotocol', moduleName='the OpenFlow reference user switch' + '(openflow.org)')

moduledeps.pathCheck

util.quietRun

cmd = 'which ofdatapath'    // 這個命令是查詢ofdatapath程序在哪個目錄。

util.errRun( cmd )

popen = Popen( cmd, stdout=PIPE, stderr=stderr, shell=shell )

data = '/usr/local/bin/ofdatapath'

判斷上述目錄有對應的文件,就表示這個模塊安裝了。否則執行exit(1)退出程序。

self.opts = ' --listen=punix:/tmp/ap1.listen'   // 設置AP的選項參數,后續啟動 ofprotocol 進程的時候,會使用到這個參數。

self.dpopts = '--no-slicing'     // 設置datapath的選項參數。后續啟動 ofdatapath 進程的時候,會使用到這個參數。

self.nameToNode['ap1'] = {UserAP} ap1

Mininet_wifi::addParameters(ap, self.autoSetMacs, node_mode='master', **defaults)

params['wlans'] = self.countWiFiIfaces(**params)  //   輸入參數里面的wlans2,這里返回的也是2.

self.n_radios  // 這個表示當前的net中總的無線接口數量。

node.params['wlan'] = []

node.params['mac'] = []

node.phyID = []

node.ifaceToAssociate = 0

參數中有 'position',設置 node.params['position']

for wlan in range(params['wlans']):  // 1,2兩個wlan進行遍歷。

node.func.append('none')

node.phyID.append(0)

node.params['wlan']里面添加上 'ap1-wlan1'   // 第二次循環添加上 'ap1-wlan2'添加后的node.params如下:————這里只是給node分配wlan的名稱,並沒有實際創建或者分配wlan的接口。

node.params = {'wlan': ['ap1-wlan1', 'ap1-wlan2'], 'ssid': 'ssid91,', 'mac': [], 'mode': 'g', 'position': [10.0, 10.0, 0.0], 'channel': 1}

把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 = {'wlan': ['ap1-wlan1', 'ap1-wlan2'], 'ssid': ['ssid91', ''], 'antennaHeight': [1.0, 1.0], 'range': [0, 0], 'stationsInRange': {}, 'antennaGain': [5.0, 5.0], 'txpower': [14, 14], 'mac': [''], 'mode': ['g', 'g'], 'associatedStations': [], 'position': [10.0, 10.0, 0.0], 'freq': [2.412, 2.412], 'channel': ['1', '1']}

self.aps.append(ap)

net.addAccessPoint函數執行完成。

 

net.addController('c0', ip="192.168.0.193", port=6653)

設置controller類為 <class 'mininet.node.RemoteController'>,因為在初始化mininet-wifi的時候,創建的就是遠程控制器。

RemoteController::__init__

Controller::__init__

cargs = {str} '-v ptcp:%d'    // 啟動控制器的時候會使用到該參數

cdir = {NoneType} None

command = {str} 'controller'

inNamespace = {bool} False

ip = {str} '192.168.0.193'

name = {str} 'c0'

port = {int} 6653

protocol = {str} 'tcp'

Node::__init__

self.checkSetup()

self.intfs = {}

self.ports = {}

self.nameToIntf = {}

Node::startShell()   // 見添加AP的過程。和上面是一樣的,啟動一個shell進程。

self.controllers.append( controller_new )

self.nameToNode[ name ] = controller_new

 

net.configureWifiNodes()

if not self.ppm_is_set

Node_wifi::setPropagationModel

module(nodes, self.n_radios, self.alt_module, **params)  // nodes=ap+station,n_radios=這個表示當前的網絡里面總的無線網卡的數量,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等於4.

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 = ['wlan11', 'wlan10', 'wlan9', 'wlan8', 'wlan7', 'wlan6', 'wlan5', 'wlan4', 'wlan3', 'wlan2', 'wlan1', 'wlan0', 'ap2-mp2', 'ap2-wlan2', 'ap2-wlan1', 'ap1-mp2', 'ap1-wlan2', 'ap1-wlan1']

返回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'    // 列出 mn00s00~mn00s05、mn01s00~mn01s05 這些子目錄。

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

返回['mn00s00', 'mn00s01', 'mn00s02', 'mn00s03', 'mn00s04', 'mn00s05', 'mn01s00', 'mn01s01', 'mn01s02', 'mn01s03', 'mn01s04', 'mn01s05', 'mn02s00', 'mn02s01', 'mn02s02', 'mn02s03', 'mn02s04', 'mn02s05', '']

對phys進行遍歷

phy.startswith(self.prefix)  prefix=='mn00s'   Python startswith() 方法用於檢查字符串是否是以指定子字符串開頭,如果是則返回 True,否則返回 False

分別執行如下命令,

cmd = ['hwsim_mgmt', '-c', '-n', 'mn03s00']   // 這些命令是使用導入的模塊創建 mac80211_hwsim 設備

...

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

module::get_phy

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

返回 ['mn00s00' ~ 'mn00s05', 'mn01s00' ~ 'mn01s05', 'mn02s00' ~ 'mn02s05', 'mn03s00' ~ 'mn03s05']

self.assign_iface(nodes, physicalWlans, phys, **params)  # iface assign

nodes=2個station+2個ap

physicalWlans = ['wlan11', 'wlan10', 'wlan9', 'wlan8', 'wlan7', 'wlan6', 'wlan5', 'wlan4', 'wlan3', 'wlan2', 'wlan1', 'wlan0', 'ap2-mp2', 'ap2-wlan2', 'ap2-wlan1', 'ap1-mp2', 'ap1-wlan2', 'ap1-wlan1']

self.get_wlan_iface(physicalWlans)

下發 iw dev 2>&1 | grep Interface | awk '{print $2}  命令查詢所有的接口,這里返回的是除了上述的物理接口還包括['wlan12', 'wlan13', 'wlan14', 'wlan15', 'wlan16', 'wlan17']

返回 ['wlan12', 'wlan13', 'wlan14', 'wlan15', 'wlan16', 'wlan17']

對nodes進行遍歷

如果是AP

如果是ap1,就把wlan14修改成ap1-wlan1;把wlan15修改成ap1-wlan2

如果是ap2,就把wlan16修改成ap2-wlan1;把wlan17修改成ap2-wlan2

如果是station:

sta1 的 wlan='sta1-wlan0'

rfkill = subprocess.check_output(rfkill list | grep mn00s00 | awk \'{print $1}\'| tr -d ":")   rfkill list  #對應編號的wifi設備

os.system('rfkill unblock %s' % rfkill[0])  // #打開對應編號的設備

os.system('iw phy %s set netns %s' % (phys[0], node.pid))  netns(net namespace)可以讓一台機器上模擬多個網絡設備,是網絡虛擬化的重要組成,將不同類型的網絡應用隔離

node.cmd(ip link set wlan12 down)     //  node.cmd該函數能在節點所在的進程shell上執行輸入的命令

node.cmd(ip link set wlan12 name ap1-wlan0)    // 改名,如果是sta2,就把wlan13修改成sta2-wlan0

 

configureWirelessLink()

對stations進行遍歷

link = TCLinkWirelessStation(node, intfName1='sta1-wlan0')

WirelessLinkStation.__init__(self, node1, port1=port1, intfName1=intfName1, cls1=TCWirelessLink,

params1[ 'port' ] = node1.newPort()

返回數字 0,應該表示0號端口。

IntfWireless::__init__

Node_wifi::addIntf()

這里只是把接口名稱 sta1-wlan0和端口0,添加到node的屬性里面去。

這里獲取的link就是 {TCLinkWirelessStation} sta1-wlan0<->wifi

configureMacAddr()

Node::setMAC (sta1-wlan0,'00:00:00:00:00:11')

IntfWireless::setMAC

self.cmd('ip link set down')

self.cmd('ip link set address  '00:00:00:00:00:11' ') 設置mac地址

self.cmd('ip link set up')

createVirtualIfaces  // 對stations創建虛接口

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

aps = 2個ap; driver = {str} 'nl80211'; link = {type} <class 'mn_wifi.link.wmediumd'>

AccessPoint::__init__

AccessPoint::configure

for ap in aps:

cls.configAP(ap, wlan)  // 配置ap的wlan參數

TCLinkWirelessAP::__init__

WirelessLinkAP.__init__

IntfWireless::__init__() 參數是:node = {UserAP} ap1;name = {str} 'ap1-wlan1';port = {int} 1

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()

for ap in aps:

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

wmediumd::__init__

wmediumd::configureWmediumd 下面是對ap和station都操作了的

設置 wmediumd 的位置和發射功率

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

w_starter::start

 

net.addLink(sta1, ap1)

cls = <class 'mn_wifi.link.wmediumd'>

self.infraAssociation(node1, node2, port1, port2, cls, **params)

Association.associate(sta, ap, enable_wmediumd,enable_interference, **params) 關聯到AP

configureWirelessLink

dist = sta.get_distance_to(ap)  查詢sta到ap的距離

cls.associate_infra(sta, ap, **params)

Association.associate_noEncrypt(sta, ap, wlan, ap_wlan)  // 無加密連接

cmd = iwconfig sta1-wlan0 essid ssid91 ap 02:00:00:00:02:00  iwconfig命令用於系統配置無線網絡設備或顯示無線網絡設備信息。

iwconfig命令類似於ifconfig命令,但是他配置對象是無線網卡,它對網絡設備進行無線操作,如設置無線通信頻段。

essid 設置ESSID;ap 強迫無線網卡向給定地址的接入點注冊

Association.update(sta, ap, wlan)

更新ap和sta的部分參數,主要是更新sta和ap的相互關聯關系。

 

net.addLink(ap1, intf='ap1-wlan2', cls=mesh, ssid='mesh-ssid', channel=5)

mesh::__init__

設置ap的ssid參數為 'mesh-ssid'

mesh.name = 'ap1-wlan2'

setMeshIface

mesh.name = 'ap1-mp2'

IntfWireless::setType 

iw dev %s interface add %s type %s

IntfWireless::setMAC  '02:00:00:00:03:00'  設置接口的mac地址

ap1.cmd('ip link set ap1-wlan2 down') 

node.params['wlan'][wlan] = self.name   把ap1的wlan的第二個由'ap1-wlan0'修改為'ap1-mp2'

IntfWireless::setChannel

cmd = iw dev ap1-mp2 set channel 5  設置接口的通道

self.ipLink('up')

configureMesh(self, node, wlan, **params):

node.func[wlan] = 'mesh'

self.associate(node, wlan, **params)  // Performs Mesh Association

self.join('mesh', ssid, freq, ht_cap, name)

cmd = iw dev ap1-mp2 mesh join mesh-ssid freq 2432    // 把該接口加入到mesh網絡里面去。

TCLinkWirelessStation::__init__

WirelessLinkStation.__init__

IntfWireless::__init__

建了接口和port之間的映射關系。intf = {TCWirelessLink} ap1-mp2;port = {int} 3

 

ap1.start([c0])

UserAP::start

cmd = ofdatapath -i ap1-wlan1,ap1-wlan2,ap1-mp2 punix:/tmp/ap1 -d 100000000001 --no-slicing 1> /tmp/ap1-ofd.log 2> /tmp/ap1-ofd.log &  // 100000000001是自己的pid

cmd = ofprotocol unix:/tmp/ap1 tcp:192.168.0.193:6653 --fail=closed  --listen=punix:/tmp/ap1.listen 1> /tmp/ap1-ofp.log 2>/tmp/ap1-ofp.log &

執行上述命令啟動ofdatapath、ofprotocol這兩個進程,這兩個進程就是ofsoftswitch的兩個進程。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM