open vswitch研究:vswitchd (三)

vswitchd是用户态的daemon进程,其核心是执行ofproto的逻辑。我们知道ovs是遵从openflow交换机的规范实现的,就拿二层包转发为例,传统交换机(包括Linux bridge的实现)是通过查找cam表,找到dst mac对应的port;而open vswitch的实现则是根据入包skb,查找是否有对应的flow。如果有flow,说明这个skb不是流的第一个包了,那么可以在flow->action里找到转发的port。这里要说明的是,SDN的思想就是所有的包都需要对应一个flow,基于flow给出包的行为action,传统的action无非就是转发,接受,或者丢弃,而在SDN中,会有更多的action定义:修改skb的内容,改变包的路径,clone多份出来发到不同路径等等。


如果skb没有对应的flow,说明这是flow的第一个包,需要为这个包创建一个flow,vswitchd会在一个while循环里反复检查有没有ofproto的请求过来,有可能是ovs-ofctl传过来的,也可能是openvswitch.ko通过netlink发送的upcall请求,当然大部分情况下,都是flow miss导致的创建flow的请求,这时vswitchd会基于openflow规范创建flow, action,我们看下这个流程:


由于open vswitch是一个2层交换机模型,所有包开始都是从某个port接收进来,即调用ovs_dp_process_received_packet,该函数先基于skb通过ovs_flow_extract生成key,然后调用ovs_flow_tbl_lookup基于key查找flow,如果无法找到flow,调用ovs_dp_upcall通过netlink把一个dp_upcall_info结构发到vswitchd里去处理(调用genlmsg_unicast)


vswitchd会在handle_upcalls里来处理上述的netlink request,对于flow table里miss的情况,会调用handle_miss_upcalls,继而又调用handle_flow_miss,下面来看handle_miss_upcalls的实现

