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.intfs和self.ports两个字典来分别维护。
self.ports = {} // 端口和接口有一一映射关系,这个映射关系,通过self.intfs和self.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) // 输入参数里面的wlans是2,这里返回的也是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的两个进程。