openflow1.0.0 是斯坦福团队在2008年为满足AP在SDN系统中适配提出的openflow协议无线域扩展。此协议以openflow1.0为基础,解析处理 ieee80211 数据;利用 click 元素实现AP到控制器的转发功能。
这一篇文章从主函数出发,解读了代码的几个框架性函数。
主程序运行在 udatapath.c 中。
1 | int |
主要看一下 parse_options 函数,此函数定义了该子程序的解析选项。
parse_options
这里不是完整的函数代码,我们主要关注参数选项和对应的执行函数。
1 | static void |
主函数对程序输入参数进行排错判断
1 | if (argc - optind < 1) { |
建立新的datapath
1 | error = dp_new(&dp, dpid); |
查看 dp_new 函数
dp_new
1 | int |
此函数对 datapath 结构体的各成员做出初始化定义,其中比较重要的是 chain ,一起来看一下 chain_create 结构体。
1 | /* Creates and returns a new chain. Returns NULL if the chain cannot be |
1 | /* Attempts to append 'table' to the set of tables in 'chain'. Returns 0 or |
回到 main 函数,这一段代码主要实现虚拟通道的建立和连接,不做详解。
1 | n_listeners = 0; |
如果相同程序以存在则停止此程序,运行守护进程:
1 | die_if_already_running(); |
daemonize(void)
1 | void |
接下来,主函数建立socket通道。前面提到该AP由click模式实现与控制器或上层 openflow 交换机的交互,click相当于一个交换数据的交换机,用不同端口将数据交换隔离开。openflow1.0.0 程序建立 socket 通道,将数据包以 udp 协议传输到click模块中。通道由 make_socket 函数建立。
1 | /*add the monitor function*/ |
make_socket
thread_para 是一个包含端口port和datapath的结构体,具体定义为:
1 | struct thread_para { |
1 | //server socket using UDP |
后台进程建立成功后,主函数中设计了一个死循环,运行 dp_run, dp_wait, poll_back 三个函数。
1 | for (;;) { |
我们主要来看一下 dp_run 函数,其余的 dp_wait 和 poll_block 函数不做详解。
dp_run
此函数顾名思义就是运行一个datapath,其中主要重要的函数包括 netdev_recv 、fwd_port_input 和 remote_run ,将会在后面具体分析。
1 | void |
ofpbuf_new 提供一个空的内存空间。
1 | struct ofpbuf * |
netdev_recv
netdev_recv 将网络设备接受到的数据包存进缓存空间。
1 | int |
此函数利用 recvfrom 函数将网络设备收到的数据存储在 buffer 的 ofpbuf_tail 中。接受到数据包之后, fwd_port_input 函数首先对其处理。
fwd_port_input
1 | /* 'buffer' was received on 'p', which may be a a physical switch port or a |
数据包首先由 run_flow_through_tables 判断是否需要输出至控制器,再由 dp_output_control 函数操作。
run_flow_through_tables
1 | /* 'buffer' was received on 'p', which may be a a physical switch port or a |
首先,flow_extract 解析数据包,该函数主要判断数据包是否为IP数据包,是则返回1;否则返回0。如果是IP数据包,程序将删除这一缓存,不进行下一步处理。同时,函数将数据包中的各信息位信息存储到结构体 flow 中。flow 由 sw_flow 定义。
1 | struct sw_flow { |
数据流解析函数如下:
1 | /* Returns 1 if 'packet' is an IP fragment, 0 otherwise. */ |
如果不是一个IP数据,数据包的信息现已存储在结构体 flow 中。
接着,通过 chain_lookup 函数为此数据包匹配对应的 key 并存入 flow 中。
1 | /* Searches 'chain' for a flow matching 'key', which must not have any wildcard |
最后, execute_actions 函数执行该flow的 action 。
execute_actions
此函数需要的参数包括 datapath,buffer,key,acitons,actions_len。
由函数的调用可以看到,actions 和 actions_len 存储在flow的 sw_flow_actions 结构体中。
1 | /* Execute a list of actions against 'buffer'. */ |
此函数确定转发前后的端口,对 OpenFlow 协议的 build-in action 和 vendor aciton 分别执行 execute_ofpact 和 execute_vendor ,其他 actions 统一由 do_output 执行。
do_output
函数首先根据输出端口判断该数据包是转发给控制器的还是由端口输出的。
1 | static void |
do_output_port
根据不同的 out_port 对数据包进行处理,涉及的处理函数有 output_packet ,output_all 和 dp_output_control 。
1 | /** Takes ownership of 'buffer' and transmits it to 'out_port' on 'dp'. |
- output_packet 函数利用 netdev_send 函数将数据包由网络设备发出。
- output_all 函数将数据包从所有端口发出,在函数内调用 do_output_port ,每次发送后将端口号加1继续发送。
do_output_control
1 | /* Takes ownership of 'buffer' and transmits it to 'dp''s controller. If the |
函数利用 ofpbuf_push_uninit 函数重构 buffer 的数据包头部,然后用 send_openflow_buffer 函数将 buffer 发出。
1 | void * |
在解析 send_openflow_buffer 函数之前,先来看一下之前提到的 remote_run 函数。
remote_run
此函数主要通过构建虚拟连接来建立一个远程进程,我的个人理解是通过这个函数来建立AP与Controller之间的安全信道。
首先,结构体 remote 构建了一个安全信道。
1 | /* A connection to a secure channel. */ |
结构体 rconn 被定义为一个连接控制器或交换机的可靠连接。
1 | /* A reliable connection to an OpenFlow switch or controller. |
这是一个虚拟的连接,具体连接又由 vconn-provider 提供,包括状态、版本、IP以及接收和发送的结构体。
1 | /* Active virtual connection to an OpenFlow device. |
所以,总而言之可以把 remote 视为AP的安全信道。
在解析remote_run函数前,还要说明一个结构体:sender。sender由remote结构体和一个id组成,用以表示接收到的 OpenFlow 消息。
1 | /* The origin of a received OpenFlow message, to enable sending a reply. */ |
remote_run 函数主要职责是保持安全信道运行并捕获由controller发来的消息。
1 | static void |
- rconn_run 函数建立连接。
- rconn_recv 接受连接中的数据包,并将数据包存储在 buffer 中。
- fwd_control_input 解析接收到的数据包并进行下一步操作。
fwd_control_input
- 从数据包的包头中提取数据,根据不同数据类型进行操作。
- 操作函数暂存在 handler 中。
- 具体的操作函数将在后续文章中逐个介绍。
1 | /* 'msg', which is 'length' bytes long, was received from the control path. |
最后回到非常重要的函数 send_openflow_buffer ,此函数也在之前多次看到,现在我们来看一下它的具体代码。
send_openflow_buffer
- 函数首先判断是否为控制器发送来的数据,如果是,则sender不为零,执行第一个if操作,发送回去。
- 如果不是,解析目的端口,然后用 send_openflow_buffer_to_remote 把 buffer 发送到安全信道。
1 | int |
send_openflow_buffer_to_remote
1 | static int |
1 | /* Sends 'b' on 'rc'. Increments '*n_queued' while the packet is in flight; it |
这几段代码比较简单,不过多说明,总之就是通过 rconn_send 发送到安全信道中。
因为是主函数直接调用的函数,总体还是框架性的函数。其他功能性函数将在接下里的文章中介绍。