SDN功能测试(四)--- 实现自定义action(2)修改OVS源码<队列去重(内核态实现)>
一:拓扑搭建
(一)代码实现
from mininet.topo import Topo from mininet.net import Mininet from mininet.node import RemoteController from mininet.link import TCLink from mininet.util import dumpNodeConnections class MyTopo(Topo): def __init__(self): super(MyTopo,self).__init__() #add host Host1 = self.addHost('h1',ip='10.0.0.1') Host2 = self.addHost('h2',ip='10.0.0.2') Host3 = self.addHost('h3',ip='10.0.0.3') Host4 = self.addHost('h4',ip='10.0.0.4') Host5 = self.addHost('h5',ip='10.0.0.5') Host6 = self.addHost('h6',ip='10.0.0.11') Host7 = self.addHost('h7',ip='10.0.0.12') Host8 = self.addHost('h8',ip='10.0.0.13') Host9 = self.addHost('h9',ip='10.0.0.14') Host10 = self.addHost('h10',ip='10.0.0.15') switch1 = self.addSwitch('e1') switch2 = self.addSwitch('s1') switch3 = self.addSwitch('s2') switch4 = self.addSwitch('e2') self.addLink(Host1,switch1, bw=5, delay='5ms', loss=0, max_queue_size=1000, use_htb=True) self.addLink(Host2,switch1, bw=5, delay='5ms', loss=0, max_queue_size=1000, use_htb=True) self.addLink(Host3,switch1, bw=5, delay='5ms', loss=0, max_queue_size=1000, use_htb=True) self.addLink(Host4,switch1, bw=5, delay='5ms', loss=0, max_queue_size=1000, use_htb=True) self.addLink(Host5,switch1, bw=5, delay='5ms', loss=0, max_queue_size=1000, use_htb=True) self.addLink(switch1,switch2, bw=5, delay='5ms', loss=0, max_queue_size=1000, use_htb=True) self.addLink(switch2,switch4, bw=5, delay='5ms', loss=0, max_queue_size=1000, use_htb=True) self.addLink(switch1,switch3, bw=5, delay='5ms', loss=0, max_queue_size=1000, use_htb=True) self.addLink(switch3,switch4, bw=5, delay='5ms', loss=0, max_queue_size=1000, use_htb=True) self.addLink(switch4,Host6, bw=50, delay='5ms', loss=0, max_queue_size=1000, use_htb=True) self.addLink(switch4,Host7, bw=50, delay='5ms', loss=0, max_queue_size=1000, use_htb=True) self.addLink(switch4,Host8, bw=50, delay='5ms', loss=0, max_queue_size=1000, use_htb=True) self.addLink(switch4,Host9, bw=50, delay='5ms', loss=0, max_queue_size=1000, use_htb=True) self.addLink(switch4,Host10, bw=50, delay='5ms', loss=0, max_queue_size=1000, use_htb=True) topos = {"mytopo":(lambda : MyTopo())}
二:流表项实现
dpctl del-flows sh ovs-ofctl add-flow s1 in_port=1,actions=output:2 sh ovs-ofctl add-flow s1 in_port=2,actions=output:1 sh ovs-ofctl add-flow s2 in_port=1,actions=output:2 sh ovs-ofctl add-flow s2 in_port=2,actions=output:1 sh ovs-ofctl add-flow e1 in_port=1,actions=output:6,7 sh ovs-ofctl add-flow e1 in_port=2,actions=output:6,7 sh ovs-ofctl add-flow e1 in_port=3,actions=output:6,7 sh ovs-ofctl add-flow e1 in_port=4,actions=output:6,7 sh ovs-ofctl add-flow e1 in_port=5,actions=output:6,7 sh ovs-ofctl add-flow e2 in_port=3,actions=output:1,2 sh ovs-ofctl add-flow e2 in_port=4,actions=output:1,2 sh ovs-ofctl add-flow e2 in_port=5,actions=output:1,2 sh ovs-ofctl add-flow e2 in_port=6,actions=output:1,2 sh ovs-ofctl add-flow e2 in_port=7,actions=output:1,2 sh ovs-ofctl add-flow e1 dl_type=0x0806,actions=output:1,2,3,4,5 sh ovs-ofctl add-flow e2 dl_type=0x0806,actions=output:3,4,5,6,7 sh ovs-ofctl add-flow e1 ip,nw_src=10.0.0.11,actions=output:1 sh ovs-ofctl add-flow e1 ip,nw_src=10.0.0.12,actions=output:2 sh ovs-ofctl add-flow e1 ip,nw_src=10.0.0.13,actions=output:3 sh ovs-ofctl add-flow e1 ip,nw_src=10.0.0.14,actions=output:4 sh ovs-ofctl add-flow e1 ip,nw_src=10.0.0.15,actions=output:5
sh ovs-ofctl add-flow e2 "priority=100,dl_type=0x0800,nw_dst=10.0.0.11,actions=rmdupqueue(queue_id=50,max_len=100),3" //去重复 sh ovs-ofctl add-flow e2 "priority=100,dl_type=0x0806,nw_dst=10.0.0.11,actions=output:3" //arp通过
sh ovs-ofctl add-flow e2 ip,nw_src=10.0.0.2,actions=output:4 sh ovs-ofctl add-flow e2 ip,nw_src=10.0.0.3,actions=output:5 sh ovs-ofctl add-flow e2 ip,nw_src=10.0.0.4,actions=output:6 sh ovs-ofctl add-flow e2 ip,nw_src=10.0.0.5,actions=output:7
补充:上面0x0800针对ip报文,我们如果只考虑UDP,则使用如下:
sh ovs-ofctl add-flow e2 "udp,nw_dst=10.0.0.11,actions=rmdupqueue(queue_id=50,max_len=100),3" //去重复
三:UDP通信实现
(一)服务端
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <netdb.h> #include <arpa/inet.h> #include <netinet/in.h> #define MAX_LEN 1000 int str_to_number(const char* str); int main(int argc, char** argv) { char message[MAX_LEN]; int sk; struct sockaddr_in src_addr; //用于指定本地监听信息 struct sockaddr_in cli_addr; //獲取客戶端地址信息 int src_addr_len,cli_addr_len; int count,ret; struct in_addr addr; if (argc != 2) //获取监听端口 { printf("Error: you must enter port to monite\n"); exit(0); } bzero(&src_addr, sizeof(src_addr)); src_addr.sin_family = AF_INET; src_addr.sin_port = htons(str_to_number(argv[1])); printf("port:%d\n",str_to_number(argv[1])); src_addr_len = sizeof(src_addr); cli_addr_len = sizeof(cli_addr); sk = socket(AF_INET, SOCK_DGRAM, 0); if(sk<0) { printf("socket create failure\n"); return -1; } ret = bind(sk, (struct sockaddr*)&src_addr, src_addr_len); if(ret < 0) { printf("socket bind failure\n"); return -1; } while (1) { printf("Waiting for data from sender \n"); count = recvfrom(sk, message, MAX_LEN, 0, (struct sockaddr*)&cli_addr, &cli_addr_len); if(count==-1) { printf("receive data failure\n"); return -1; } addr.s_addr = cli_addr.sin_addr.s_addr; printf("Receive info: %s from %s %d\n", message,inet_ntoa(addr),cli_addr.sin_port); } close(sk); return 0; } int str_to_number(const char* str) { int i,len, num = 0; len= strlen(str); for (i = 0; i < len;i++) num = num * 10 + str[i] - '0'; return num; }
(二)客户端
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <netdb.h> #include <arpa/inet.h> #include <netinet/in.h> #define MAX_LEN 1000 //#define SER_IP "10.0.0.1" #define MAXSIZE 40 #define IPOPT_TAG 0x21 //IP选项标志字段 #define IPOPT_LEN 8 //IP选项长度字段 int str_to_number(const char* str); int main(int argc, char** argv) { int sk; char buf[MAX_LEN]; struct sockaddr_in ser_addr; //是用于指定对方(目的主机)信息 struct sockaddr_in loc_addr; //可以用来指定一些本地的信息,比如指定端口进行通信,而不是让系统随机分配 int ser_addr_len,loc_addr_len; int ret,count; struct in_addr addr; unsigned int SeqID=0; unsigned char opt[MAXSIZE]; //ip option选项 opt[0] = 0x21; opt[1] = IPOPT_LEN; if (argc != 3) { printf("Error: the number of args must be 3\n"); exit(0); } //配置服务器信息 bzero(&ser_addr, sizeof(ser_addr)); ser_addr.sin_family = AF_INET; //设置为IPV4通信 //ser_addr.sin_addr.s_addr = htonl(INADDR_ANY); ser_addr.sin_addr.s_addr = inet_addr(argv[1]); //设置目的ip ser_addr.sin_port = htons(str_to_number(argv[2])); //设置目的端口去链接服务器 ser_addr_len = sizeof(ser_addr); //配置本地信息 bzero(&loc_addr, sizeof(loc_addr)); loc_addr.sin_family = AF_INET; //设置为IPV4通信 //loc_addr.sin_addr.s_addr = htonl(INADDR_ANY); loc_addr.sin_addr.s_addr = htonl(INADDR_ANY); //设置目的ip loc_addr.sin_port = htons(8080); //设置本地端口去链接服务器 loc_addr_len = sizeof(loc_addr); sk = socket(AF_INET, SOCK_DGRAM, 0); //设置UDP报文传输 0表示默认 SOCK_DGRAM 默认使用UDP //其中第三位 0 是调用方式标志位,设置socket通方式,比如非阻塞 if(sk<0) { printf("socket create failure\n"); return -1; } //将本地配置使用bind绑定 ret = bind(sk,(struct sockaddr*)&loc_addr,loc_addr_len); if(ret < 0) { printf("socket bind failure\n"); return -1; } for (;;) { printf("Input info:>>>"); scanf("%s", buf); if (!strcmp(buf, "quit")) break; *(int *)(opt + 2) = htonl(++SeqID); setsockopt(sk,IPPROTO_IP,IP_OPTIONS,(void *)opt,IPOPT_LEN); sendto(sk, buf, strlen(buf)+1, 0, (struct sockaddr*)&ser_addr, ser_addr_len); } printf("communicate end\n"); close(sk); return 0; } int str_to_number(const char* str) { int i,len, num = 0; len= strlen(str); for (i = 0; i < len;i++) num = num * 10 + str[i] - '0'; return num; }
四:测试结果
可以看到h1与h6使用队列,实现去重,h2与h7没有使用队列,收到重复数据。