SDN网络搭建
1.准备工作
1)虚拟机vmware(注意版本,高版本的系统克隆之后,在低版本上不兼容)
2)Ubuntu18.04 64位
3)配置尽量好一点
2.安装mininet(这里介绍的是下载源码的方法)
1)更新软件,注意前面需要加上sudo(super user do,获取超级管理员权限)
apt-get update
apt-get upgrade
2)打开Ubuntu终端,首先安装git命令
apt-get install git
3)从github上获取mininet源码
git clone git://github.com/mininet/mininet
4)查看版本
cd mininet
git tag
5)选择版本
git checkout -b tag_name
- 安装mininet
cat INSTALL
切换到mininet文件下
cd mininet
这里有多个安装选项:mininet/util/install.sh[options]
“-a”:完整安装包括Mininet VM,还包括如Open vSwitch等依赖关系软件,以及像的OpenFlow Wireshark和POX。默认情况下,这些工具将被安装在你的home目录中。完整安装命令:
./util/install.sh -a
“-nfv”:安装Mininet、基于OpenFlow的交换机和Open vSwitch。命令:
./util/install.sh -nfv
“-s mydir” :使用此选项可将源代码建立在一个指定的目录中,而不是在home目录中。另外,你只想安装OpenFlow1.3和Open vSwitch2.3.0,可以使用安装命令:
./util/install.sh -s mydir
./util/install.sh -n3V 2.3.0
6)简单测试mininet的基本基本功能
sudo mn --test pingall
最好换一下源,换源参考:
3.安装控制器(这里介绍的是opendaylight,还有floodlight,ryu等控制器)
1)安装odl依赖包
sudo apt-get update
a) 基础包
sudo apt-get install unzip lrzsz
b) jdk
sudo apt-get install openjdk-8-jdk
c) 设置java环境变量
gedit /etc/environment
文件最末尾增加一行:
JAVA_HOME="/usr/lib/jvm/java-8-openjdk-amd64"
当前终端需要退出ubuntu然后重新登入,才能加载该环境变量
2)下载ODL包
从官网下载:https://www.opendaylight.org/downloads
(我用的是碳版本)
3)解压配置
tar -zxvf distribution-karaf-0.6.0-Carbon.tar.gz
解压之后进入distribution-karaf-0.6.1-Carbon目录, 修改etc/org.apache.karaf.management.cfg文件的以下两行内容:
rmiRegistryHost = 127.0.0.1
rmiServerHost = 127.0.0.1
4)运行odl
进入distribution-karaf-0.6.1-Carbon目录
./bin/karaf
5)安装功能组件
opendaylight-user@root>feature:install odl-restconf
opendaylight-user@root>feature:install odl-l2switch-switch-ui
opendaylight-user@root>feature:install odl-openflowplugin-flow-services-ui
opendaylight-user@root>feature:install odl-mdsal-apidocs
opendaylight-user@root>feature:install odl-dluxapps-applications
opendaylight-user@root>feature:install odl-faas-all
6)登录管理WEB UI
http://localhost:8181/index.html
用户名和密码都是admin
4.配置mininet,自定义拓扑
1)cd mininet/custom
vim topo-2sw-2host.py #编辑4个主机和4个交换机相连
2)sudo mn --custom topo.py --topo mytopo --controller=remote --switch ovsk,protocols=OpenFlow13 #运行,需要打开控制器才能ping通
3)打开浏览器,输入网址:http://<yourMachineIP>:8181/index.html,可以看到定义的网络拓扑结构等相关信息。
5.负载均衡
# -*- coding: utf-8 -*-
import httplib2
import time
import json
class OdlUtil:
url = ''
def __init__(self, host, port):
self.url = 'http://' + host + ':' + str(port)
def install_flow(self, container_name='default',username="admin", password="admin"):
http = httplib2.Http()
http.add_credentials(username, password)
headers = {'Accept': 'application/json'}
flow_name = 'flow_' + str(int(time.time()*1000))
#s4流表
#h2默认发往s4的1口的流表,优先级最高
h2_to_s4_1 ='{"flow": [{"id": "41","match": {"ethernet-match":'\
'{"ethernet-type": {"type": "2048"}},'\
'"ipv4-source":"10.0.0.2/32","ipv4-destination": "10.0.0.1/32"},'\
'"instructions": {"instruction": [{"order": "0",'\
'"apply-actions": {"action": [{"output-action": {'\
'"output-node-connector": "1"},"order": "0"}]}}]},'\
'"priority": "115","cookie": "1","table_id": "0"}]}'
lh2_to_s4_1 ='{"flow": [{"id": "41","match": {"ethernet-match":'\
'{"ethernet-type": {"type": "2048"}},'\
'"ipv4-source":"10.0.0.2/32","ipv4-destination": "10.0.0.1/32"},'\
'"instructions": {"instruction": [{"order": "0",'\
'"apply-actions": {"action": [{"output-action": {'\
'"output-node-connector": "2"},"order": "0"}]}}]},'\
'"priority": "105","cookie": "1","table_id": "0"}]}'
#h3默认发往s4的1口的流表,优先级最高
h3_to_s4_1 ='{"flow": [{"id": "51","match": {"ethernet-match":'\
'{"ethernet-type": {"type": "2048"}},'\
'"ipv4-source":"10.0.0.3/32","ipv4-destination": "10.0.0.1/32"},'\
'"instructions": {"instruction": [{"order": "0",'\
'"apply-actions": {"action": [{"output-action": {'\
'"output-node-connector": "1"},"order": "0"}]}}]},'\
'"priority": "115","cookie": "1","table_id": "0"}]}'
#h4默认发往s4的1口的流表,优先级最高
h4_to_s4_1 ='{"flow": [{"id": "61","match": {"ethernet-match":'\
'{"ethernet-type": {"type": "2048"}},'\
'"ipv4-source":"10.0.0.4/32","ipv4-destination": "10.0.0.1/32"},'\
'"instructions": {"instruction": [{"order": "0",'\
'"apply-actions": {"action": [{"output-action": {'\
'"output-node-connector": "1"},"order": "0"}]}}]},'\
'"priority": "115","cookie": "1","table_id": "0"}]}'
#s4的1口流量满载时h2发的数据包,2,3端口出的流表项优先级高
h2_to_s4_2 ='{"flow": [{"id": "42","match": {"ethernet-match":'\
'{"ethernet-type": {"type": "2048"}},'\
'"ipv4-source":"10.0.0.2/32","ipv4-destination": "10.0.0.1/32"},'\
'"instructions": {"instruction": [{"order": "0",'\
'"apply-actions": {"action": [{"output-action": {'\
'"output-node-connector": "2"},"order": "0"}]}}]},'\
'"priority": "112","cookie": "1","table_id": "0"}]}'
h2_to_s4_3 ='{"flow": [{"id": "43","match": {"ethernet-match":'\
'{"ethernet-type": {"type": "2048"}},'\
'"ipv4-source":"10.0.0.2/32","ipv4-destination": "10.0.0.1/32"},'\
'"instructions": {"instruction": [{"order": "0",'\
'"apply-actions": {"action": [{"output-action": {'\
'"output-node-connector": "3"},"order": "0"}]}}]},'\
'"priority": "110","cookie": "1","table_id": "0"}]}'
#s4的1口流量满载时h3发的数据包,2,3端口出的流表项优先级高
h3_to_s4_2 ='{"flow": [{"id": "52","match": {"ethernet-match":'\
'{"ethernet-type": {"type": "2048"}},'\
'"ipv4-source":"10.0.0.3/32","ipv4-destination": "10.0.0.1/32"},'\
'"instructions": {"instruction": [{"order": "0",'\
'"apply-actions": {"action": [{"output-action": {'\
'"output-node-connector": "2"},"order": "0"}]}}]},'\
'"priority": "112","cookie": "1","table_id": "0"}]}'
h3_to_s4_3 ='{"flow": [{"id": "53","match": {"ethernet-match":'\
'{"ethernet-type": {"type": "2048"}},'\
'"ipv4-source":"10.0.0.3/32","ipv4-destination": "10.0.0.1/32"},'\
'"instructions": {"instruction": [{"order": "0",'\
'"apply-actions": {"action": [{"output-action": {'\
'"output-node-connector": "3"},"order": "0"}]}}]},'\
'"priority": "110","cookie": "1","table_id": "0"}]}'
#s4的1口流量满载时h4发的数据包,2,3端口出的流表项优先级高
h4_to_s4_2 ='{"flow": [{"id": "62","match": {"ethernet-match":'\
'{"ethernet-type": {"type": "2048"}},'\
'"ipv4-source":"10.0.0.4/32","ipv4-destination": "10.0.0.1/32"},'\
'"instructions": {"instruction": [{"order": "0",'\
'"apply-actions": {"action": [{"output-action": {'\
'"output-node-connector": "2"},"order": "0"}]}}]},'\
'"priority": "112","cookie": "1","table_id": "0"}]}'
h4_to_s4_3 ='{"flow": [{"id": "63","match": {"ethernet-match":'\
'{"ethernet-type": {"type": "2048"}},'\
'"ipv4-source":"10.0.0.4/32","ipv4-destination": "10.0.0.1/32"},'\
'"instructions": {"instruction": [{"order": "0",'\
'"apply-actions": {"action": [{"output-action": {'\
'"output-node-connector": "3"},"order": "0"}]}}]},'\
'"priority": "110","cookie": "1","table_id": "0"}]}'
#s2流表
s2_2to1='{"flow": [{"id": "1","match": {"ethernet-match":'\
'{"ethernet-type": {"type": "2048"}},'\
'"ipv4-source":"10.0.0.2/32","ipv4-destination": "10.0.0.1/32"},'\
'"instructions": {"instruction": [{"order": "0",'\
'"apply-actions": {"action": [{"output-action": {'\
'"output-node-connector": "1"},"order": "0"}]}}]},'\
'"priority": "105","cookie": "1","table_id": "0"}]}'
#s3流表
s3_2to1='{"flow": [{"id": "1","match": {"ethernet-match":'\
'{"ethernet-type": {"type": "2048"}},'\
'"ipv4-source":"10.0.0.2/32","ipv4-destination": "10.0.0.1/32"},'\
'"instructions": {"instruction": [{"order": "0",'\
'"apply-actions": {"action": [{"output-action": {'\
'"output-node-connector": "1"},"order": "0"}]}}]},'\
'"priority": "105","cookie": "1","table_id": "0"}]}'
#s1流表
s1_234to1='{"flow": [{"id": "1","match": {"ethernet-match":'\
'{"ethernet-type": {"type": "2048"}},'\
'"ipv4-source":"10.0.0.2/32","ipv4-destination": "10.0.0.1/32"},'\
'"instructions": {"instruction": [{"order": "0",'\
'"apply-actions": {"action": [{"output-action": {'\
'"output-node-connector": "1"},"order": "0"}]}}]},'\
'"priority": "105","cookie": "1","table_id": "0"}]}'
headers = {'Content-type': 'application/json'}
#下发流表,中间的地址可以从ODL控制器页面获得
#先下s2、s3和s1的流表
response, content = http.request(uri='http://127.0.0.1:8181/restconf/config/opendaylight-inventory:nodes/node/openflow:2/flow-node-inventory:table/0/flow/1', body=s2_2to1, method='PUT',headers=headers)
response, content = http.request(uri='http://127.0.0.1:8181/restconf/config/opendaylight-inventory:nodes/node/openflow:3/flow-node-inventory:table/0/flow/1', body=s3_2to1, method='PUT',headers=headers)
response, content = http.request(uri='http://127.0.0.1:8181/restconf/config/opendaylight-inventory:nodes/node/openflow:1/flow-node-inventory:table/0/flow/1', body=s1_234to1, method='PUT',headers=headers)
#默认下发h2数据包从s4的1端口出的流表
response, content = http.request(uri='http://127.0.0.1:8181/restconf/config/opendaylight-inventory:nodes/node/openflow:4/flow-node-inventory:table/0/flow/41', body=h2_to_s4_1, method='PUT',headers=headers)
#默认下发h3数据包从s4的1端口出的流表
response, content = http.request(uri='http://127.0.0.1:8181/restconf/config/opendaylight-inventory:nodes/node/openflow:4/flow-node-inventory:table/0/flow/51', body=h3_to_s4_1, method='PUT',headers=headers)
#默认下发h4数据包从s4的1端口出的流表
response, content = http.request(uri='http://127.0.0.1:8181/restconf/config/opendaylight-inventory:nodes/node/openflow:4/flow-node-inventory:table/0/flow/61', body=h4_to_s4_1, method='PUT',headers=headers)
while(True):
#获取s4端口1的流量
uri = 'http://127.0.0.1:8181/restconf/operational/opendaylight-inventory:nodes/node/openflow:4/node-connector/openflow:4:1'
response, content = http.request(uri=uri, method='GET')
content = json.loads(content)
statistics = content['node-connector'][0]['opendaylight-port-statistics:flow-capable-node-connector-statistics']
bytes1 = statistics['bytes']['transmitted']
#1秒后再次获取
time.sleep(1)
response, content = http.request(uri=uri, method='GET')
content = json.loads(content)
statistics = content['node-connector'][0]['opendaylight-port-statistics:flow-capable-node-connector-statistics']
bytes2 = statistics['bytes']['transmitted']
#在检测到s4的1口流量空闲时发的流表
speed=float(bytes2-bytes1)/1
if speed !=0 :#获取有效的速度
if speed < 1000 :
print 'speed =',speed,', s4端口1空闲,数据包往1口通过'
#下发默认的流表
response, content = http.request(uri='http://127.0.0.1:8181/restconf/config/opendaylight-inventory:nodes/node/openflow:4/flow-node-inventory:table/0/flow/41', body=h2_to_s4_1, method='PUT',headers=headers)
response, content = http.request(uri='http://127.0.0.1:8181/restconf/config/opendaylight-inventory:nodes/node/openflow:4/flow-node-inventory:table/0/flow/51', body=h3_to_s4_1, method='PUT',headers=headers)
response, content = http.request(uri='http://127.0.0.1:8181/restconf/config/opendaylight-inventory:nodes/node/openflow:4/flow-node-inventory:table/0/flow/61', body=h4_to_s4_1, method='PUT',headers=headers)
#在检测到s2的1口流量满载时下发新的流表
else :
print 'speed =',speed,', s4端口1满载,数据包改为往2口和3口通过'
#h2数据包的流表
response, content = http.request(uri='http://127.0.0.1:8181/restconf/config/opendaylight-inventory:nodes/node/openflow:4/flow-node-inventory:table/0/flow/41', body=lh2_to_s4_1, method='PUT',headers=headers)
response, content = http.request(uri='http://127.0.0.1:8181/restconf/config/opendaylight-inventory:nodes/node/openflow:4/flow-node-inventory:table/0/flow/42', body=h2_to_s4_2, method='PUT',headers=headers)
response, content = http.request(uri='http://127.0.0.1:8181/restconf/config/opendaylight-inventory:nodes/node/openflow:4/flow-node-inventory:table/0/flow/43', body=h2_to_s4_3, method='PUT',headers=headers)
#h3数据包的流表
response, content = http.request(uri='http://127.0.0.1:8181/restconf/config/opendaylight-inventory:nodes/node/openflow:4/flow-node-inventory:table/0/flow/52', body=h3_to_s4_2, method='PUT',headers=headers)
response, content = http.request(uri='http://127.0.0.1:8181/restconf/config/opendaylight-inventory:nodes/node/openflow:4/flow-node-inventory:table/0/flow/53', body=h3_to_s4_3, method='PUT',headers=headers)
#h4数据包的流表
response, content = http.request(uri='http://127.0.0.1:8181/restconf/config/opendaylight-inventory:nodes/node/openflow:4/flow-node-inventory:table/0/flow/62', body=h4_to_s4_2, method='PUT',headers=headers)
response, content = http.request(uri='http://127.0.0.1:8181/restconf/config/opendaylight-inventory:nodes/node/openflow:4/flow-node-inventory:table/0/flow/63', body=h4_to_s4_3, method='PUT',headers=headers)
odl = OdlUtil('127.0.0.1', '8181')
odl.install_flow()