static void
handle_miss_upcalls(struct dpif_backer *backer, struct dpif_upcall *upcalls,
                    size_t n_upcalls)
{

    /* Construct the to-do list.
     *
     * This just amounts to extracting the flow from each packet and sticking
     * the packets that have the same flow in the same "flow_miss" structure so
     * that we can process them together. */
    hmap_init(&todo);
    n_misses = 0;

注释里写得很明白,下面的循环会遍历netlink传到用户态的struct dpif_upcall,该结构包含了miss packet,和基于报文生成的的flow key,对于flow key相同的packet,会集中处理

    for (upcall = upcalls; upcall < &upcalls[n_upcalls]; upcall++) {

        fitness = odp_flow_key_to_flow(upcall->key, upcall->key_len, &flow);
        port = odp_port_to_ofport(backer, flow.in_port); 

odp_flow_key_to_flow,先调用lib/parse_flow_nlattrs函数解析upcall->key, upcall->key_len,把解析出来的attr属性放到一个bitmap present_attrs中,而对应类型的struct nlattr则放到struct nlattr* attrs[]中。接下来对present_attrs的每一位,从upcall->key中取得相应值并存入flow中。对于vlan的parse,特别调用了parse_8021q_onward

odp_port_to_ofport,用来把flow.in_port,即datapath的port号转换成openflow port,即struct ofport_dpif* port

        flow_extract(upcall->packet, flow.skb_priority,                                               
                     &flow.tunnel, flow.in_port, &miss->flow);                                        

这里把packet解析到flow中,该函数和odp_flow_key_to_flow有些地方重复

       /* Add other packets to a to-do list. */                                                      
        hash = flow_hash(&miss->flow, 0);
        existing_miss = flow_miss_find(&todo, &miss->flow, hash);
        if (!existing_miss) {
            hmap_insert(&todo, &miss->hmap_node, hash);
            miss->ofproto = ofproto;
            miss->key = upcall->key;
            miss->key_len = upcall->key_len;                                                          
            miss->upcall_type = upcall->type;
            list_init(&miss->packets);
    
            n_misses++;                                                                               
        } else {
            miss = existing_miss;
        }   
        list_push_back(&miss->packets, &upcall->packet->list_node);
    }

flow_hash计算出miss->flow的哈希值,之后在todo这个hmap里基于哈希值查找struct flow_miss*,如果为空,表示这是第一个flow_miss,初始化这个flow_miss并加入到todo中,最后把packet假如到flow_miss->packets的list中。这里验证了之前的结论,对于一次性的多个upcall,会把属于同一个flow_miss的packets链接到同一个flow_miss下再一并处理。


OVS定义了facet,用来表示用户态程序,比如vswitchd,对于一条被匹配的flow的视图。同时kernel space对于一条flow同样有一个视图,facet表示两个视图相同的部分。不同的部分用subfacet来表示,struct subfacet里定义了action行为

如果datapath计算出的flow_key,和vswitchd基于packet计算出的flow_key完全一致的话,facet只会包含唯一的subfacet,如果datapath计算出的flow_key的成员比vswitchd基于packet计算出来的还要多,那么每个多出来的部分都会成为一个subfacet

struct subfacet {
    /* Owners. */
    struct hmap_node hmap_node; /* In struct ofproto_dpif 'subfacets' list. */
    struct list list_node;      /* In struct facet's 'facets' list. */
    struct facet *facet;        /* Owning facet. */

    /* Key.
     *
     * To save memory in the common case, 'key' is NULL if 'key_fitness' is
     * ODP_FIT_PERFECT, that is, odp_flow_key_from_flow() can accurately
     * regenerate the ODP flow key from ->facet->flow. */
    enum odp_key_fitness key_fitness;
    struct nlattr *key;
    int key_len;

    long long int used;         /* Time last used; time created if not used. */

    uint64_t dp_packet_count;   /* Last known packet count in the datapath. */
    uint64_t dp_byte_count;     /* Last known byte count in the datapath. */

    /* Datapath actions.
     *
     * These should be essentially identical for every subfacet in a facet, but
     * may differ in trivial ways due to VLAN splinters. */
    size_t actions_len;         /* Number of bytes in actions[]. */
    struct nlattr *actions;     /* Datapath actions. */

    enum slow_path_reason slow; /* 0 if fast path may be used. */
    enum subfacet_path path;    /* Installed in datapath? */

}

我们先来看handle_flow_miss

/* Handles flow miss 'miss' on 'ofproto'.  May add any required datapath
 * operations to 'ops', incrementing '*n_ops' for each new op. */
static void
handle_flow_miss(struct ofproto_dpif *ofproto, struct flow_miss *miss,
                 struct flow_miss_op *ops, size_t *n_ops)
{
    struct facet *facet;
    uint32_t hash;

    /* The caller must ensure that miss->hmap_node.hash contains
     * flow_hash(miss->flow, 0). */
    hash = miss->hmap_node.hash;

    facet = facet_lookup_valid(ofproto, &miss->flow, hash);

在表示datapath的数据结构struct ofproto_dpif* ofproto中查找flow。ofproto->facets是一个hashmap,首先计算出miss flow的hash值,之后在hash对应的hmap_node list中查找是否有匹配的flow,比较的方式比较暴力,直接拿memcmp比较。。


    if (!facet) {
        struct rule_dpif *rule = rule_dpif_lookup(ofproto, &miss->flow);

        if (!flow_miss_should_make_facet(ofproto, miss, hash)) {
            handle_flow_miss_without_facet(miss, rule, ops, n_ops);

此时认为没有必要创建flow facet,对于一些trivial的流量,创建一个flow facet反而会带来更大的overload


            return;
        }

        facet = facet_create(rule, &miss->flow, hash);

好吧,我们为这个flow创建一个facet
    }
    handle_flow_miss_with_facet(miss, facet, ops, n_ops);
}

struct flow_miss是对flow的一个封装,用来加快miss flow的batch处理。大多数情况下,都会创建这个facet出来,

2012-10-26T07:15:43Z|22522|ofproto_dpif|INFO|[qinq] miss flow, create facet: vlan_tci 0, proto 0x806, in_port 1, src mac 0:16:3e:83:0:1, dst mac 0:25:9e:5d:62:53

2012-10-26T07:15:43Z|22529|ofproto_dpif|INFO|[qinq] miss flow, create facet: vlan_tci 0, proto 0x806, in_port 2, src mac 0:25:9e:5d:62:53, dst mac 0:16:3e:83:0:1

可以看出一个双工通信创建了两个flow出来,同时也创建了facet


下面来看handle_flow_miss_with_facet,里面调用subfacet_make_actions来生成action,该函数首先调用action_xlate_ctx_init,初始化一个action_xlate_ctx结构,该结构定义如下:

struct action_xlate_ctx {
/* action_xlate_ctx_init() initializes these members. */


    /* The ofproto. */
    struct ofproto_dpif *ofproto;

    /* Flow to which the OpenFlow actions apply.  xlate_actions() will modify
     * this flow when actions change header fields. */
    struct flow flow;

    /* The packet corresponding to 'flow', or a null pointer if we are
     * revalidating without a packet to refer to. */
    const struct ofpbuf *packet;

    /* Should OFPP_NORMAL update the MAC learning table?  Should "learn"
     * actions update the flow table?
     *
     * We want to update these tables if we are actually processing a packet,
     * or if we are accounting for packets that the datapath has processed, but
     * not if we are just revalidating. */
    bool may_learn;

    /* The rule that we are currently translating, or NULL. */

    struct rule_dpif *rule;

    /* Union of the set of TCP flags seen so far in this flow.  (Used only by
     * NXAST_FIN_TIMEOUT.  Set to zero to avoid updating updating rules'
     * timeouts.) */
    uint8_t tcp_flags;

/* xlate_actions() initializes and uses these members.  The client might want
 * to look at them after it returns. */

    struct ofpbuf *odp_actions; /* Datapath actions. */
    tag_type tags;              /* Tags associated with actions. */
    enum slow_path_reason slow; /* 0 if fast path may be used. */
    bool has_learn;             /* Actions include NXAST_LEARN? */
    bool has_normal;            /* Actions output to OFPP_NORMAL? */
    bool has_fin_timeout;       /* Actions include NXAST_FIN_TIMEOUT? */
    uint16_t nf_output_iface;   /* Output interface index for NetFlow. */
    mirror_mask_t mirrors;      /* Bitmap of associated mirrors. */

/* xlate_actions() initializes and uses these members, but the client has no
 * reason to look at them. */

    int recurse;                /* Recursion level, via xlate_table_action. */
    bool max_resubmit_trigger;  /* Recursed too deeply during translation. */
    struct flow base_flow;      /* Flow at the last commit. */
    uint32_t orig_skb_priority; /* Priority when packet arrived. */
    uint8_t table_id;           /* OpenFlow table ID where flow was found. */
    uint32_t sflow_n_outputs;   /* Number of output ports. */
    uint16_t sflow_odp_port;    /* Output port for composing sFlow action. */
    uint16_t user_cookie_offset;/* Used for user_action_cookie fixup. */
    bool exit;                  /* No further actions should be processed. */
    struct flow orig_flow;      /* Copy of original flow. */
};

之后调用xlate_actions,openflow1.0定义了如下action,

enum ofp10_action_type {
    OFPAT10_OUTPUT,             /* Output to switch port. */
    OFPAT10_SET_VLAN_VID,       /* Set the 802.1q VLAN id. */
    OFPAT10_SET_VLAN_PCP,       /* Set the 802.1q priority. */
    OFPAT10_STRIP_VLAN,         /* Strip the 802.1q header. */
    OFPAT10_SET_DL_SRC,         /* Ethernet source address. */
    OFPAT10_SET_DL_DST,         /* Ethernet destination address. */
    OFPAT10_SET_NW_SRC,         /* IP source address. */
    OFPAT10_SET_NW_DST,         /* IP destination address. */
    OFPAT10_SET_NW_TOS,         /* IP ToS (DSCP field, 6 bits). */
    OFPAT10_SET_TP_SRC,         /* TCP/UDP source port. */
    OFPAT10_SET_TP_DST,         /* TCP/UDP destination port. */
    OFPAT10_ENQUEUE,            /* Output to queue. */
    OFPAT10_VENDOR = 0xffff
};

对应不同的action type,其action传入的数据结构也不同,e.g.

/* Action structure for OFPAT10_SET_VLAN_VID. */
struct ofp_action_vlan_vid {
    ovs_be16 type;                  /* OFPAT10_SET_VLAN_VID. */
    ovs_be16 len;                   /* Length is 8. */
    ovs_be16 vlan_vid;              /* VLAN id. */
    uint8_t pad[2];
};


/* Action structure for OFPAT10_SET_VLAN_PCP. */
struct ofp_action_vlan_pcp {
    ovs_be16 type;                  /* OFPAT10_SET_VLAN_PCP. */
    ovs_be16 len;                   /* Length is 8. */
    uint8_t vlan_pcp;               /* VLAN priority. */
    uint8_t pad[3];
};

union ofp_action {
    ovs_be16 type;
    struct ofp_action_header header;
    struct ofp_action_vendor_header vendor;
    struct ofp_action_output output;
    struct ofp_action_vlan_vid vlan_vid;
    struct ofp_action_vlan_pcp vlan_pcp;
    struct ofp_action_nw_addr nw_addr;
    struct ofp_action_nw_tos nw_tos;
    struct ofp_action_tp_port tp_port;
};

do_xlate_actions传入一个struct ofp_action*数组,对每个struct ofp_action,执行不同的操作,e.g.

        case OFPUTIL_OFPAT10_OUTPUT:
            xlate_output_action(ctx, &ia->output);
            break;

        case OFPUTIL_OFPAT10_SET_VLAN_VID:
            ctx->flow.vlan_tci &= ~htons(VLAN_VID_MASK);
            ctx->flow.vlan_tci |= ia->vlan_vid.vlan_vid | htons(VLAN_CFI);
            break;

        case OFPUTIL_OFPAT10_SET_VLAN_PCP:
            ctx->flow.vlan_tci &= ~htons(VLAN_PCP_MASK);
            ctx->flow.vlan_tci |= htons(
                (ia->vlan_pcp.vlan_pcp << VLAN_PCP_SHIFT) | VLAN_CFI);
            break;

        case OFPUTIL_OFPAT10_STRIP_VLAN:
            ctx->flow.vlan_tci = htons(0);
            break;

对于转发报文,最重要的就是xlate_output_action,该函数调用的xlate_output_action__,其中传入的port为datapath port index,或者其他控制参数,可以在ofp_port的定义中看到如下定义:

enum ofp_port {
    /* Maximum number of physical switch ports. */
    OFPP_MAX = 0xff00,
    
    /* Fake output "ports". */
    OFPP_IN_PORT    = 0xfff8,  /* Send the packet out the input port.  This
                                  virtual port must be explicitly used
                                  in order to send back out of the input
                                  port. */
    OFPP_TABLE      = 0xfff9,  /* Perform actions in flow table.
                                  NB: This can only be the destination
                                  port for packet-out messages. */
    OFPP_NORMAL     = 0xfffa,  /* Process with normal L2/L3 switching. */
    OFPP_FLOOD      = 0xfffb,  /* All physical ports except input port and
                                  those disabled by STP. */
    OFPP_ALL        = 0xfffc,  /* All physical ports except input port. */
    OFPP_CONTROLLER = 0xfffd,  /* Send to controller. */
    OFPP_LOCAL      = 0xfffe,  /* Local openflow "port". */
    OFPP_NONE       = 0xffff   /* Not associated with a physical port. */
};  

在xlate_output_action__中,大部分情况都是走到OFPP_NORMAL里面,调用xlate_normal,里面会调用mac_learning_lookup, 查找mac表找到报文的出口port,然后调用output_normal,output_normal最终调用compose_output_action

compose_output_action__(struct action_xlate_ctx *ctx, uint16_t ofp_port,

                        bool check_stp)
{
    const struct ofport_dpif *ofport = get_ofp_port(ctx->ofproto, ofp_port);
    uint16_t odp_port = ofp_port_to_odp_port(ofp_port);
    ovs_be16 flow_vlan_tci = ctx->flow.vlan_tci;
    uint8_t flow_nw_tos = ctx->flow.nw_tos;
    uint16_t out_port;

...

    out_port = vsp_realdev_to_vlandev(ctx->ofproto, odp_port,
                                      ctx->flow.vlan_tci);
    if (out_port != odp_port) {
        ctx->flow.vlan_tci = htons(0);
    }
    commit_odp_actions(&ctx->flow, &ctx->base_flow, ctx->odp_actions);
    nl_msg_put_u32(ctx->odp_actions, OVS_ACTION_ATTR_OUTPUT, out_port);

    ctx->sflow_odp_port = odp_port;
    ctx->sflow_n_outputs++;
    ctx->nf_output_iface = ofp_port;
    ctx->flow.vlan_tci = flow_vlan_tci;
    ctx->flow.nw_tos = flow_nw_tos;
}

commit_odp_actions,用来把所有action编码车功能nlattr的格式存到ctx->odp_actions中,之后的nl_msg_put_u32(ctx->odp_actions, OVS_ACTION_ATTR_OUTPUT, out_port)把报文的出口port添加进去,这样一条flow action差不多组合完毕了


下面来讨论下vswitchd中的cam表,代码在lib/mac-learning.h lib/mac-learning.c中,

vswitchd内部维护了一个mac/port的cam表,其中mac entry的老化时间为300秒,cam表定义了flooding vlan的概念,即如果vlan是flooding,表示不会去学习任何地址,这个vlan的所有转发都通过flooding完成,

/* A MAC learning table entry. */
struct mac_entry {
    struct hmap_node hmap_node; /* Node in a mac_learning hmap. */         
    struct list lru_node;       /* Element in 'lrus' list. */
    time_t expires;             /* Expiration time. */
    time_t grat_arp_lock;       /* Gratuitous ARP lock expiration time. */        
    uint8_t mac[ETH_ADDR_LEN];  /* Known MAC address. */
    uint16_t vlan;              /* VLAN tag. */
    tag_type tag;               /* Tag for this learning entry. */

    /* Learned port. */
    union {
        void *p;
        int i;
    } port;
};

/* MAC learning table. */
struct mac_learning {
    struct hmap table;          /* Learning table. */        mac_entry组成的hmap哈希表,mac_entry通过hmap_node挂载到mac_learning->table中
    struct list lrus;           /* In-use entries, least recently used at the
                                   front, most recently used at the back. */              lru的链表,mac_entry通过lru_node挂载到mac_learning->lrus中
    uint32_t secret;            /* Secret for randomizing hash table. */     
    unsigned long *flood_vlans; /* Bitmap of learning disabled VLANs. */
    unsigned int idle_time;     /* Max age before deleting an entry. */           最大老化时间
};  


static uint32_t
mac_table_hash(const struct mac_learning *ml, const uint8_t mac[ETH_ADDR_LEN],
               uint16_t vlan)
{
    unsigned int mac1 = get_unaligned_u32((uint32_t *) mac);
    unsigned int mac2 = get_unaligned_u16((uint16_t *) (mac + 4));
    return hash_3words(mac1, mac2 | (vlan << 16), ml->secret);
}   

mac_entry计算的hash值,由mac_learning->secret,vlan, mac地址共同通过hash_3words计算出来


mac_entry_lookup,通过mac地址,vlan来查看是否已经对应的mac_entry

get_lru,找到lru链表对应的第一个mac_entry

mac_learning_create/mac_learning_destroy,创建/销毁mac_learning表

mac_learning_may_learn,如果vlan不是flooding vlan且mac地址不是多播地址,返回true

mac_learning_insert,向mac_learning中插入一条mac_entry,首先通过mac_entry_lookup查看mac, vlan对应的mac_entry是否存在,不存在的话如果此时mac_learning已经有了MAC_MAX条mac_entry,老化最老的那条,之后创建mac_entry并插入到cam表中。

mac_learning_lookup,调用mac_entry_lookup在cam表中查找某个vlan对应的mac地址

mac_learning_run,循环老化已经超时的mac_entry





How to Port Open vSwitch to New Software or Hardware
====================================================

Open vSwitch (OVS) is intended to be easily ported to new software and
hardware platforms.  This document describes the types of changes that
are most likely to be necessary in porting OVS to Unix-like platforms.
(Porting OVS to other kinds of platforms is likely to be more
difficult.)


Vocabulary
----------

For historical reasons, different words are used for essentially the
same concept in different areas of the Open vSwitch source tree.  Here
is a concordance, indexed by the area of the source tree:

        datapath/       vport           ---
        vswitchd/       iface           port
        ofproto/        port            bundle
        ofproto/bond.c  slave           bond
        lib/lacp.c      slave           lacp
        lib/netdev.c    netdev          ---
        database        Interface       Port


Open vSwitch Architectural Overview
-----------------------------------

The following diagram shows the very high-level architecture of Open
vSwitch from a porter's perspective.

                   +-------------------+
                   |    ovs-vswitchd   |<-->ovsdb-server
                   +-------------------+
                   |      ofproto      |<-->OpenFlow controllers
                   +--------+-+--------+
                   | netdev | | ofproto|
                   +--------+ |provider|
                   | netdev | +--------+
                   |provider|
                   +--------+

Some of the components are generic.  Modulo bugs or inadequacies,
these components should not need to be modified as part of a port:

  - "ovs-vswitchd" is the main Open vSwitch userspace program, in
    vswitchd/.  It reads the desired Open vSwitch configuration from
    the ovsdb-server program over an IPC channel and passes this
    configuration down to the "ofproto" library.  It also passes
    certain status and statistical information from ofproto back
    into the database.

  - "ofproto" is the Open vSwitch library, in ofproto/, that
    implements an OpenFlow switch.  It talks to OpenFlow controllers
    over the network and to switch hardware or software through an
    "ofproto provider", explained further below.

  - "netdev" is the Open vSwitch library, in lib/netdev.c, that
    abstracts interacting with network devices, that is, Ethernet
    interfaces.  The netdev library is a thin layer over "netdev
    provider" code, explained further below.

The other components may need attention during a port.  You will
almost certainly have to implement a "netdev provider".  Depending on
the type of port you are doing and the desired performance, you may
also have to implement an "ofproto provider" or a lower-level
component called a "dpif" provider.

The following sections talk about these components in more detail.


Writing a netdev Provider
-------------------------

A "netdev provider" implements an operating system and hardware
specific interface to "network devices", e.g. eth0 on Linux.  Open
vSwitch must be able to open each port on a switch as a netdev, so you
will need to implement a "netdev provider" that works with your switch
hardware and software.

struct netdev_class, in lib/netdev-provider.h, defines the interfaces
required to implement a netdev.  That structure contains many function
pointers, each of which has a comment that is meant to describe its
behavior in detail.  If the requirements are unclear, please report
this as a bug.

The netdev interface can be divided into a few rough categories:

  * Functions required to properly implement OpenFlow features.  For
    example, OpenFlow requires the ability to report the Ethernet
    hardware address of a port.  These functions must be implemented
    for minimally correct operation.

  * Functions required to implement optional Open vSwitch features.
    For example, the Open vSwitch support for in-band control
    requires netdev support for inspecting the TCP/IP stack's ARP
    table.  These functions must be implemented if the corresponding
    OVS features are to work, but may be omitted initially.

  * Functions needed in some implementations but not in others.  For
    example, most kinds of ports (see below) do not need
    functionality to receive packets from a network device.

The existing netdev implementations may serve as useful examples
during a port:

  * lib/netdev-linux.c implements netdev functionality for Linux
    network devices, using Linux kernel calls.  It may be a good
    place to start for full-featured netdev implementations.

  * lib/netdev-vport.c provides support for "virtual ports"
    implemented by the Open vSwitch datapath module for the Linux
    kernel.  This may serve as a model for minimal netdev
    implementations.

  * lib/netdev-dummy.c is a fake netdev implementation useful only
    for testing.


Porting Strategies
------------------

After a netdev provider has been implemented for a system's network
devices, you may choose among three basic porting strategies.

The lowest-effort strategy is to use the "userspace switch"
implementation built into Open vSwitch.  This ought to work, without
writing any more code, as long as the netdev provider that you
implemented supports receiving packets.  It yields poor performance,
however, because every packet passes through the ovs-vswitchd process.
See [INSTALL.userspace.md] for instructions on how to configure a
userspace switch.

If the userspace switch is not the right choice for your port, then
you will have to write more code.  You may implement either an
"ofproto provider" or a "dpif provider".  Which you should choose
depends on a few different factors:

  * Only an ofproto provider can take full advantage of hardware
    with built-in support for wildcards (e.g. an ACL table or a
    TCAM).

  * A dpif provider can take advantage of the Open vSwitch built-in
    implementations of bonding, LACP, 802.1ag, 802.1Q VLANs, and
    other features.  An ofproto provider has to provide its own
    implementations, if the hardware can support them at all.

  * A dpif provider is usually easier to implement, but most
    appropriate for software switching.  It "explodes" wildcard
    rules into exact-match entries (with an optional wildcard mask).
    This allows fast hash lookups in software, but makes
    inefficient use of TCAMs in hardware that support wildcarding.

The following sections describe how to implement each kind of port.


ofproto Providers
-----------------

An "ofproto provider" is what ofproto uses to directly monitor and
control an OpenFlow-capable switch.  struct ofproto_class, in
ofproto/ofproto-provider.h, defines the interfaces to implement an
ofproto provider for new hardware or software.  That structure contains
many function pointers, each of which has a comment that is meant to
describe its behavior in detail.  If the requirements are unclear,
please report this as a bug.

The ofproto provider interface is preliminary.  Please let us know if
it seems unsuitable for your purpose.  We will try to improve it.


Writing a dpif Provider
-----------------------

Open vSwitch has a built-in ofproto provider named "ofproto-dpif",
which is built on top of a library for manipulating datapaths, called
"dpif".  A "datapath" is a simple flow table, one that is only required
to support exact-match flows, that is, flows without wildcards.  When a
packet arrives on a network device, the datapath looks for it in this
table.  If there is a match, then it performs the associated actions.
If there is no match, the datapath passes the packet up to ofproto-dpif,
which maintains the full OpenFlow flow table.  If the packet matches in
this flow table, then ofproto-dpif executes its actions and inserts a
new entry into the dpif flow table.  (Otherwise, ofproto-dpif passes the
packet up to ofproto to send the packet to the OpenFlow controller, if
one is configured.)

When calculating the dpif flow, ofproto-dpif generates an exact-match
flow that describes the missed packet.  It makes an effort to figure out
what fields can be wildcarded based on the switch's configuration and
OpenFlow flow table.  The dpif is free to ignore the suggested wildcards
and only support the exact-match entry.  However, if the dpif supports
wildcarding, then it can use the masks to match multiple flows with
fewer entries and potentially significantly reduce the number of flow
misses handled by ofproto-dpif.

The "dpif" library in turn delegates much of its functionality to a
"dpif provider".  The following diagram shows how dpif providers fit
into the Open vSwitch architecture:

                _
               |   +-------------------+
               |   |    ovs-vswitchd   |<-->ovsdb-server
               |   +-------------------+
               |   |      ofproto      |<-->OpenFlow controllers
               |   +--------+-+--------+  _
               |   | netdev | |ofproto-|   |
     userspace |   +--------+ |  dpif  |   |
               |   | netdev | +--------+   |
               |   |provider| |  dpif  |   |
               |   +---||---+ +--------+   |
               |       ||     |  dpif  |   | implementation of
               |       ||     |provider|   | ofproto provider
               |_      ||     +---||---+   |
                       ||         ||       |
                _  +---||-----+---||---+   |
               |   |          |datapath|   |
        kernel |   |          +--------+  _|
               |   |                   |
               |_  +--------||---------+
                            ||
                         physical
                           NIC

struct dpif_class, in lib/dpif-provider.h, defines the interfaces
required to implement a dpif provider for new hardware or software.
That structure contains many function pointers, each of which has a
comment that is meant to describe its behavior in detail.  If the
requirements are unclear, please report this as a bug.

There are two existing dpif implementations that may serve as
useful examples during a port:

  * lib/dpif-netlink.c is a Linux-specific dpif implementation that
    talks to an Open vSwitch-specific kernel module (whose sources
    are in the "datapath" directory).  The kernel module performs
    all of the switching work, passing packets that do not match any
    flow table entry up to userspace.  This dpif implementation is
    essentially a wrapper around calls into the kernel module.

  * lib/dpif-netdev.c is a generic dpif implementation that performs
    all switching internally.  This is how the Open vSwitch
    userspace switch is implemented.

vswitchd是ovs中最核心的组件,openflow的相关逻辑都在vswitchd里实现,一般来说,ovs分为datapath, vswitchd以及ovsdb三个部分,datapath一般是和具体是数据面平台相关的,比如白盒交换机,或者Linux内核等,同时datapath不是必须的组件。ovsdb用于存储vswitch本身的配置信息,比如端口,拓扑,规则等。vswitchd在ovs dist包里是以用户态进程形式呈现的,但这个不是绝对的,上文摘录的部分给出了把ovs移植到其他平台上的方法,也算是目前官方仅有的一篇大致描述了ovs架构的文档

可以看出vswitchd本身是分层的结构,最上面的daemon层主要用于和ovsdb通信,做配置的下发和更新等,中间是ofproto层,用于和openflow控制器通信,以及通过ofproto_class暴露了ofproto provider接口,不同平台上openflow的具体实现就通过ofproto_class统一了接口。

在ovs的定义里,netdev代表了具体平台的设备实现,e.g. linux内核的net_device或者移植到交换机平台下的port等,struct netdev_class定义了netdev-provider的具体实现需要的接口,具体的平台实现需要支持这些统一的接口,从而完成netdev设备的创建,销毁,打开,关闭等一系列操作。

不同的netdev类型通过netdev_register_provider被注册,vswitchd内部会保存一个struct cmap netdev_classes保存所有注册的netdev类型,struct netdev定义如下

[cpp] view plain copy
  1. /* A network device (e.g. an Ethernet device). 
  2.  * 
  3.  * Network device implementations may read these members but should not modify 
  4.  * them. */  
  5. struct netdev {  
  6.     /* The following do not change during the lifetime of a struct netdev. */  
  7.     char *name;                         /* Name of network device. */  
  8.     const struct netdev_class *netdev_class; /* Functions to control 
  9.                                                 this device. */  
  10.   
  11.     /* A sequence number which indicates changes in one of 'netdev''s 
  12.      * properties.   It must be nonzero so that users have a value which 
  13.      * they may use as a reset when tracking 'netdev'. 
  14.      * 
  15.      * Minimally, the sequence number is required to change whenever 
  16.      * 'netdev''s flags, features, ethernet address, or carrier changes. */  
  17.     uint64_t change_seq;  
  18.   
  19.     /* A netdev provider might be unable to change some of the device's 
  20.      * parameter (n_rxq, mtu) when the device is in use.  In this case 
  21.      * the provider can notify the upper layer by calling 
  22.      * netdev_request_reconfigure().  The upper layer will react by stopping 
  23.      * the operations on the device and calling netdev_reconfigure() to allow 
  24.      * the configuration changes.  'last_reconfigure_seq' remembers the value 
  25.      * of 'reconfigure_seq' when the last reconfiguration happened. */  
  26.     struct seq *reconfigure_seq;  
  27.     uint64_t last_reconfigure_seq;  
  28.   
  29.     /* If this is 'true', the user explicitly specified an MTU for this 
  30.      * netdev.  Otherwise, Open vSwitch is allowed to override it. */  
  31.     bool mtu_user_config;  
  32.   
  33.     /* The core netdev code initializes these at netdev construction and only 
  34.      * provide read-only access to its client.  Netdev implementations may 
  35.      * modify them. */  
  36.     int n_txq;  
  37.     int n_rxq;  
  38.     int ref_cnt;                        /* Times this devices was opened. */  
  39.     struct shash_node *node;            /* Pointer to element in global map. */  
  40.     struct ovs_list saved_flags_list; /* Contains "struct netdev_saved_flags". */  
  41. };  
struct netdev_class的定义如下,可以看出netdev_class更接近于一个ops结构体,同时加入了设备的队列管理操作
[cpp] view plain copy
  1. /* Network device class structure, to be defined by each implementation of a 
  2.  * network device. 
  3.  * 
  4.  * These functions return 0 if successful or a positive errno value on failure, 
  5.  * except where otherwise noted. 
  6.  * 
  7.  * 
  8.  * Data Structures 
  9.  * =============== 
  10.  * 
  11.  * These functions work primarily with two different kinds of data structures: 
  12.  * 
  13.  *   - "struct netdev", which represents a network device. 
  14.  * 
  15.  *   - "struct netdev_rxq", which represents a handle for capturing packets 
  16.  *     received on a network device 
  17.  * 
  18.  * Each of these data structures contains all of the implementation-independent 
  19.  * generic state for the respective concept, called the "base" state.  None of 
  20.  * them contains any extra space for implementations to use.  Instead, each 
  21.  * implementation is expected to declare its own data structure that contains 
  22.  * an instance of the generic data structure plus additional 
  23.  * implementation-specific members, called the "derived" state.  The 
  24.  * implementation can use casts or (preferably) the CONTAINER_OF macro to 
  25.  * obtain access to derived state given only a pointer to the embedded generic 
  26.  * data structure. 
  27.  * 
  28.  * 
  29.  * Life Cycle 
  30.  * ========== 
  31.  * 
  32.  * Four stylized functions accompany each of these data structures: 
  33.  * 
  34.  *            "alloc"          "construct"        "destruct"       "dealloc" 
  35.  *            ------------   ----------------  ---------------  -------------- 
  36.  * netdev      ->alloc        ->construct        ->destruct        ->dealloc 
  37.  * netdev_rxq  ->rxq_alloc    ->rxq_construct    ->rxq_destruct    ->rxq_dealloc 
  38.  * 
  39.  * Any instance of a given data structure goes through the following life 
  40.  * cycle: 
  41.  * 
  42.  *   1. The client calls the "alloc" function to obtain raw memory.  If "alloc" 
  43.  *      fails, skip all the other steps. 
  44.  * 
  45.  *   2. The client initializes all of the data structure's base state.  If this 
  46.  *      fails, skip to step 7. 
  47.  * 
  48.  *   3. The client calls the "construct" function.  The implementation 
  49.  *      initializes derived state.  It may refer to the already-initialized 
  50.  *      base state.  If "construct" fails, skip to step 6. 
  51.  * 
  52.  *   4. The data structure is now initialized and in use. 
  53.  * 
  54.  *   5. When the data structure is no longer needed, the client calls the 
  55.  *      "destruct" function.  The implementation uninitializes derived state. 
  56.  *      The base state has not been uninitialized yet, so the implementation 
  57.  *      may still refer to it. 
  58.  * 
  59.  *   6. The client uninitializes all of the data structure's base state. 
  60.  * 
  61.  *   7. The client calls the "dealloc" to free the raw memory.  The 
  62.  *      implementation must not refer to base or derived state in the data 
  63.  *      structure, because it has already been uninitialized. 
  64.  * 
  65.  * If netdev support multi-queue IO then netdev->construct should set initialize 
  66.  * netdev->n_rxq to number of queues. 
  67.  * 
  68.  * Each "alloc" function allocates and returns a new instance of the respective 
  69.  * data structure.  The "alloc" function is not given any information about the 
  70.  * use of the new data structure, so it cannot perform much initialization. 
  71.  * Its purpose is just to ensure that the new data structure has enough room 
  72.  * for base and derived state.  It may return a null pointer if memory is not 
  73.  * available, in which case none of the other functions is called. 
  74.  * 
  75.  * Each "construct" function initializes derived state in its respective data 
  76.  * structure.  When "construct" is called, all of the base state has already 
  77.  * been initialized, so the "construct" function may refer to it.  The 
  78.  * "construct" function is allowed to fail, in which case the client calls the 
  79.  * "dealloc" function (but not the "destruct" function). 
  80.  * 
  81.  * Each "destruct" function uninitializes and frees derived state in its 
  82.  * respective data structure.  When "destruct" is called, the base state has 
  83.  * not yet been uninitialized, so the "destruct" function may refer to it.  The 
  84.  * "destruct" function is not allowed to fail. 
  85.  * 
  86.  * Each "dealloc" function frees raw memory that was allocated by the 
  87.  * "alloc" function.  The memory's base and derived members might not have ever 
  88.  * been initialized (but if "construct" returned successfully, then it has been 
  89.  * "destruct"ed already).  The "dealloc" function is not allowed to fail. 
  90.  * 
  91.  * 
  92.  * Device Change Notification 
  93.  * ========================== 
  94.  * 
  95.  * Minimally, implementations are required to report changes to netdev flags, 
  96.  * features, ethernet address or carrier through connectivity_seq. Changes to 
  97.  * other properties are allowed to cause notification through this interface, 
  98.  * although implementations should try to avoid this. connectivity_seq_get() 
  99.  * can be used to acquire a reference to the struct seq. The interface is 
  100.  * described in detail in seq.h. */  
  101. struct netdev_class {  
  102.     /* Type of netdevs in this class, e.g. "system", "tap", "gre", etc. 
  103.      * 
  104.      * One of the providers should supply a "system" type, since this is 
  105.      * the type assumed if no type is specified when opening a netdev. 
  106.      * The "system" type corresponds to an existing network device on 
  107.      * the system. */  
  108.     const char *type;  
  109.   
  110.     /* If 'true' then this netdev should be polled by PMD threads. */  
  111.     bool is_pmd;  
  112.   
  113. /* ## ------------------- ## */  
  114. /* ## Top-Level Functions ## */  
  115. /* ## ------------------- ## */  
  116.   
  117.     /* Called when the netdev provider is registered, typically at program 
  118.      * startup.  Returning an error from this function will prevent any network 
  119.      * device in this class from being opened. 
  120.      * 
  121.      * This function may be set to null if a network device class needs no 
  122.      * initialization at registration time. */  
  123.     int (*init)(void);  
  124.   
  125.     /* Performs periodic work needed by netdevs of this class.  May be null if 
  126.      * no periodic work is necessary. 
  127.      * 
  128.      * 'netdev_class' points to the class.  It is useful in case the same 
  129.      * function is used to implement different classes. */  
  130.     void (*run)(const struct netdev_class *netdev_class);  
  131.   
  132.     /* Arranges for poll_block() to wake up if the "run" member function needs 
  133.      * to be called.  Implementations are additionally required to wake 
  134.      * whenever something changes in any of its netdevs which would cause their 
  135.      * ->change_seq() function to change its result.  May be null if nothing is 
  136.      * needed here. 
  137.      * 
  138.      * 'netdev_class' points to the class.  It is useful in case the same 
  139.      * function is used to implement different classes. */  
  140.     void (*wait)(const struct netdev_class *netdev_class);  
  141.   
  142. /* ## ---------------- ## */  
  143. /* ## netdev Functions ## */  
  144. /* ## ---------------- ## */  
  145.   
  146.     /* Life-cycle functions for a netdev.  See the large comment above on 
  147.      * struct netdev_class. */  
  148.     struct netdev *(*alloc)(void);  
  149.     int (*construct)(struct netdev *);  
  150.     void (*destruct)(struct netdev *);  
  151.     void (*dealloc)(struct netdev *);  
  152.   
  153.     /* Fetches the device 'netdev''s configuration, storing it in 'args'. 
  154.      * The caller owns 'args' and pre-initializes it to an empty smap. 
  155.      * 
  156.      * If this netdev class does not have any configuration options, this may 
  157.      * be a null pointer. */  
  158.     int (*get_config)(const struct netdev *netdev, struct smap *args);  
  159.   
  160.     /* Changes the device 'netdev''s configuration to 'args'. 
  161.      * 
  162.      * If this netdev class does not support configuration, this may be a null 
  163.      * pointer. */  
  164.     int (*set_config)(struct netdev *netdev, const struct smap *args);  
  165.   
  166.     /* Returns the tunnel configuration of 'netdev'.  If 'netdev' is 
  167.      * not a tunnel, returns null. 
  168.      * 
  169.      * If this function would always return null, it may be null instead. */  
  170.     const struct netdev_tunnel_config *  
  171.         (*get_tunnel_config)(const struct netdev *netdev);  
  172.   
  173.     /* Build Tunnel header.  Ethernet and ip header parameters are passed to 
  174.      * tunnel implementation to build entire outer header for given flow. */  
  175.     int (*build_header)(const struct netdev *, struct ovs_action_push_tnl *data,  
  176.                         const struct netdev_tnl_build_header_params *params);  
  177.   
  178.     /* build_header() can not build entire header for all packets for given 
  179.      * flow.  Push header is called for packet to build header specific to 
  180.      * a packet on actual transmit.  It uses partial header build by 
  181.      * build_header() which is passed as data. */  
  182.     void (*push_header)(struct dp_packet *packet,  
  183.                         const struct ovs_action_push_tnl *data);  
  184.   
  185.     /* Pop tunnel header from packet, build tunnel metadata and resize packet 
  186.      * for further processing. 
  187.      * Returns NULL in case of error or tunnel implementation queued packet for further 
  188.      * processing. */  
  189.     struct dp_packet * (*pop_header)(struct dp_packet *packet);  
  190.   
  191.     /* Returns the id of the numa node the 'netdev' is on.  If there is no 
  192.      * such info, returns NETDEV_NUMA_UNSPEC. */  
  193.     int (*get_numa_id)(const struct netdev *netdev);  
  194.   
  195.     /* Configures the number of tx queues of 'netdev'. Returns 0 if successful, 
  196.      * otherwise a positive errno value. 
  197.      * 
  198.      * 'n_txq' specifies the exact number of transmission queues to create. 
  199.      * 
  200.      * The caller will call netdev_reconfigure() (if necessary) before using 
  201.      * netdev_send() on any of the newly configured queues, giving the provider 
  202.      * a chance to adjust its settings. 
  203.      * 
  204.      * On error, the tx queue configuration is unchanged. */  
  205.     int (*set_tx_multiq)(struct netdev *netdev, unsigned int n_txq);  
  206.   
  207.     /* Sends buffers on 'netdev'. 
  208.      * Returns 0 if successful (for every buffer), otherwise a positive errno 
  209.      * value.  Returns EAGAIN without blocking if one or more packets cannot be 
  210.      * queued immediately. Returns EMSGSIZE if a partial packet was transmitted 
  211.      * or if a packet is too big or too small to transmit on the device. 
  212.      * 
  213.      * If the function returns a non-zero value, some of the packets might have 
  214.      * been sent anyway. 
  215.      * 
  216.      * If 'may_steal' is false, the caller retains ownership of all the 
  217.      * packets.  If 'may_steal' is true, the caller transfers ownership of all 
  218.      * the packets to the network device, regardless of success. 
  219.      * 
  220.      * If 'concurrent_txq' is true, the caller may perform concurrent calls 
  221.      * to netdev_send() with the same 'qid'. The netdev provider is responsible 
  222.      * for making sure that these concurrent calls do not create a race 
  223.      * condition by using locking or other synchronization if required. 
  224.      * 
  225.      * The network device is expected to maintain one or more packet 
  226.      * transmission queues, so that the caller does not ordinarily have to 
  227.      * do additional queuing of packets.  'qid' specifies the queue to use 
  228.      * and can be ignored if the implementation does not support multiple 
  229.      * queues. 
  230.      * 
  231.      * May return EOPNOTSUPP if a network device does not implement packet 
  232.      * transmission through this interface.  This function may be set to null 
  233.      * if it would always return EOPNOTSUPP anyhow.  (This will prevent the 
  234.      * network device from being usefully used by the netdev-based "userspace 
  235.      * datapath".  It will also prevent the OVS implementation of bonding from 
  236.      * working properly over 'netdev'.) */  
  237.     int (*send)(struct netdev *netdev, int qid, struct dp_packet_batch *batch,  
  238.                 bool may_steal, bool concurrent_txq);  
  239.   
  240.     /* Registers with the poll loop to wake up from the next call to 
  241.      * poll_block() when the packet transmission queue for 'netdev' has 
  242.      * sufficient room to transmit a packet with netdev_send(). 
  243.      * 
  244.      * The network device is expected to maintain one or more packet 
  245.      * transmission queues, so that the caller does not ordinarily have to 
  246.      * do additional queuing of packets.  'qid' specifies the queue to use 
  247.      * and can be ignored if the implementation does not support multiple 
  248.      * queues. 
  249.      * 
  250.      * May be null if not needed, such as for a network device that does not 
  251.      * implement packet transmission through the 'send' member function. */  
  252.     void (*send_wait)(struct netdev *netdev, int qid);  
  253.   
  254.     /* Sets 'netdev''s Ethernet address to 'mac' */  
  255.     int (*set_etheraddr)(struct netdev *netdev, const struct eth_addr mac);  
  256.   
  257.     /* Retrieves 'netdev''s Ethernet address into 'mac'. 
  258.      * 
  259.      * This address will be advertised as 'netdev''s MAC address through the 
  260.      * OpenFlow protocol, among other uses. */  
  261.     int (*get_etheraddr)(const struct netdev *netdev, struct eth_addr *mac);  
  262.   
  263.     /* Retrieves 'netdev''s MTU into '*mtup'. 
  264.      * 
  265.      * The MTU is the maximum size of transmitted (and received) packets, in 
  266.      * bytes, not including the hardware header; thus, this is typically 1500 
  267.      * bytes for Ethernet devices. 
  268.      * 
  269.      * If 'netdev' does not have an MTU (e.g. as some tunnels do not), then 
  270.      * this function should return EOPNOTSUPP.  This function may be set to 
  271.      * null if it would always return EOPNOTSUPP. */  
  272.     int (*get_mtu)(const struct netdev *netdev, int *mtup);  
  273.   
  274.     /* Sets 'netdev''s MTU to 'mtu'. 
  275.      * 
  276.      * If 'netdev' does not have an MTU (e.g. as some tunnels do not), then 
  277.      * this function should return EOPNOTSUPP.  This function may be set to 
  278.      * null if it would always return EOPNOTSUPP. */  
  279.     int (*set_mtu)(struct netdev *netdev, int mtu);  
  280.   
  281.     /* Returns the ifindex of 'netdev', if successful, as a positive number. 
  282.      * On failure, returns a negative errno value. 
  283.      * 
  284.      * The desired semantics of the ifindex value are a combination of those 
  285.      * specified by POSIX for if_nametoindex() and by SNMP for ifIndex.  An 
  286.      * ifindex value should be unique within a host and remain stable at least 
  287.      * until reboot.  SNMP says an ifindex "ranges between 1 and the value of 
  288.      * ifNumber" but many systems do not follow this rule anyhow. 
  289.      * 
  290.      * This function may be set to null if it would always return -EOPNOTSUPP. 
  291.      */  
  292.     int (*get_ifindex)(const struct netdev *netdev);  
  293.   
  294.     /* Sets 'carrier' to true if carrier is active (link light is on) on 
  295.      * 'netdev'. 
  296.      * 
  297.      * May be null if device does not provide carrier status (will be always 
  298.      * up as long as device is up). 
  299.      */  
  300.     int (*get_carrier)(const struct netdev *netdev, bool *carrier);  
  301.   
  302.     /* Returns the number of times 'netdev''s carrier has changed since being 
  303.      * initialized. 
  304.      * 
  305.      * If null, callers will assume the number of carrier resets is zero. */  
  306.     long long int (*get_carrier_resets)(const struct netdev *netdev);  
  307.   
  308.     /* Forces ->get_carrier() to poll 'netdev''s MII registers for link status 
  309.      * instead of checking 'netdev''s carrier.  'netdev''s MII registers will 
  310.      * be polled once every 'interval' milliseconds.  If 'netdev' does not 
  311.      * support MII, another method may be used as a fallback.  If 'interval' is 
  312.      * less than or equal to zero, reverts ->get_carrier() to its normal 
  313.      * behavior. 
  314.      * 
  315.      * Most network devices won't support this feature and will set this 
  316.      * function pointer to NULL, which is equivalent to returning EOPNOTSUPP. 
  317.      */  
  318.     int (*set_miimon_interval)(struct netdev *netdev, long long int interval);  
  319.   
  320.     /* Retrieves current device stats for 'netdev' into 'stats'. 
  321.      * 
  322.      * A network device that supports some statistics but not others, it should 
  323.      * set the values of the unsupported statistics to all-1-bits 
  324.      * (UINT64_MAX). */  
  325.     int (*get_stats)(const struct netdev *netdev, struct netdev_stats *);  
  326.   
  327.     /* Stores the features supported by 'netdev' into each of '*current', 
  328.      * '*advertised', '*supported', and '*peer'.  Each value is a bitmap of 
  329.      * NETDEV_F_* bits. 
  330.      * 
  331.      * This function may be set to null if it would always return EOPNOTSUPP. 
  332.      */  
  333.     int (*get_features)(const struct netdev *netdev,  
  334.                         enum netdev_features *current,  
  335.                         enum netdev_features *advertised,  
  336.                         enum netdev_features *supported,  
  337.                         enum netdev_features *peer);  
  338.   
  339.     /* Set the features advertised by 'netdev' to 'advertise', which is a 
  340.      * set of NETDEV_F_* bits. 
  341.      * 
  342.      * This function may be set to null for a network device that does not 
  343.      * support configuring advertisements. */  
  344.     int (*set_advertisements)(struct netdev *netdev,  
  345.                               enum netdev_features advertise);  
  346.   
  347.     /* Attempts to set input rate limiting (policing) policy, such that up to 
  348.      * 'kbits_rate' kbps of traffic is accepted, with a maximum accumulative 
  349.      * burst size of 'kbits' kb. 
  350.      * 
  351.      * This function may be set to null if policing is not supported. */  
  352.     int (*set_policing)(struct netdev *netdev, unsigned int kbits_rate,  
  353.                         unsigned int kbits_burst);  
  354.   
  355.     /* Adds to 'types' all of the forms of QoS supported by 'netdev', or leaves 
  356.      * it empty if 'netdev' does not support QoS.  Any names added to 'types' 
  357.      * should be documented as valid for the "type" column in the "QoS" table 
  358.      * in vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)). 
  359.      * 
  360.      * Every network device must support disabling QoS with a type of "", but 
  361.      * this function must not add "" to 'types'. 
  362.      * 
  363.      * The caller is responsible for initializing 'types' (e.g. with 
  364.      * sset_init()) before calling this function.  The caller retains ownership 
  365.      * of 'types'. 
  366.      * 
  367.      * May be NULL if 'netdev' does not support QoS at all. */  
  368.     int (*get_qos_types)(const struct netdev *netdev, struct sset *types);  
  369.   
  370.     /* Queries 'netdev' for its capabilities regarding the specified 'type' of 
  371.      * QoS.  On success, initializes 'caps' with the QoS capabilities. 
  372.      * 
  373.      * Should return EOPNOTSUPP if 'netdev' does not support 'type'.  May be 
  374.      * NULL if 'netdev' does not support QoS at all. */  
  375.     int (*get_qos_capabilities)(const struct netdev *netdev,  
  376.                                 const char *type,  
  377.                                 struct netdev_qos_capabilities *caps);  
  378.   
  379.     /* Queries 'netdev' about its currently configured form of QoS.  If 
  380.      * successful, stores the name of the current form of QoS into '*typep' 
  381.      * and any details of configuration as string key-value pairs in 
  382.      * 'details'. 
  383.      * 
  384.      * A '*typep' of "" indicates that QoS is currently disabled on 'netdev'. 
  385.      * 
  386.      * The caller initializes 'details' before calling this function.  The 
  387.      * caller takes ownership of the string key-values pairs added to 
  388.      * 'details'. 
  389.      * 
  390.      * The netdev retains ownership of '*typep'. 
  391.      * 
  392.      * '*typep' will be one of the types returned by netdev_get_qos_types() for 
  393.      * 'netdev'.  The contents of 'details' should be documented as valid for 
  394.      * '*typep' in the "other_config" column in the "QoS" table in 
  395.      * vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)). 
  396.      * 
  397.      * May be NULL if 'netdev' does not support QoS at all. */  
  398.     int (*get_qos)(const struct netdev *netdev,  
  399.                    const char **typep, struct smap *details);  
  400.   
  401.     /* Attempts to reconfigure QoS on 'netdev', changing the form of QoS to 
  402.      * 'type' with details of configuration from 'details'. 
  403.      * 
  404.      * On error, the previous QoS configuration is retained. 
  405.      * 
  406.      * When this function changes the type of QoS (not just 'details'), this 
  407.      * also resets all queue configuration for 'netdev' to their defaults 
  408.      * (which depend on the specific type of QoS).  Otherwise, the queue 
  409.      * configuration for 'netdev' is unchanged. 
  410.      * 
  411.      * 'type' should be "" (to disable QoS) or one of the types returned by 
  412.      * netdev_get_qos_types() for 'netdev'.  The contents of 'details' should 
  413.      * be documented as valid for the given 'type' in the "other_config" column 
  414.      * in the "QoS" table in vswitchd/vswitch.xml (which is built as 
  415.      * ovs-vswitchd.conf.db(8)). 
  416.      * 
  417.      * May be NULL if 'netdev' does not support QoS at all. */  
  418.     int (*set_qos)(struct netdev *netdev,  
  419.                    const char *type, const struct smap *details);  
  420.   
  421.     /* Queries 'netdev' for information about the queue numbered 'queue_id'. 
  422.      * If successful, adds that information as string key-value pairs to 
  423.      * 'details'.  Returns 0 if successful, otherwise a positive errno value. 
  424.      * 
  425.      * Should return EINVAL if 'queue_id' is greater than or equal to the 
  426.      * number of supported queues (as reported in the 'n_queues' member of 
  427.      * struct netdev_qos_capabilities by 'get_qos_capabilities'). 
  428.      * 
  429.      * The caller initializes 'details' before calling this function.  The 
  430.      * caller takes ownership of the string key-values pairs added to 
  431.      * 'details'. 
  432.      * 
  433.      * The returned contents of 'details' should be documented as valid for the 
  434.      * given 'type' in the "other_config" column in the "Queue" table in 
  435.      * vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)). 
  436.      */  
  437.     int (*get_queue)(const struct netdev *netdev,  
  438.                      unsigned int queue_id, struct smap *details);  
  439.   
  440.     /* Configures the queue numbered 'queue_id' on 'netdev' with the key-value 
  441.      * string pairs in 'details'.  The contents of 'details' should be 
  442.      * documented as valid for the given 'type' in the "other_config" column in 
  443.      * the "Queue" table in vswitchd/vswitch.xml (which is built as 
  444.      * ovs-vswitchd.conf.db(8)).  Returns 0 if successful, otherwise a positive 
  445.      * errno value.  On failure, the given queue's configuration should be 
  446.      * unmodified. 
  447.      * 
  448.      * Should return EINVAL if 'queue_id' is greater than or equal to the 
  449.      * number of supported queues (as reported in the 'n_queues' member of 
  450.      * struct netdev_qos_capabilities by 'get_qos_capabilities'), or if 
  451.      * 'details' is invalid for the type of queue. 
  452.      * 
  453.      * This function does not modify 'details', and the caller retains 
  454.      * ownership of it. 
  455.      * 
  456.      * May be NULL if 'netdev' does not support QoS at all. */  
  457.     int (*set_queue)(struct netdev *netdev,  
  458.                      unsigned int queue_id, const struct smap *details);  
  459.   
  460.     /* Attempts to delete the queue numbered 'queue_id' from 'netdev'. 
  461.      * 
  462.      * Should return EINVAL if 'queue_id' is greater than or equal to the 
  463.      * number of supported queues (as reported in the 'n_queues' member of 
  464.      * struct netdev_qos_capabilities by 'get_qos_capabilities').  Should 
  465.      * return EOPNOTSUPP if 'queue_id' is valid but may not be deleted (e.g. if 
  466.      * 'netdev' has a fixed set of queues with the current QoS mode). 
  467.      * 
  468.      * May be NULL if 'netdev' does not support QoS at all, or if all of its 
  469.      * QoS modes have fixed sets of queues. */  
  470.     int (*delete_queue)(struct netdev *netdev, unsigned int queue_id);  
  471.   
  472.     /* Obtains statistics about 'queue_id' on 'netdev'.  Fills 'stats' with the 
  473.      * queue's statistics.  May set individual members of 'stats' to all-1-bits 
  474.      * if the statistic is unavailable. 
  475.      * 
  476.      * May be NULL if 'netdev' does not support QoS at all. */  
  477.     int (*get_queue_stats)(const struct netdev *netdev, unsigned int queue_id,  
  478.                            struct netdev_queue_stats *stats);  
  479.   
  480.     /* Attempts to begin dumping the queues in 'netdev'.  On success, returns 0 
  481.      * and initializes '*statep' with any data needed for iteration.  On 
  482.      * failure, returns a positive errno value. 
  483.      * 
  484.      * May be NULL if 'netdev' does not support QoS at all. */  
  485.     int (*queue_dump_start)(const struct netdev *netdev, void **statep);  
  486.   
  487.     /* Attempts to retrieve another queue from 'netdev' for 'state', which was 
  488.      * initialized by a successful call to the 'queue_dump_start' function for 
  489.      * 'netdev'.  On success, stores a queue ID into '*queue_id' and fills 
  490.      * 'details' with the configuration of the queue with that ID.  Returns EOF 
  491.      * if the last queue has been dumped, or a positive errno value on error. 
  492.      * This function will not be called again once it returns nonzero once for 
  493.      * a given iteration (but the 'queue_dump_done' function will be called 
  494.      * afterward). 
  495.      * 
  496.      * The caller initializes and clears 'details' before calling this 
  497.      * function.  The caller takes ownership of the string key-values pairs 
  498.      * added to 'details'. 
  499.      * 
  500.      * The returned contents of 'details' should be documented as valid for the 
  501.      * given 'type' in the "other_config" column in the "Queue" table in 
  502.      * vswitchd/vswitch.xml (which is built as ovs-vswitchd.conf.db(8)). 
  503.      * 
  504.      * May be NULL if 'netdev' does not support QoS at all. */  
  505.     int (*queue_dump_next)(const struct netdev *netdev, void *state,  
  506.                            unsigned int *queue_id, struct smap *details);  
  507.   
  508.     /* Releases resources from 'netdev' for 'state', which was initialized by a 
  509.      * successful call to the 'queue_dump_start' function for 'netdev'. 
  510.      * 
  511.      * May be NULL if 'netdev' does not support QoS at all. */  
  512.     int (*queue_dump_done)(const struct netdev *netdev, void *state);  
  513.   
  514.     /* Iterates over all of 'netdev''s queues, calling 'cb' with the queue's 
  515.      * ID, its statistics, and the 'aux' specified by the caller.  The order of 
  516.      * iteration is unspecified, but (when successful) each queue must be 
  517.      * visited exactly once. 
  518.      * 
  519.      * 'cb' will not modify or free the statistics passed in. */  
  520.     int (*dump_queue_stats)(const struct netdev *netdev,  
  521.                             void (*cb)(unsigned int queue_id,  
  522.                                        struct netdev_queue_stats *,  
  523.                                        void *aux),  
  524.                             void *aux);  
  525.   
  526.     /* Assigns 'addr' as 'netdev''s IPv4 address and 'mask' as its netmask.  If 
  527.      * 'addr' is INADDR_ANY, 'netdev''s IPv4 address is cleared. 
  528.      * 
  529.      * This function may be set to null if it would always return EOPNOTSUPP 
  530.      * anyhow. */  
  531.     int (*set_in4)(struct netdev *netdev, struct in_addr addr,  
  532.                    struct in_addr mask);  
  533.   
  534.     /* Returns all assigned IP address to  'netdev' and returns 0. 
  535.      * API allocates array of address and masks and set it to 
  536.      * '*addr' and '*mask'. 
  537.      * Otherwise, returns a positive errno value and sets '*addr', '*mask 
  538.      * and '*n_addr' to NULL. 
  539.      * 
  540.      * The following error values have well-defined meanings: 
  541.      * 
  542.      *   - EADDRNOTAVAIL: 'netdev' has no assigned IPv6 address. 
  543.      * 
  544.      *   - EOPNOTSUPP: No IPv6 network stack attached to 'netdev'. 
  545.      * 
  546.      * 'addr' may be null, in which case the address itself is not reported. */  
  547.     int (*get_addr_list)(const struct netdev *netdev, struct in6_addr **in,  
  548.                          struct in6_addr **mask, int *n_in6);  
  549.   
  550.     /* Adds 'router' as a default IP gateway for the TCP/IP stack that 
  551.      * corresponds to 'netdev'. 
  552.      * 
  553.      * This function may be set to null if it would always return EOPNOTSUPP 
  554.      * anyhow. */  
  555.     int (*add_router)(struct netdev *netdev, struct in_addr router);  
  556.   
  557.     /* Looks up the next hop for 'host' in the host's routing table.  If 
  558.      * successful, stores the next hop gateway's address (0 if 'host' is on a 
  559.      * directly connected network) in '*next_hop' and a copy of the name of the 
  560.      * device to reach 'host' in '*netdev_name', and returns 0.  The caller is 
  561.      * responsible for freeing '*netdev_name' (by calling free()). 
  562.      * 
  563.      * This function may be set to null if it would always return EOPNOTSUPP 
  564.      * anyhow. */  
  565.     int (*get_next_hop)(const struct in_addr *host, struct in_addr *next_hop,  
  566.                         char **netdev_name);  
  567.   
  568.     /* Retrieves driver information of the device. 
  569.      * 
  570.      * Populates 'smap' with key-value pairs representing the status of the 
  571.      * device.  'smap' is a set of key-value string pairs representing netdev 
  572.      * type specific information.  For more information see 
  573.      * ovs-vswitchd.conf.db(5). 
  574.      * 
  575.      * The caller is responsible for destroying 'smap' and its data. 
  576.      * 
  577.      * This function may be set to null if it would always return EOPNOTSUPP 
  578.      * anyhow. */  
  579.     int (*get_status)(const struct netdev *netdev, struct smap *smap);  
  580.   
  581.     /* Looks up the ARP table entry for 'ip' on 'netdev' and stores the 
  582.      * corresponding MAC address in 'mac'.  A return value of ENXIO, in 
  583.      * particular, indicates that there is no ARP table entry for 'ip' on 
  584.      * 'netdev'. 
  585.      * 
  586.      * This function may be set to null if it would always return EOPNOTSUPP 
  587.      * anyhow. */  
  588.     int (*arp_lookup)(const struct netdev *netdev, ovs_be32 ip,  
  589.                       struct eth_addr *mac);  
  590.   
  591.     /* Retrieves the current set of flags on 'netdev' into '*old_flags'.  Then, 
  592.      * turns off the flags that are set to 1 in 'off' and turns on the flags 
  593.      * that are set to 1 in 'on'.  (No bit will be set to 1 in both 'off' and 
  594.      * 'on'; that is, off & on == 0.) 
  595.      * 
  596.      * This function may be invoked from a signal handler.  Therefore, it 
  597.      * should not do anything that is not signal-safe (such as logging). */  
  598.     int (*update_flags)(struct netdev *netdev, enum netdev_flags off,  
  599.                         enum netdev_flags on, enum netdev_flags *old_flags);  
  600.   
  601.     /* If the provider called netdev_request_reconfigure(), the upper layer 
  602.      * will eventually call this.  The provider can update the device 
  603.      * configuration knowing that the upper layer will not call rxq_recv() or 
  604.      * send() until this function returns. 
  605.      * 
  606.      * On error, the configuration is indeterminant and the device cannot be 
  607.      * used to send and receive packets until a successful configuration is 
  608.      * applied. */  
  609.     int (*reconfigure)(struct netdev *netdev);  
  610.   
  611. /* ## -------------------- ## */  
  612. /* ## netdev_rxq Functions ## */  
  613. /* ## -------------------- ## */  
  614.   
  615. /* If a particular netdev class does not support receiving packets, all these 
  616.  * function pointers must be NULL. */  
  617.   
  618.     /* Life-cycle functions for a netdev_rxq.  See the large comment above on 
  619.      * struct netdev_class. */  
  620.     struct netdev_rxq *(*rxq_alloc)(void);  
  621.     int (*rxq_construct)(struct netdev_rxq *);  
  622.     void (*rxq_destruct)(struct netdev_rxq *);  
  623.     void (*rxq_dealloc)(struct netdev_rxq *);  
  624.   
  625.     /* Attempts to receive a batch of packets from 'rx'.  In 'batch', the 
  626.      * caller supplies 'packets' as the pointer to the beginning of an array 
  627.      * of NETDEV_MAX_BURST pointers to dp_packet.  If successful, the 
  628.      * implementation stores pointers to up to NETDEV_MAX_BURST dp_packets into 
  629.      * the array, transferring ownership of the packets to the caller, stores 
  630.      * the number of received packets into 'count', and returns 0. 
  631.      * 
  632.      * The implementation does not necessarily initialize any non-data members 
  633.      * of 'packets' in 'batch'.  That is, the caller must initialize layer 
  634.      * pointers and metadata itself, if desired, e.g. with pkt_metadata_init() 
  635.      * and miniflow_extract(). 
  636.      * 
  637.      * Implementations should allocate buffers with DP_NETDEV_HEADROOM bytes of 
  638.      * headroom. 
  639.      * 
  640.      * Returns EAGAIN immediately if no packet is ready to be received or 
  641.      * another positive errno value if an error was encountered. */  
  642.     int (*rxq_recv)(struct netdev_rxq *rx, struct dp_packet_batch *batch);  
  643.   
  644.     /* Registers with the poll loop to wake up from the next call to 
  645.      * poll_block() when a packet is ready to be received with 
  646.      * netdev_rxq_recv() on 'rx'. */  
  647.     void (*rxq_wait)(struct netdev_rxq *rx);  
  648.   
  649.     /* Discards all packets waiting to be received from 'rx'. */  
  650.     int (*rxq_drain)(struct netdev_rxq *rx);  
  651. };  
目前已经实现的netdev_class包括,netdev_linux_class, netdev_internal_class, netdev_tap_class, dpdk_class, dpdk_ring_class, dpdk_vhost_class, dpdk_vhost_client_class, patch_clas等。lib/netdev-linux.c里面实现的netdev通过调用内核api实现了基于内核的网络设备netdev,lib/netdev-vport.c则基于datapath模块实现了基于vport的网络设备netdev。

ofproto层通过ofproto_class定义了openflow的接口,除此之外,还有几个重要的数据结构和ofproto相关,struct ofproto, struct ofport, struct rule, struct oftable, struct ofgroup

1. struct ofproto代表了一个openflow switch结构体,内部包含了struct ofproto_class, struct ofport的hash map,struct oftable, struct ofgroup的hash map etc.

[cpp] view plain copy
  1. /* An OpenFlow switch. 
  2.  * 
  3.  * With few exceptions, ofproto implementations may look at these fields but 
  4.  * should not modify them. */  
  5. struct ofproto {  
  6.     struct hmap_node hmap_node; /* In global 'all_ofprotos' hmap. */  
  7.     const struct ofproto_class *ofproto_class;  
  8.     char *type;                 /* Datapath type. */  
  9.     char *name;                 /* Datapath name. */  
  10.   
  11.     /* Settings. */  
  12.     uint64_t fallback_dpid;     /* Datapath ID if no better choice found. */  
  13.     uint64_t datapath_id;       /* Datapath ID. */  
  14.     bool forward_bpdu;          /* Option to allow forwarding of BPDU frames 
  15.                                  * when NORMAL action is invoked. */  
  16.     char *mfr_desc;             /* Manufacturer (NULL for default). */  
  17.     char *hw_desc;              /* Hardware (NULL for default). */  
  18.     char *sw_desc;              /* Software version (NULL for default). */  
  19.     char *serial_desc;          /* Serial number (NULL for default). */  
  20.     char *dp_desc;              /* Datapath description (NULL for default). */  
  21.     enum ofputil_frag_handling frag_handling;  
  22.   
  23.     /* Datapath. */  
  24.     struct hmap ports;          /* Contains "struct ofport"s. */  
  25.     struct shash port_by_name;  
  26.     struct simap ofp_requests;  /* OpenFlow port number requests. */  
  27.     uint16_t alloc_port_no;     /* Last allocated OpenFlow port number. */  
  28.     uint16_t max_ports;         /* Max possible OpenFlow port num, plus one. */  
  29.     struct hmap ofport_usage;   /* Map ofport to last used time. */  
  30.     uint64_t change_seq;        /* Change sequence for netdev status. */  
  31.   
  32.     /* Flow tables. */  
  33.     long long int eviction_group_timer; /* For rate limited reheapification. */  
  34.     struct oftable *tables;  
  35.     int n_tables;  
  36.     ovs_version_t tables_version;  /* Controls which rules are visible to 
  37.                                     * table lookups. */  
  38.   
  39.     /* Rules indexed on their cookie values, in all flow tables. */  
  40.     struct hindex cookies OVS_GUARDED_BY(ofproto_mutex);  
  41.     struct hmap learned_cookies OVS_GUARDED_BY(ofproto_mutex);  
  42.   
  43.     /* List of expirable flows, in all flow tables. */  
  44.     struct ovs_list expirable OVS_GUARDED_BY(ofproto_mutex);  
  45.   
  46.     /* Meter table. 
  47.      * OpenFlow meters start at 1.  To avoid confusion we leave the first 
  48.      * pointer in the array un-used, and index directly with the OpenFlow 
  49.      * meter_id. */  
  50.     struct ofputil_meter_features meter_features;  
  51.     struct meter **meters; /* 'meter_features.max_meter' + 1 pointers. */  
  52.   
  53.     /* OpenFlow connections. */  
  54.     struct connmgr *connmgr;  
  55.   
  56.     /* Delayed rule executions. 
  57.      * 
  58.      * We delay calls to ->ofproto_class->rule_execute() past releasing 
  59.      * ofproto_mutex during a flow_mod, because otherwise a "learn" action 
  60.      * triggered by the executing the packet would try to recursively modify 
  61.      * the flow table and reacquire the global lock. */  
  62.     struct guarded_list rule_executes; /* Contains "struct rule_execute"s. */  
  63.   
  64.     int min_mtu;                    /* Current MTU of non-internal ports. */  
  65.   
  66.     /* Groups. */  
  67.     struct cmap groups;               /* Contains "struct ofgroup"s. */  
  68.     uint32_t n_groups[4] OVS_GUARDED; /* # of existing groups of each type. */  
  69.     struct ofputil_group_features ogf;  
  70. };  

2. struct ofport代表了openflow switch的一个端口,同时关联一个struct netdev的设备抽象

[cpp] view plain copy
  1. /* An OpenFlow port within a "struct ofproto". 
  2.  * 
  3.  * The port's name is netdev_get_name(port->netdev). 
  4.  * 
  5.  * With few exceptions, ofproto implementations may look at these fields but 
  6.  * should not modify them. */  
  7. struct ofport {  
  8.     struct hmap_node hmap_node; /* In struct ofproto's "ports" hmap. */  
  9.     struct ofproto *ofproto;    /* The ofproto that contains this port. */  
  10.     struct netdev *netdev;  
  11.     struct ofputil_phy_port pp;  
  12.     ofp_port_t ofp_port;        /* OpenFlow port number. */  
  13.     uint64_t change_seq;  
  14.     long long int created;      /* Time created, in msec. */  
  15.     int mtu;  
  16. };  

3. struct rule表示一条openflow规则,rule里面包含了一组struct rule_actions

[cpp] view plain copy
  1. struct rule {  
  2.     /* Where this rule resides in an OpenFlow switch. 
  3.      * 
  4.      * These are immutable once the rule is constructed, hence 'const'. */  
  5.     struct ofproto *const ofproto; /* The ofproto that contains this rule. */  
  6.     const struct cls_rule cr;      /* In owning ofproto's classifier. */  
  7.     const uint8_t table_id;        /* Index in ofproto's 'tables' array. */  
  8.     bool removed;                  /* Rule has been removed from the ofproto 
  9.                                     * data structures. */  
  10.   
  11.     /* Protects members marked OVS_GUARDED. 
  12.      * Readers only need to hold this mutex. 
  13.      * Writers must hold both this mutex AND ofproto_mutex. 
  14.      * By implication writers can read *without* taking this mutex while they 
  15.      * hold ofproto_mutex. */  
  16.     struct ovs_mutex mutex OVS_ACQ_AFTER(ofproto_mutex);  
  17.   
  18.     /* Number of references. 
  19.      * The classifier owns one reference. 
  20.      * Any thread trying to keep a rule from being freed should hold its own 
  21.      * reference. */  
  22.     struct ovs_refcount ref_count;  
  23.   
  24.     /* A "flow cookie" is the OpenFlow name for a 64-bit value associated with 
  25.      * a flow.. */  
  26.     ovs_be64 flow_cookie OVS_GUARDED;  
  27.     struct hindex_node cookie_node OVS_GUARDED_BY(ofproto_mutex);  
  28.   
  29.     enum ofputil_flow_mod_flags flags OVS_GUARDED;  
  30.   
  31.     /* Timeouts. */  
  32.     uint16_t hard_timeout OVS_GUARDED; /* In seconds from ->modified. */  
  33.     uint16_t idle_timeout OVS_GUARDED; /* In seconds from ->used. */  
  34.   
  35.     /* Eviction precedence. */  
  36.     const uint16_t importance;  
  37.   
  38.     /* Removal reason for sending flow removed message. 
  39.      * Used only if 'flags' has OFPUTIL_FF_SEND_FLOW_REM set and if the 
  40.      * value is not OVS_OFPRR_NONE. */  
  41.     uint8_t removed_reason;  
  42.   
  43.     /* Eviction groups (see comment on struct eviction_group for explanation) . 
  44.      * 
  45.      * 'eviction_group' is this rule's eviction group, or NULL if it is not in 
  46.      * any eviction group.  When 'eviction_group' is nonnull, 'evg_node' is in 
  47.      * the ->eviction_group->rules hmap. */  
  48.     struct eviction_group *eviction_group OVS_GUARDED_BY(ofproto_mutex);  
  49.     struct heap_node evg_node OVS_GUARDED_BY(ofproto_mutex);  
  50.   
  51.     /* OpenFlow actions.  See struct rule_actions for more thread-safety 
  52.      * notes. */  
  53.     const struct rule_actions * const actions;  
  54.   
  55.     /* In owning meter's 'rules' list.  An empty list if there is no meter. */  
  56.     struct ovs_list meter_list_node OVS_GUARDED_BY(ofproto_mutex);  
  57.   
  58.     /* Flow monitors (e.g. for NXST_FLOW_MONITOR, related to struct ofmonitor). 
  59.      * 
  60.      * 'add_seqno' is the sequence number when this rule was created. 
  61.      * 'modify_seqno' is the sequence number when this rule was last modified. 
  62.      * See 'monitor_seqno' in connmgr.c for more information. */  
  63.     enum nx_flow_monitor_flags monitor_flags OVS_GUARDED_BY(ofproto_mutex);  
  64.     uint64_t add_seqno OVS_GUARDED_BY(ofproto_mutex);  
  65.     uint64_t modify_seqno OVS_GUARDED_BY(ofproto_mutex);  
  66.   
  67.     /* Optimisation for flow expiry.  In ofproto's 'expirable' list if this 
  68.      * rule is expirable, otherwise empty. */  
  69.     struct ovs_list expirable OVS_GUARDED_BY(ofproto_mutex);  
  70.   
  71.     /* Times.  Last so that they are more likely close to the stats managed 
  72.      * by the provider. */  
  73.     long long int created OVS_GUARDED; /* Creation time. */  
  74.   
  75.     /* Must hold 'mutex' for both read/write, 'ofproto_mutex' not needed. */  
  76.     long long int modified OVS_GUARDED; /* Time of last modification. */  
  77. };  
  78.   
  79. struct rule_actions {  
  80.     /* Flags. 
  81.      * 
  82.      * 'has_meter' is true if 'ofpacts' contains an OFPACT_METER action. 
  83.      * 
  84.      * 'has_learn_with_delete' is true if 'ofpacts' contains an OFPACT_LEARN 
  85.      * action whose flags include NX_LEARN_F_DELETE_LEARNED. */  
  86.     bool has_meter;  
  87.     bool has_learn_with_delete;  
  88.     bool has_groups;  
  89.   
  90.     /* Actions. */  
  91.     uint32_t ofpacts_len;         /* Size of 'ofpacts', in bytes. */  
  92.     struct ofpact ofpacts[];      /* Sequence of "struct ofpacts". */  
  93. };  
  94.   
  95. struct ofpact {  
  96.     /* We want the space advantage of an 8-bit type here on every 
  97.      * implementation, without giving up the advantage of having a useful type 
  98.      * on implementations that support packed enums. */  
  99. #ifdef HAVE_PACKED_ENUM  
  100.     enum ofpact_type type;      /* OFPACT_*. */  
  101. #else  
  102.     uint8_t type;               /* OFPACT_* */  
  103. #endif  
  104.   
  105.     uint8_t raw;                /* Original type when added, if any. */  
  106.     uint16_t len;               /* Length of the action, in bytes, including 
  107.                                  * struct ofpact, excluding padding. */  
  108. };  
struct ofproto_class是一个接口工厂类,对应的实现是ofproto-dpif,我们先来看接口定义
[cpp] view plain copy
  1. struct ofproto_class {  
  2. /* ## ----------------- ## */  
  3. /* ## Factory Functions ## */  
  4. /* ## ----------------- ## */  
  5.   
  6.     /* Initializes provider.  The caller may pass in 'iface_hints', 
  7.      * which contains an shash of "struct iface_hint" elements indexed 
  8.      * by the interface's name.  The provider may use these hints to 
  9.      * describe the startup configuration in order to reinitialize its 
  10.      * state.  The caller owns the provided data, so a provider must 
  11.      * make copies of anything required.  An ofproto provider must 
  12.      * remove any existing state that is not described by the hint, and 
  13.      * may choose to remove it all. */  
  14.     void (*init)(const struct shash *iface_hints);  
  15.   
  16.     /* Enumerates the types of all supported ofproto types into 'types'.  The 
  17.      * caller has already initialized 'types'.  The implementation should add 
  18.      * its own types to 'types' but not remove any existing ones, because other 
  19.      * ofproto classes might already have added names to it. */  
  20.     void (*enumerate_types)(struct sset *types);  
  21.   
  22.     /* Enumerates the names of all existing datapath of the specified 'type' 
  23.      * into 'names' 'all_dps'.  The caller has already initialized 'names' as 
  24.      * an empty sset. 
  25.      * 
  26.      * 'type' is one of the types enumerated by ->enumerate_types(). 
  27.      * 
  28.      * Returns 0 if successful, otherwise a positive errno value. 
  29.      */  
  30.     int (*enumerate_names)(const char *type, struct sset *names);  
  31.   
  32.     /* Deletes the datapath with the specified 'type' and 'name'.  The caller 
  33.      * should have closed any open ofproto with this 'type' and 'name'; this 
  34.      * function is allowed to fail if that is not the case. 
  35.      * 
  36.      * 'type' is one of the types enumerated by ->enumerate_types(). 
  37.      * 'name' is one of the names enumerated by ->enumerate_names() for 'type'. 
  38.      * 
  39.      * Returns 0 if successful, otherwise a positive errno value. 
  40.      */  
  41.     int (*del)(const char *type, const char *name);  
  42.   
  43.     /* Returns the type to pass to netdev_open() when a datapath of type 
  44.      * 'datapath_type' has a port of type 'port_type', for a few special 
  45.      * cases when a netdev type differs from a port type.  For example, 
  46.      * when using the userspace datapath, a port of type "internal" 
  47.      * needs to be opened as "tap". 
  48.      * 
  49.      * Returns either 'type' itself or a string literal, which must not 
  50.      * be freed. */  
  51.     const char *(*port_open_type)(const char *datapath_type,  
  52.                                   const char *port_type);  
  53.   
  54.     /* Performs any periodic activity required on ofprotos of type 
  55.      * 'type'. 
  56.      * 
  57.      * An ofproto provider may implement it or not, depending on whether 
  58.      * it needs type-level maintenance. 
  59.      * 
  60.      * Returns 0 if successful, otherwise a positive errno value. */  
  61.     int (*type_run)(const char *type);  
  62.   
  63.     /* Causes the poll loop to wake up when a type 'type''s 'run' 
  64.      * function needs to be called, e.g. by calling the timer or fd 
  65.      * waiting functions in poll-loop.h. 
  66.      * 
  67.      * An ofproto provider may implement it or not, depending on whether 
  68.      * it needs type-level maintenance. */  
  69.     void (*type_wait)(const char *type);  
  70.   
  71.     /* Performs any periodic activity required by 'ofproto'.  It should: 
  72.      * 
  73.      *   - Call connmgr_send_packet_in() for each received packet that missed 
  74.      *     in the OpenFlow flow table or that had a OFPP_CONTROLLER output 
  75.      *     action. 
  76.      * 
  77.      *   - Call ofproto_rule_expire() for each OpenFlow flow that has reached 
  78.      *     its hard_timeout or idle_timeout, to expire the flow. 
  79.      * 
  80.      * Returns 0 if successful, otherwise a positive errno value. */  
  81.     int (*run)(struct ofproto *ofproto);  
  82.   
  83.     /* Causes the poll loop to wake up when 'ofproto''s 'run' function needs to 
  84.      * be called, e.g. by calling the timer or fd waiting functions in 
  85.      * poll-loop.h.  */  
  86.     void (*wait)(struct ofproto *ofproto);  
  87.   
  88.     /* Every "struct rule" in 'ofproto' is about to be deleted, one by one. 
  89.      * This function may prepare for that, for example by clearing state in 
  90.      * advance.  It should *not* actually delete any "struct rule"s from 
  91.      * 'ofproto', only prepare for it. 
  92.      * 
  93.      * This function is optional; it's really just for optimization in case 
  94.      * it's cheaper to delete all the flows from your hardware in a single pass 
  95.      * than to do it one by one. */  
  96.     void (*flush)(struct ofproto *ofproto);  
  97.   
  98.     /* Helper for the OpenFlow OFPT_TABLE_FEATURES request. 
  99.      * 
  100.      * The 'features' array contains 'ofproto->n_tables' elements.  Each 
  101.      * element is initialized as: 
  102.      * 
  103.      *   - 'table_id' to the array index. 
  104.      * 
  105.      *   - 'name' to "table#" where # is the table ID. 
  106.      * 
  107.      *   - 'metadata_match' and 'metadata_write' to OVS_BE64_MAX. 
  108.      * 
  109.      *   - 'config' to the table miss configuration. 
  110.      * 
  111.      *   - 'max_entries' to 1,000,000. 
  112.      * 
  113.      *   - Both 'nonmiss' and 'miss' to: 
  114.      * 
  115.      *     * 'next' to all 1-bits for all later tables. 
  116.      * 
  117.      *     * 'instructions' to all instructions. 
  118.      * 
  119.      *     * 'write' and 'apply' both to: 
  120.      * 
  121.      *       - 'ofpacts': All actions. 
  122.      * 
  123.      *       - 'set_fields': All fields. 
  124.      * 
  125.      *   - 'match', 'mask', and 'wildcard' to all fields. 
  126.      * 
  127.      * If 'stats' is nonnull, it also contains 'ofproto->n_tables' elements. 
  128.      * Each element is initialized as: 
  129.      * 
  130.      *   - 'table_id' to the array index. 
  131.      * 
  132.      *   - 'active_count' to the 'n_flows' of struct ofproto for the table. 
  133.      * 
  134.      *   - 'lookup_count' and 'matched_count' to 0. 
  135.      * 
  136.      * The implementation should update any members in each element for which 
  137.      * it has better values: 
  138.      * 
  139.      *   - Any member of 'features' to better describe the implementation's 
  140.      *     capabilities. 
  141.      * 
  142.      *   - 'lookup_count' to the number of packets looked up in this flow table 
  143.      *     so far. 
  144.      * 
  145.      *   - 'matched_count' to the number of packets looked up in this flow 
  146.      *     table so far that matched one of the flow entries. 
  147.      */  
  148.     void (*query_tables)(struct ofproto *ofproto,  
  149.                          struct ofputil_table_features *features,  
  150.                          struct ofputil_table_stats *stats);  
  151.   
  152.     /* Sets the current tables version the provider should use for classifier 
  153.      * lookups.  This must be called with a new version number after each set 
  154.      * of flow table changes has been completed, so that datapath revalidation 
  155.      * can be triggered. */  
  156.     void (*set_tables_version)(struct ofproto *ofproto, ovs_version_t version);  
  157.   
  158.     struct ofproto *(*alloc)(void);  
  159.     int (*construct)(struct ofproto *ofproto);  
  160.     void (*destruct)(struct ofproto *ofproto);  
  161.     void (*dealloc)(struct ofproto *ofproto);  
  162.   
  163.     struct ofport *(*port_alloc)(void);  
  164.     int (*port_construct)(struct ofport *ofport);  
  165.     void (*port_destruct)(struct ofport *ofport, bool del);  
  166.     void (*port_dealloc)(struct ofport *ofport);  
  167.   
  168.     /* Called after 'ofport->netdev' is replaced by a new netdev object.  If 
  169.      * the ofproto implementation uses the ofport's netdev internally, then it 
  170.      * should switch to using the new one.  The old one has been closed. 
  171.      * 
  172.      * An ofproto implementation that doesn't need to do anything in this 
  173.      * function may use a null pointer. */  
  174.     void (*port_modified)(struct ofport *ofport);  
  175.   
  176.     /* Called after an OpenFlow request changes a port's configuration. 
  177.      * 'ofport->pp.config' contains the new configuration.  'old_config' 
  178.      * contains the previous configuration. 
  179.      * 
  180.      * The caller implements OFPUTIL_PC_PORT_DOWN using netdev functions to 
  181.      * turn NETDEV_UP on and off, so this function doesn't have to do anything 
  182.      * for that bit (and it won't be called if that is the only bit that 
  183.      * changes). */  
  184.     void (*port_reconfigured)(struct ofport *ofport,  
  185.                               enum ofputil_port_config old_config);  
  186.   
  187.     /* Looks up a port named 'devname' in 'ofproto'.  On success, returns 0 and 
  188.      * initializes '*port' appropriately. Otherwise, returns a positive errno 
  189.      * value. 
  190.      * 
  191.      * The caller owns the data in 'port' and must free it with 
  192.      * ofproto_port_destroy() when it is no longer needed. */  
  193.     int (*port_query_by_name)(const struct ofproto *ofproto,  
  194.                               const char *devname, struct ofproto_port *port);  
  195.   
  196.     /* Attempts to add 'netdev' as a port on 'ofproto'.  Returns 0 if 
  197.      * successful, otherwise a positive errno value.  The caller should 
  198.      * inform the implementation of the OpenFlow port through the 
  199.      * ->port_construct() method. 
  200.      * 
  201.      * It doesn't matter whether the new port will be returned by a later call 
  202.      * to ->port_poll(); the implementation may do whatever is more 
  203.      * convenient. */  
  204.     int (*port_add)(struct ofproto *ofproto, struct netdev *netdev);  
  205.   
  206.     /* Deletes port number 'ofp_port' from the datapath for 'ofproto'.  Returns 
  207.      * 0 if successful, otherwise a positive errno value. 
  208.      * 
  209.      * It doesn't matter whether the new port will be returned by a later call 
  210.      * to ->port_poll(); the implementation may do whatever is more 
  211.      * convenient. */  
  212.     int (*port_del)(struct ofproto *ofproto, ofp_port_t ofp_port);  
  213.   
  214.     /* Refreshes datapath configuration of 'port'. 
  215.      * Returns 0 if successful, otherwise a positive errno value. */  
  216.     int (*port_set_config)(const struct ofport *port, const struct smap *cfg);  
  217.   
  218.     /* Get port stats */  
  219.     int (*port_get_stats)(const struct ofport *port,  
  220.                           struct netdev_stats *stats);  
  221.   
  222.     /* Port iteration functions. 
  223.      * 
  224.      * The client might not be entirely in control of the ports within an 
  225.      * ofproto.  Some hardware implementations, for example, might have a fixed 
  226.      * set of ports in a datapath.  For this reason, the client needs a way to 
  227.      * iterate through all the ports that are actually in a datapath.  These 
  228.      * functions provide that functionality. 
  229.      * 
  230.      * The 'state' pointer provides the implementation a place to 
  231.      * keep track of its position.  Its format is opaque to the caller. 
  232.      * 
  233.      * The ofproto provider retains ownership of the data that it stores into 
  234.      * ->port_dump_next()'s 'port' argument.  The data must remain valid until 
  235.      * at least the next call to ->port_dump_next() or ->port_dump_done() for 
  236.      * 'state'.  The caller will not modify or free it. 
  237.      * 
  238.      * Details 
  239.      * ======= 
  240.      * 
  241.      * ->port_dump_start() attempts to begin dumping the ports in 'ofproto'. 
  242.      * On success, it should return 0 and initialize '*statep' with any data 
  243.      * needed for iteration.  On failure, returns a positive errno value, and 
  244.      * the client will not call ->port_dump_next() or ->port_dump_done(). 
  245.      * 
  246.      * ->port_dump_next() attempts to retrieve another port from 'ofproto' for 
  247.      * 'state'.  If there is another port, it should store the port's 
  248.      * information into 'port' and return 0.  It should return EOF if all ports 
  249.      * have already been iterated.  Otherwise, on error, it should return a 
  250.      * positive errno value.  This function will not be called again once it 
  251.      * returns nonzero once for a given iteration (but the 'port_dump_done' 
  252.      * function will be called afterward). 
  253.      * 
  254.      * ->port_dump_done() allows the implementation to release resources used 
  255.      * for iteration.  The caller might decide to stop iteration in the middle 
  256.      * by calling this function before ->port_dump_next() returns nonzero. 
  257.      * 
  258.      * Usage Example 
  259.      * ============= 
  260.      * 
  261.      * int error; 
  262.      * void *state; 
  263.      * 
  264.      * error = ofproto->ofproto_class->port_dump_start(ofproto, &state); 
  265.      * if (!error) { 
  266.      *     for (;;) { 
  267.      *         struct ofproto_port port; 
  268.      * 
  269.      *         error = ofproto->ofproto_class->port_dump_next( 
  270.      *                     ofproto, state, &port); 
  271.      *         if (error) { 
  272.      *             break; 
  273.      *         } 
  274.      *         // Do something with 'port' here (without modifying or freeing 
  275.      *         // any of its data). 
  276.      *     } 
  277.      *     ofproto->ofproto_class->port_dump_done(ofproto, state); 
  278.      * } 
  279.      * // 'error' is now EOF (success) or a positive errno value (failure). 
  280.      */  
  281.     int (*port_dump_start)(const struct ofproto *ofproto, void **statep);  
  282.     int (*port_dump_next)(const struct ofproto *ofproto, void *state,  
  283.                           struct ofproto_port *port);  
  284.     int (*port_dump_done)(const struct ofproto *ofproto, void *state);  
  285.   
  286.     struct rule *(*rule_alloc)(void);  
  287.     enum ofperr (*rule_construct)(struct rule *rule)  
  288.         /* OVS_REQUIRES(ofproto_mutex) */;  
  289.     void (*rule_insert)(struct rule *rule, struct rule *old_rule,  
  290.                         bool forward_counts)  
  291.         /* OVS_REQUIRES(ofproto_mutex) */;  
  292.     void (*rule_delete)(struct rule *rule) /* OVS_REQUIRES(ofproto_mutex) */;  
  293.     void (*rule_destruct)(struct rule *rule);  
  294.     void (*rule_dealloc)(struct rule *rule);  
  295.   
  296.     /* Applies the actions in 'rule' to 'packet'.  (This implements sending 
  297.      * buffered packets for OpenFlow OFPT_FLOW_MOD commands.) 
  298.      * 
  299.      * Takes ownership of 'packet' (so it should eventually free it, with 
  300.      * ofpbuf_delete()). 
  301.      * 
  302.      * 'flow' reflects the flow information for 'packet'.  All of the 
  303.      * information in 'flow' is extracted from 'packet', except for 
  304.      * flow->tunnel and flow->in_port, which are assigned the correct values 
  305.      * for the incoming packet.  The register values are zeroed.  'packet''s 
  306.      * header pointers and offsets (e.g. packet->l3) are appropriately 
  307.      * initialized.  packet->l3 is aligned on a 32-bit boundary. 
  308.      * 
  309.      * The implementation should add the statistics for 'packet' into 'rule'. 
  310.      * 
  311.      * Returns 0 if successful, otherwise an OpenFlow error code. */  
  312.     enum ofperr (*rule_execute)(struct rule *rule, const struct flow *flow,  
  313.                                 struct dp_packet *packet);  
  314.   
  315.     /* Implements the OpenFlow OFPT_PACKET_OUT command.  The datapath should 
  316.      * execute the 'ofpacts_len' bytes of "struct ofpacts" in 'ofpacts'. 
  317.      * 
  318.      * The caller retains ownership of 'packet' and of 'ofpacts', so 
  319.      * ->packet_out() should not modify or free them. 
  320.      * 
  321.      * This function must validate that it can correctly implement 'ofpacts'. 
  322.      * If not, then it should return an OpenFlow error code. 
  323.      * 
  324.      * 'flow' reflects the flow information for 'packet'.  All of the 
  325.      * information in 'flow' is extracted from 'packet', except for 
  326.      * flow->in_port (see below).  flow->tunnel and its register values are 
  327.      * zeroed. 
  328.      * 
  329.      * flow->in_port comes from the OpenFlow OFPT_PACKET_OUT message.  The 
  330.      * implementation should reject invalid flow->in_port values by returning 
  331.      * OFPERR_OFPBRC_BAD_PORT.  (If the implementation called 
  332.      * ofproto_init_max_ports(), then the client will reject these ports 
  333.      * itself.)  For consistency, the implementation should consider valid for 
  334.      * flow->in_port any value that could possibly be seen in a packet that it 
  335.      * passes to connmgr_send_packet_in().  Ideally, even an implementation 
  336.      * that never generates packet-ins (e.g. due to hardware limitations) 
  337.      * should still allow flow->in_port values for every possible physical port 
  338.      * and OFPP_LOCAL.  The only virtual ports (those above OFPP_MAX) that the 
  339.      * caller will ever pass in as flow->in_port, other than OFPP_LOCAL, are 
  340.      * OFPP_NONE and OFPP_CONTROLLER.  The implementation should allow both of 
  341.      * these, treating each of them as packets generated by the controller as 
  342.      * opposed to packets originating from some switch port. 
  343.      * 
  344.      * (Ordinarily the only effect of flow->in_port is on output actions that 
  345.      * involve the input port, such as actions that output to OFPP_IN_PORT, 
  346.      * OFPP_FLOOD, or OFPP_ALL.  flow->in_port can also affect Nicira extension 
  347.      * "resubmit" actions.) 
  348.      * 
  349.      * 'packet' is not matched against the OpenFlow flow table, so its 
  350.      * statistics should not be included in OpenFlow flow statistics. 
  351.      * 
  352.      * Returns 0 if successful, otherwise an OpenFlow error code. */  
  353.     enum ofperr (*packet_out)(struct ofproto *ofproto, struct dp_packet *packet,  
  354.                               const struct flow *flow,  
  355.                               const struct ofpact *ofpacts,  
  356.                               size_t ofpacts_len);  
  357.   
  358.     enum ofperr (*nxt_resume)(struct ofproto *ofproto,  
  359.                               const struct ofputil_packet_in_private *);  
  360.   
  361.     /* Registers meta-data associated with the 'n_qdscp' Qualities of Service 
  362.      * 'queues' attached to 'ofport'.  This data is not intended to be 
  363.      * sufficient to implement QoS.  Instead, providers may use this 
  364.      * information to implement features which require knowledge of what queues 
  365.      * exist on a port, and some basic information about them. 
  366.      * 
  367.      * EOPNOTSUPP as a return value indicates that this ofproto_class does not 
  368.      * support QoS, as does a null pointer. */  
  369.     int (*set_queues)(struct ofport *ofport,  
  370.                       const struct ofproto_port_queue *queues, size_t n_qdscp);  
  371.   
  372.     /* If 's' is nonnull, this function registers a "bundle" associated with 
  373.      * client data pointer 'aux' in 'ofproto'.  A bundle is the same concept as 
  374.      * a Port in OVSDB, that is, it consists of one or more "slave" devices 
  375.      * (Interfaces, in OVSDB) along with VLAN and LACP configuration and, if 
  376.      * there is more than one slave, a bonding configuration.  If 'aux' is 
  377.      * already registered then this function updates its configuration to 's'. 
  378.      * Otherwise, this function registers a new bundle. 
  379.      * 
  380.      * If 's' is NULL, this function unregisters the bundle registered on 
  381.      * 'ofproto' associated with client data pointer 'aux'.  If no such bundle 
  382.      * has been registered, this has no effect. 
  383.      * 
  384.      * This function affects only the behavior of the NXAST_AUTOPATH action and 
  385.      * output to the OFPP_NORMAL port.  An implementation that does not support 
  386.      * it at all may set it to NULL or return EOPNOTSUPP.  An implementation 
  387.      * that supports only a subset of the functionality should implement what 
  388.      * it can and return 0. */  
  389.     int (*bundle_set)(struct ofproto *ofproto, void *aux,  
  390.                       const struct ofproto_bundle_settings *s);  
  391.   
  392.     /* If 'port' is part of any bundle, removes it from that bundle.  If the 
  393.      * bundle now has no ports, deletes the bundle.  If the bundle now has only 
  394.      * one port, deconfigures the bundle's bonding configuration. */  
  395.     void (*bundle_remove)(struct ofport *ofport);  
  396.   
  397.     /* These functions should be NULL if an implementation does not support 
  398.      * them.  They must be all null or all non-null.. */  
  399.   
  400.     /* Initializes 'features' to describe the metering features supported by 
  401.      * 'ofproto'. */  
  402.     void (*meter_get_features)(const struct ofproto *ofproto,  
  403.                                struct ofputil_meter_features *features);  
  404.   
  405.     /* If '*id' is UINT32_MAX, adds a new meter with the given 'config'.  On 
  406.      * success the function must store a provider meter ID other than 
  407.      * UINT32_MAX in '*id'.  All further references to the meter will be made 
  408.      * with the returned provider meter id rather than the OpenFlow meter id. 
  409.      * The caller does not try to interpret the provider meter id, giving the 
  410.      * implementation the freedom to either use the OpenFlow meter_id value 
  411.      * provided in the meter configuration, or any other value suitable for the 
  412.      * implementation. 
  413.      * 
  414.      * If '*id' is a value other than UINT32_MAX, modifies the existing meter 
  415.      * with that meter provider ID to have configuration 'config', while 
  416.      * leaving '*id' unchanged.  On failure, the existing meter configuration 
  417.      * is left intact. */  
  418.     enum ofperr (*meter_set)(struct ofproto *ofproto, ofproto_meter_id *id,  
  419.                              const struct ofputil_meter_config *config);  
  420.   
  421.     /* Gets the meter and meter band packet and byte counts for maximum of 
  422.      * 'stats->n_bands' bands for the meter with provider ID 'id' within 
  423.      * 'ofproto'.  The caller fills in the other stats values.  The band stats 
  424.      * are copied to memory at 'stats->bands' provided by the caller.  The 
  425.      * number of returned band stats is returned in 'stats->n_bands'. */  
  426.     enum ofperr (*meter_get)(const struct ofproto *ofproto,  
  427.                              ofproto_meter_id id,  
  428.                              struct ofputil_meter_stats *stats);  
  429.   
  430.     /* Deletes a meter, making the 'ofproto_meter_id' invalid for any 
  431.      * further calls. */  
  432.     void (*meter_del)(struct ofproto *, ofproto_meter_id);  
  433.   
  434. /* ## --------------------- ## */  
  435. /* ## Datapath information  ## */  
  436. /* ## --------------------- ## */  
  437.     /* Retrieve the version string of the datapath. The version 
  438.      * string can be NULL if it can not be determined. 
  439.      * 
  440.      * The version retuned is read only. The caller should not 
  441.      * free it. 
  442.      * 
  443.      * This function should be NULL if an implementation does not support it. 
  444.      */  
  445.     const char *(*get_datapath_version)(const struct ofproto *);  
  446.   
  447. /* ## ------------------- ## */  
  448. /* ## Connection tracking ## */  
  449. /* ## ------------------- ## */  
  450.     /* Flushes the connection tracking tables. If 'zone' is not NULL, 
  451.      * only deletes connections in '*zone'. */  
  452.     void (*ct_flush)(const struct ofproto *, const uint16_t *zone);  
  453. };  
下面来看看ofproto_class的具体实现ofproto-dpif,ofproto-dpif用于datapath未命中的报文,通过查找openflow流表计算出具体的action,并下发到datapath中,同时ofproto-dpif还会进一步把报文上送给ofproto,最终交给集中式控制器来处理。
[cpp] view plain copy
  1. /* Ofproto-dpif -- DPIF based ofproto implementation. 
  2.  * 
  3.  * Ofproto-dpif provides an ofproto implementation for those platforms which 
  4.  * implement the netdev and dpif interface defined in netdev.h and dpif.h.  The 
  5.  * most important of which is the Linux Kernel Module (dpif-linux), but 
  6.  * alternatives are supported such as a userspace only implementation 
  7.  * (dpif-netdev), and a dummy implementation used for unit testing. 
  8.  * 
  9.  * Ofproto-dpif is divided into three major chunks. 
  10.  * 
  11.  * - ofproto-dpif.c 
  12.  *   The main ofproto-dpif module is responsible for implementing the 
  13.  *   provider interface, installing and removing datapath flows, maintaining 
  14.  *   packet statistics, running protocols (BFD, LACP, STP, etc), and 
  15.  *   configuring relevant submodules. 
  16.  * 
  17.  * - ofproto-dpif-upcall.c 
  18.  *   Ofproto-dpif-upcall is responsible for retrieving upcalls from the kernel, 
  19.  *   processing miss upcalls, and handing more complex ones up to the main 
  20.  *   ofproto-dpif module.  Miss upcall processing boils down to figuring out 
  21.  *   what each packet's actions are, executing them (i.e. asking the kernel to 
  22.  *   forward it), and handing it up to ofproto-dpif to decided whether or not 
  23.  *   to install a kernel flow. 
  24.  * 
  25.  * - ofproto-dpif-xlate.c 
  26.  *   Ofproto-dpif-xlate is responsible for translating OpenFlow actions into 
  27.  *   datapath actions. */  
ofproto-dpif的结构图如下
               |   +-------------------+
               |   |    ovs-vswitchd   |<-->ovsdb-server
               |   +-------------------+
               |   |      ofproto      |<-->OpenFlow controllers
               |   +--------+-+--------+  _
               |   | netdev | |ofproto-|   |
     userspace |   +--------+ |  dpif  |   |
               |   | netdev | +--------+   |
               |   |provider| |  dpif  |   |
               |   +---||---+ +--------+   |
               |       ||     |  dpif  |   | implementation of
               |       ||     |provider|   | ofproto provider
               |_      ||     +---||---+   |
                       ||         ||       |
                _  +---||-----+---||---+   |
               |   |          |datapath|   |
        kernel |   |          +--------+  _|
               |   |                   |
               |_  +--------||---------+
                            ||
                         physical
                           NIC
struct dpif_class是datapath interface实现的工厂接口类,用于和实际的datapath, e.g. openvswitch.ko, 或者userspace datapath交互。目前已有的两个dpif的实现是dpif-netlink和dpif-netdev,前者是基于内核datapath的dpif实现,后者基于用户态datapath。代码可以在lib/dpif-netlink.c以及lib/dpif-netdev.c里找到。
struct dpif_class的接口定义在lib/dpif-provider.h中,仅供参考





你可能感兴趣的:(openVSwitch)