首先,写这篇博客主要是记录一下博主在前前后后两三个月的时间里,编译安装OVS所采过的坑。水平有限,牛人勿喷。
###OpenvSwitch代码分析:
首先看一下OVS在一个SDN网络中的位置和与各部分组件的关系图:
上图绿框部分是博主想自己写的模块,也是想改动的地方。
继续看一张OVS自身的架构图:
每个模块都有不同的功能,主要就是 内核模块 + ovs-vswitchd + ovsdb-server:
ovs-vswitchd 为主要模块,实现交换机的守护进程daemon
openvswitch.ko为Linux内核模块,支持数据流在内核的交换
ovsdb-server为轻量级数据库服务器,保存配置信息,ovs-vswitchd通过这个数据库获取配置信息
博主就是在安装内核模块的时候踩过了一些坑,一会再说。= =!
再看一下openvswitch的代码结构:
在如上的代码结构中,vswitchd中就是ovs-vswitchd的入口代码,ovsdb就是ovsdb-server的代码,ofproto即上述的中间抽象层,lib下面有netdev,dpif的实现,datapath里面就是内核模块openvswitch.ko的代码。
在OVS中有很多复杂而精妙的数据结构和程序构架,博主也是看了很多大牛的分析分享在这推荐几个博客:
庾志辉OVS 专栏
http://blog.csdn.net/column/details/openvswitch.html
datapath 模块分析
http://vinllen.com/ovs-datapathbi-ji/
NightCat的分析整理
http://www.jianshu.com/p/bf112793d658
###OpenvSwitch安装
安装Open vSwitch,主要是两大块:内核层的openvswitch-datapath模块和用户层的openvswitch-common模块和openvswitch-switch模块。
因为博主的特殊需求,所以要对openvswitch的代码进行自己编译安装。所以如果只是想安装openvswitch的话,大可不必自己编译安装,直接用apt-get或者yum等包管理工具直接自动化安装即可。
因为博主主要是想实现对OpenFlow消息的加密和认证,所以修改openvswitch 2.5 源代码的 ovs/ofproto/ofproto.c 中的handle_flow_mod函数。该函数用来处理FLOW_MOD消息,方便后续的验证和调试。
博主在这想让OVS接收到FLOW_MOD消息后在桌面上创建一个文件并连接本机的一个socket端口发送一个消息。改动如下:
static enum ofperr
handle_flow_mod(struct ofconn *ofconn, const struct ofp_header *oh)
OVS_EXCLUDED(ofproto_mutex)
{
struct ofproto *ofproto = ofconn_get_ofproto(ofconn);
struct ofproto_flow_mod ofm;
uint64_t ofpacts_stub[1024 / 8];
struct ofpbuf ofpacts;
enum ofperr error;
FILE *FSpointer;
#define MYPORT 11777
#define BUFFER_SIZE 1024
error = reject_slave_controller(ofconn);
if (error) {
goto exit;
}
ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
error = ofputil_decode_flow_mod(&ofm.fm, oh, ofconn_get_protocol(ofconn),
&ofpacts,
u16_to_ofp(ofproto->max_ports),
ofproto->n_tables);
/*
@PeterWang 2017-06-24
@Create a flie to FS&Connect to socket
*/
//*******************************************************************************//
FSpointer = fopen("/home/peter/Desktop/test.txt","a");
fprintf(FSpointer, "%s\n","received a FLOW_MOD Info:");
fprintf(FSpointer, "%x / %x :%d\n\n",ofm.fm.cookie,ofm.fm.cookie_mask,ofm.fm.command);
int sock_cli = socket(AF_INET,SOCK_STREAM, 0);
///定义sockaddr_in
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(MYPORT); ///服务器端口
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); ///服务器ip
///连接服务器,成功返回0,错误返回-1
if (connect(sock_cli, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
{
perror("connect error!");
fprintf(FSpointer, "%s\n","connect error!");
}
char sendbuf[BUFFER_SIZE]="received a FLOW_MOD Info.";
///发送
if(send(sock_cli, sendbuf, strlen(sendbuf),0)<0){
perror("send error!");
fprintf(FSpointer, "%s\n","send error!");
}
memset(sendbuf, 0, sizeof(sendbuf));
//关闭文件和连接
close(sock_cli);
fclose(FSpointer);
//*******************************************************************************//
if (!error) {
error = ofproto_check_ofpacts(ofproto, ofm.fm.ofpacts,
ofm.fm.ofpacts_len);
}
if (!error) {
struct flow_mod_requester req;
req.ofconn = ofconn;
req.request = oh;
error = handle_flow_mod__(ofproto, &ofm, &req);
}
if (error) {
goto exit_free_ofpacts;
}
ofconn_report_flow_mod(ofconn, ofm.fm.command);
exit_free_ofpacts:
ofpbuf_uninit(&ofpacts);
exit:
return error;
}
接下来就是编译安装了,关于编译安装,博主使用两种方法,一种是直接make编译安装,另一种是打包成各个独立的deb安装包。这其中因为OVS的内核模块与Linux内核有的版本不兼容所以OVS官方给出了以下的版本对应关系:
Open vSwitch | Linux kernel |
---|---|
1.4.x | 2.6.18 to 3.2 |
1.5.x | 2.6.18 to 3.2 |
1.6.x | 2.6.18 to 3.2 |
1.7.x | 2.6.18 to 3.3 |
1.8.x | 2.6.18 to 3.4 |
1.9.x | 2.6.18 to 3.8 |
1.10.x | 2.6.18 to 3.8 |
1.11.x | 2.6.18 to 3.8 |
2.0.x | 2.6.32 to 3.10 |
2.1.x | 2.6.32 to 3.11 |
2.3.x | 2.6.32 to 3.14 |
2.4.x | 2.6.32 to 4.0 |
2.5.x | 2.6.32 to 4.3 |
2.6.x | 3.10 to 4.7 |
2.7.x | 3.10 to 4.9 |
博主修改的是2.5.2版本的OVS源码,所以按上述表格来看linux内核应该是2.6.32到4.3是都可以兼容的。所以博主在https://www.kernel.org 下载并安装了3.16.44的内核并安装了,但是在后续的OVS安装过程中却并没有那么顺利。主要遇到问题是安装内核模块的时候总是报依赖错误,但是所报的依赖关系博主都一一用modprobe载入到内核了 = =! | |
后来换到了3.4.113的内核版本才正常安装,关于在3.16.44的内核报错,在后来看的一些博文中有的牛人曾提到,需要提前将内核中bridge模块卸载掉用openvswitch来替换。但是博主也试验过就算是卸载了bridge模块openvswitch模块还是报依赖错误。 |
(关于安装内核的时候遇到的问题请见博主的另一篇博客)
####直接make编译安装
下载源码:
wget https://github.com/openvswitch/ovs/archive/branch-2.5.zip
解压:
unzip branch-2.5.zip
cd ovs-branch-2.5/
配置编译环境(root用户):
./configure --with-linux=/lib/modules/`uname -r`/build 2>/dev/null
./boot.sh
需要的编译的工具(root用户):
apt-get update
apt-get install graphviz autoconf automake automake1.10 dh-autoreconf libssl-dev libtool python-all python-twisted-conch
关于解决其他依赖关系:
用 dpkg-checkbuilddeps 可以查看一下当前主机还有什么依赖关系没有安装,用apt-get或yum等包管理工具安装上即可
编译安装(root用户):
./boot.sh
make -j4 #这里-j后边的数字代表用多线程来进行编译,可以按自己机器配置选择
make install
modprobe gre
insmod datapath/linux/openvswitch.ko
make modules_install
modprobe openvswitch
####打包成deb安装
博主第一次就是通过这种办法安装成功的,这也是OVS2.5官方给出的安装方法:
下载源码:
wget https://github.com/openvswitch/ovs/archive/branch-2.5.zip
解压:
unzip branch-2.5.zip
cd ovs-branch-2.5/
首先,先安装两个工具:
apt-get install build-essential fakeroot
用 dpkg-checkbuilddeps 命令来检查依赖关系有没有安装好,将其提示的依赖关系用apt-get或yum包管理工具安装好。
安装好依赖关系后,执行以下命令来进行打包:
fakeroot debian/rules binary
编译打包过程有10分钟左右,时间因为机器配置可能有所差异。产生的 .deb安装包会在源码目录的上一级文件目录中看到,如下图:
打包完了,下面就开始安装了。首先安装内核模块,博主在此费了不少时间,因为之前没有用过module-assistant安装过内核模块所以走了很多弯路。在上述的图片中可以看到跟内核有关的.deb文件(跟datapath有关的)有两个:
openvswitch-datapath-source_2.5.3-1_all.deb
openvswitch-datapath-dkms_2.5.3-1_all.deb
再次博主一直以为 -dkms是所谓的内核模块,安装好以后也加载不上 = =
其实,正确的安装方法如下:
首先安装openvswitch-datapath-source:
dpkg -i openvswitch-datapath-source_2.5.3-1_all.deb
module-assistant build openvswitch-datapath
module-assistant install openvswitch-datapath
现在lsmod就可以看到当前跟openvswitch相关的内核模块:
继续安装其他组件:
dpkg -i openvswitch-common_2.5.3-1_amd64.deb
dpkg -i openvswitch-switch_2.5.3-1_amd64.deb
安装上这两个模块,OVS的大部分功能已经可以用了,其他几个模块组件用的很少。
到此安装就结束了,为了验证一下自己的改动是否被执行,所以将ovs创建一个br0 并将其连接到内网的一个ODL控制器,配置如下:
然后我准备用postman向ODL发送下发流表请求:
其中请求连接为:
http://192.168.1.177:8181/restconf/config/opendaylight-inventory:nodes/node/openflow:59781766371651/flow-node-inventory:table/0
发送内容为(json格式):
{
"table": [
{
"id": "0",
"flow": [
{
"id": "0",
"match": {
"in-port": "1",
"vlan-match": {
"vlan-id": {
"vlan-id-present": "true",
"vlan-id": "20"
}
}
},
"instructions": {
"instruction": [
{
"apply-actions": {
"action": [
{
"output-action": {
"output-node-connector": "3",
"max-length": "65535"
},
"order": "1"
},
{
"pop-vlan-action": {},
"order": "0"
}
]
},
"order": "0"
}
]
},
"buffer_id": "65535",
"installHw": "true",
"barrier": "true",
"strict": "true",
"priority": "162",
"idle-timeout": "0",
"hard-timeout": "0",
"table_id": "0"
}
]
}
]
}
然后在OVS的主机上运行一个python脚本来监听,代码如下:
#!/usr/bin/python
#coding=utf-8
import socket
import commands
import threading
from time import ctime
import time
import fcntl
import struct
global host_IP,port
host_IP = "127.0.0.1"
port = 11111
def serverCallMulti(host_ip = "127.0.0.1",port = 11777):
count = 0
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 定义socket类型,网络通信,TCP
s.bind((host_ip, port)) # 套接字绑定的IP与端口
s.listen(1) # 开始TCP监听
print "开始监听..."
while True :
conn, addr = s.accept() # 接受TCP连接,并返回新的套接字与IP地址
print'Connected by', addr ,". Create a new thread :(",count,")" # 输出客户端的IP地址
t = threading.Thread(target=oneThread, args=(addr,count,conn,),name=addr[0]+'|'+str(count))
t.start()
count+=1
def oneThread(addr,count,conn):
print ctime(),'开启新线程', threading.current_thread().getName()
date = conn.recv(1024) # 把接收的数据实例化 !!考虑存在阻塞
print threading.current_thread().getName(),'send: ',date
print "校验数据库流表信息..."
time.sleep(0.2)
print"ok.."
if __name__ =='__main__':
host_ip = socket.gethostname()
# serverCallMulti(host_ip=host)
# host_ip = get_ip_address('eth0')
print '监听IP :',host_ip
serverCallMulti()
下面是执行情况:
可以看到,OVS接收到FLOW_MOD消息时就会执行博主所加的代码,向python脚本发送一句“received a FLOW_MOD Info.”
至此,博主喜极而泣啊…