mininet的host該如何連線外網¶
Step 0 關閉防火牆¶
確認防火牆是否啟動,就是因為啟動防火牆我的veth無法傳出DNS協定
sudo firewall-cmd --state
先暫時關閉
sudo systemctl stop firewalld
Step1 啟動mn¶
先啟動mininet 並且用default的拓樸當範例
sudo mn
Step2 找host 1的PID¶
ref:http://www.haifux.org/lectures/299/netLec7.pdf
由於mininet 是設定Nameless namespaces所以ip netns list 是看不到namespaces因為mininet沒有命名
所以我們要先找出”Nameless namespaces” 的PID
執行完成底下的python2程式可以看到跟我類似的輸出,可以看到mininet設定h1的PID為9051,先記下該PID等等會幫該Nameless namespaces命名
9051 net:[4026532791] bash --norc --noediting -is mininet:h1
9051 mnt:[4026532789] bash --norc --noediting -is mininet:h1
9053 net:[4026532853] bash --norc --noediting -is mininet:h2
9053 mnt:[4026532851] bash --norc --noediting -is mininet:h2
ref:https://www.opencloudblog.com/?p=251
該作者程式是python2我改為相容python2與3版本
執行要加sudo
#!/usr/bin/python
#
# List all Namespaces (works for Ubuntu 12.04 and higher)
#
# (C) Ralf Trezeciak 2013-2014
#
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import os
import fnmatch
if os.geteuid() != 0:
print ("This script must be run as root\nBye")
exit(1)
def getinode( pid , type):
link = '/proc/' + pid + '/ns/' + type
ret = ''
try:
ret = os.readlink( link )
except OSError as e:
ret = ''
pass
return ret
#
# get the running command
def getcmd( p ):
try:
cmd = open(os.path.join('/proc', p, 'cmdline'), 'rb').read()
if cmd == '':
cmd = open(os.path.join('/proc', p, 'comm'), 'rb').read()
cmd=str(cmd)
cmd = cmd.replace("\\x00" , ' ')
cmd = cmd.replace("b'" , ' ')
cmd = cmd.replace("'" , ' ')
cmd = cmd.replace('\n' , ' ')
return cmd
except:
return ""
#
# look for docker parents
def getpcmd( p ):
try:
f = '/proc/' + p + '/stat'
arr = open( f, 'rb').read().split()
cmd = getcmd( arr[3] )
if cmd.startswith( '/usr/bin/docker' ):
return 'docker'
except:
pass
return ''
#
# get the namespaces of PID=1
# assumption: these are the namespaces supported by the system
#
nslist = os.listdir('/proc/1/ns/')
if len(nslist) == 0:
print ('No Namespaces found for PID=1')
exit(1)
#print nslist
#
# get the inodes used for PID=1
#
baseinode = []
for x in nslist:
baseinode.append( getinode( '1' , x ) )
#print "Default namespaces: " , baseinode
err = 0
ns = []
ipnlist = []
#
# loop over the network namespaces created using "ip"
#
try:
netns = os.listdir('/var/run/netns/')
for p in netns:
fd = os.open( '/var/run/netns/' + p, os.O_RDONLY )
info = os.fstat(fd)
os.close( fd)
ns.append( '-- net:[' + str(info.st_ino) + '] created by ip netns add ' + p )
ipnlist.append( 'net:[' + str(info.st_ino) + ']' )
except:
# might fail if no network namespaces are existing
pass
#
# walk through all pids and list diffs
#
pidlist = fnmatch.filter(os.listdir('/proc/'), '[0123456789]*')
#print pidlist
for p in pidlist:
try:
pnslist = os.listdir('/proc/' + p + '/ns/')
for x in pnslist:
i = getinode ( p , x )
if i != '' and i not in baseinode:
cmd = getcmd( p )
pcmd = getpcmd( p )
if pcmd != '':
cmd = '[' + pcmd + '] ' + cmd
tag = ''
if i in ipnlist:
tag='**'
ns.append( p + ' ' + i + tag + ' ' + cmd)
except:
# might happen if a pid is destroyed during list processing
pass
#
# print the stuff
#
print ('{0:>10} {1:20} {2}'.format('PID','Namespace','Thread/Command'))
for e in ns:
x = e.split( ' ' , 2 )
print ('{0:>10} {1:20} {2}'.format(x[0],x[1],x[2][:60]))
Step 3 namespaces命名¶
ref:https://gist.github.com/cfra/39f4110366fa1ae9b1bddd1b47f586a3
還記得我們PID為9051,現在要幫該Nameless namespaces命名
new_namespace你可以改成自己喜歡的namespaces
本範例h1的namespace都為new_namespace
網卡interface都假定為:wlp3s0
這個9051要改成你看到的PID
你可能在/run/底下沒有netns資料夾所以新增一個 如果已經有了就不用
sudo mkdir /run/netns/
新增檔案與掛載
sudo touch /run/netns/new_namespace
sudo mount -o bind /proc/9051/ns/net /run/netns/new_namespace
再來我們測試能不能直接使用namespace直接進入h1
sudo ip netns exec new_namespace bash
離開h1
exit
Step 4 開始設定¶
解法1¶
有橋接版本
docker就是依靠此方式建立網路隔離
底下教學來源ref: https://www.itread01.com/content/1543209484.html#2veth_pair_8
建立veth pair
sudo ip link add veth-1 type veth peer name veth-2
把veth-2塞入new_namespace
sudo ip link set dev veth-2 netns new_namespace
啟動veth-1與veth-2 並且對h1也就是new_namespace做設定
sudo ip link set dev veth-1 up
sudo ip netns exec new_namespace ip link set dev veth-2 up
sudo ip netns exec new_namespace ip addr add 10.0.0.1/24 dev veth-2
新增虛擬網橋
sudo ip link add br0 type bridge
sudo ip link set dev br0 up
sudo ip addr add 10.0.0.254/24 dev br0
把veth-1跟br0相連
sudo ip link set dev veth-1 master br0
在new_namespace名稱空間裡面新增預設路由
sudo ip netns exec new_namespace ip route add default via 10.0.0.254 dev veth-2
啟動轉發ipv4流量
echo 1 > /proc/sys/net/ipv4/ip_forward
這邊要注意wlp3s0要改成你可以連外網的interface
新增iptables FORWARD 規則
sudo iptables -A FORWARD --out-interface wlp3s0 --in-interface br0 -j ACCEPT
sudo iptables -A FORWARD --in-interface wlp3s0 --out-interface br0 -j ACCEPT
新增iptables NAT 規則
sudo iptables -t nat -A POSTROUTING --source 10.0.0.0/24 --out-interface wlp3s0 -j MASQUERADE
懶人解法2¶
沒有橋接版本
ref:https://gist.github.com/dpino/6c0dca1742093346461e11aa8f608a99
ref2:https://gist.githubusercontent.com/Lu-Yi-Hsun/708155264a436ca3e33551f26ea25630/raw/3b9f0ce358377cb5c2ecb4dd9a954df586adbe94/ns-inet.sh
下載bash檔¶
wget https://gist.githubusercontent.com/Lu-Yi-Hsun/708155264a436ca3e33551f26ea25630/raw/3b9f0ce358377cb5c2ecb4dd9a954df586adbe94/ns-inet.sh
設定ns-inet.sh為可執行¶
chmod +x ns-inet.sh
執行¶
改成你自己的設定
我上網的網卡是:wlp3s0
我的namespace:new_namespace
!!! note 注意當使用此bash進入namespace後並且離開mininet的xterm h1設定會恢復如果要讓xterm h1可以持續上網就別離開此指令
sudo ./ns-inet.sh wlp3s0 new_namespace
測試¶
ping www.google.com
在host1執行chrome上網
google-chrome-stable --no-sandbox