• Open vSwitch源码阅读笔记(上)


    https://www.sdnlab.com/18668.html

    作者简介:刘成天,就职于赛特斯信息科技股份有限公司,高级工程师,目前工作地点南京,主要从事vCPE和路由器相关的研发工作

    引言

    本文主要对OpenvSwitch(基于2.3.90版本)重点模块的源码实现流程做了简要的阅读记录,适合阅读OpenvSwitch源码的初级读者参考使用。在阅读源码时参照了网上很多博客尤其SDNLAB的网站文章,再次统一致谢,并将知识分享传递下去。

    1. OVS网络架构

    Openvswitch是一个虚拟交换机,支持Open Flow协议(也有一些硬件交换机支持Open Flow),他们被远端的controller通过Open Flow协议统一管理着,从而实现对接入的虚拟机(或设备)进行组网和互通,整体组网结构如下图:

    2. OVS内部架构

    • ovs-vswitchd 主要模块,实现vswitch的守候进程daemon;
    • ovsdb-server 轻量级数据库服务器,用于ovs的配置信息;
    • ovs-vsctl 通过和ovsdb-server通信,查询和更新vswitch的配置;
    • ovs-dpctl 用来配置vswitch内核模块的一个工具;
    • ovs-appctl 发送命令消息到ovs进程;
    • ovs-ofctl 查询和控制OpenFlow虚拟交换机的流表;
    • datapath 内核模块,根据流表匹配结果做相应处理;

    3. OVS代码架构


    vswitchd是ovs主要的用户态程序,它从ovsdb-server读取配置并发送到ofproto层,也从ofproto读取特定的状态和统计信息并发送到数据库;

    ofproto是openflow的接口层,负责和Openflow controller通信并通过ofproto_class与ofproto provider底层交互;

    ofproto-dpif是ofproto接口类的具体实现;

    netdev是ovs系统的网络设备抽象(比如linux的net_device或交换机的port),netdev_class定义了netdev-provider的具体实现需要的接口,具体的平台实现需要支持这些统一的接口,从而完成netdev设备的创建、销毁、打开、关闭等一系列操作;

    3.1 datapath

    由于openvswitch用户态代码相对复杂,首先从内核模块入手分析。
    datapath为 ovs内核模块,负责执行数据处理,也就是把从接收端口收到的数据包在流表中进行匹配,并执行匹配到的动作。一个datapath可以对应多个vport,一个vport类似物理交换机的端口概念。一个datapth关联一个flow table,一个flow table包含多个条目,每个条目包括两个内容:一个match/key和一个action。

    3.1.1 数据流向


    一般的数据包在 Linux网络协议中的流向为上图中的蓝色箭头流向:网卡eth0收到数据包后判断报文走向,如果是本地报文把数据传送到用户态,如果是转发报文根据选路(二层交换或三层路由)把报文送到另一个网卡如eth1。当有 OVS时,数据流向如红色所示:从网卡 eth0收到报文后进入ovs的端口,根据 key值进行流表匹配,如果匹配成功执行流表对应的 action;如果失败通过upcall送入用户态处理。

    3.1.2 模块初始化

    内核模块采用module_init(dp_init)进行datapath的初始化,代码如下:

    其中dp的genl_family注册了如下四个类型:

    3.1.3 收包处理

    通过vport注册的回调函数netdev_frame_hook()->netdev_frame_hook()-> netdev_port_receive()->ovs_vport_receive()处理接收报文,ovs_flow_key_extract()函数生成flow的key内容用以接下来进行流表匹配,最后调用ovs_dp_process_packet()函数进入真正的ovs数据包处理,代码流程如下:

    3.1.4 流表哈希桶

    流表采用hash的方式排列存放,流表的hash头结点存储数据结构如下:

    该hash桶的初始化函数alloc_buckets (),生成的数据格式可参考如下:

    3.1.5流表创建

    用户态通过netlink进行datapath流表更新的入口函数都定义在dp_flow_genl_ops中,流表创建的入口函数是ovs_flow_cmd_new函数,代码分析如下:


    根据上述流程给出流表的主要数据结构如下:

    3.1.6 流表查询

    流表查找主要是查表关键字的匹配,关键字数据结构如下,根据skb中的Ethernet帧生成key的函数为ovs_flow_key_extract():

    流表查询的入口函数ovs_flow_tbl_lookup_stats(),flow的匹配策略是和流表中所有mask和所有key进行匹配处理,为了加速查询效率,在调用真正的流表查询函数flow_lookup()之前,对于mask的查询采用了缓存机制,实现原理是首先查询缓存的mask_cache_entry,这些cache是查询成功后形成的cache,并针对cache采用分段查询的方式,代码如下:

    flow_lookup()函数的处理流程如下:

    masked_flow_lookup()函数处理如下:

    3.1.7 action处理

    ovs的action类型如下,使用nla_type()函数获取nl_type的值,入口处理函数为do_execute_actions()。


    OVS_ACTION_ATTR_OUTPUT:获取port号,调用do_output()发送报文到该port;
    OVS_ACTION_ATTR_USERSPACE:调用output_userspace()发送到用户态;
    OVS_ACTION_ATTR_HASH:调用execute_hash()获取skb的hash赋值到ovs_flow_hash
    OVS_ACTION_ATTR_PUSH_VLAN:调用push_vlan()增加vlan头部

    OVS_ACTION_ATTR_POP_VLAN:调用pop_vlan()移除vlan头

    • OVS_ACTION_ATTR_RECIRC:在action_fifos全局数组中添加一个deferred_action;
    • OVS_ACTION_ATTR_SET:调用execute_set_action()设置相关参数;
    • OVS_ACTION_ATTR_SAMPLE:概率性的发送报文到用户态(详见sflow章节)。

    3.1.8 upcall处理

    当没有找到匹配的流表时,内核通过netlink发送报文到用户层处理,入口函数ovs_dp_upcall(),该函数调用queue_userspace_packet()构造发往用户层的skb,通过netlink通信机制发送到用户层,其中形成的主要数据格式如下:

    3.2 ovs-vswitchd

    vswitchd作为守护进程和ovsdb通信以及和controller进行openflow通信,并完成和底层内核的交互。代码在vswitchd/目录下面,可以从main函数入口分析,整体处理流程如下:

    3.3 ofproto

    ofproto层通过ofproto_class类(实现是ofproto_dpif_class)实现了openflow的接口,它主要包括如下几个接口类对象:

    • ofproto 代表了一个openflow switch的具体实现,是ofproto层的整体结构体;
    • ofport代表了一个openflow switch的端口,关联一个netdev设备;
    • ofrule代表了一条openflow规则,rule里面包含一组actions;
    • ofgroup代表了一个openflow的行为组合,openflow 1.1+以上版本支持;

    3.3.1 ofproto数据结构

    3.3.2 ofproto创建流程

    其中rule和group的创建流程不在本节列出

    3.3.3 udpif

    udpif接口层采用多个线程处理内核发往用户层的upcall请求,入口函数为udpif_set_threads(),主要处理流程如下:

    3.4 openflow

    OpenFlow 是用于管理交换机流表的协议,ovs-ofctl 则是 OVS 提供的命令行工具。在没有配置 OpenFlow controller的模式下,用户可以使用 ovs-ofctl 命令通过 OpenFlow 协议去连接 OVS,创建、修改或删除 OVS中的流表项,并对 OVS 的运行状况进行动态监控。

    3.4.1 openflow连接建立

    在bridge_reconfigure()函数中调用bridge_configure_remotes进行openflow连接的相关处理,主要创建两个对象:ofconn作为客户端负责和远端conntroller主动建立连接;ofservice作为服务器提供被动式的监听服务,主要数据结构及流程如下图:

    3.4.2 ofconn报文处理

    入口函数ofproto_run(),主要调用流程如下:

    3.4.3 ofservice报文处理

    3.4.4 flow_mod消息格式

    openflow协议消息处理入口函数是handle_openflow(),其中最重要的是flow_mod流表项的处理,flow_mod流表的报文格式主要有四部分组成:openflow头部、flow_mod固定字段、match字段和instruction字段。
    match分为OFPMT_STANDARD和OFPMT_OXM两种类型,可以包含多个oxm,instruction可以包含多个action,也可以没有。抓包示例可参考如下:

    3.4.5 match字段处理

    match字段的解析处理入口函数为ofputil_pull_ofp11_match(),其中的核心处理函数为nx_pull_raw(),主要流程是解析出flow_mod的match字段,和flow中的match相关参数做一些合法性检测,最后使用解析出的value更新flow中的match。
    目前match匹配域用的较多的是OXM即TLV格式,字段解析结构示意图如下:

    核心流程处理如下:

    3.4.6 instruction字段处理

    instruction字段的解析处理入口函数ofpacts_pull_openflow_instructions(),主要流程是解析出flow_mod的instruction字段,根据不同的instruction做不同的处理,其中函数decode_openflow11_instructions()解析出所有instruction并按照不同类型放入ofp11_instruction *insts[N_OVS_INSTRUCTIONS]数组中,N_OVS_INSTRUCTIONS根据OVS_INSTRUCTIONS定义推导出值为6(即instruction支持的所有类型)。
    其中最重要的宏OVS_INSTRUCTIONS完成了主要的数据生成和转换,根据它的定义可推导出instruction的类型和后续主要的处理函数对应关系:

    ofpacts_decode()函数完成actions的解析,字段解析结构示意图如下:
    最后调用ofpacts_check_consistency()进行参数的有效性检查。

    3.4.7 flow_mod处理流程

    以增加流表OFPTYPE_FLOW_MOD为例整理函数处理流程如下:

  • 相关阅读:
    Poj3126
    Poj1426
    2806 红与黑
    3100 蜗牛
    1225 八数码难题
    2549 自然数和分解
    2547 东方辉针城
    2928 你缺什么
    1629 01迷宫
    1029 遍历问题
  • 原文地址:https://www.cnblogs.com/liuhongru/p/11231907.html
Copyright © 2020-2023  润新知