open vswitch研究:openflow I

關於openflow的規範不是本文討論範疇,這篇主要討論OVS對openflow的支持,代碼基本都在ofproto/目錄下

struct ofproto代表了一個openflow switch的模型,是一個"接口類"

struct ofproto {
    struct hmap_node hmap_node; /* In global 'all_ofprotos' hmap. */    該ofproto對應的全局ofproto的hash map的結點
    const struct ofproto_class *ofproto_class;   bridge模式的實現,真正的操作在ofproto_class裏完成
    char *type;                 /* Datapath type. */
    char *name;                 /* Datapath name. */

    /* Settings. */
    uint64_t fallback_dpid;     /* Datapath ID if no better choice found. */
    uint64_t datapath_id;       /* Datapath ID. */
    unsigned flow_eviction_threshold; /* Threshold at which to begin flow
                                       * table eviction. Only affects the
                                       * ofproto-dpif implementation */  開始回收流表項的閥值
    bool forward_bpdu;          /* Option to allow forwarding of BPDU frames
                                 * when NORMAL action is invoked. */
    char *mfr_desc;             /* Manufacturer. */
    char *hw_desc;              /* Hardware. */
    char *sw_desc;              /* Software version. */
    char *serial_desc;          /* Serial number. */
    char *dp_desc;              /* Datapath description. */
    enum ofp_config_flags frag_handling; /* One of OFPC_*.  */

    /* Datapath. */
    struct hmap ports;          /* Contains "struct ofport"s. */
    struct shash port_by_name;

    /* Flow tables. */
    struct oftable *tables;    /* openflow switch flow table */
    int n_tables;

    /* OpenFlow connections. */
    struct connmgr *connmgr;

    /* Flow table operation tracking. */
    int state;                  /* Internal state. */
    struct list pending;        /* List of "struct ofopgroup"s. */
    unsigned int n_pending;     /* list_size(&pending). */
    struct hmap deletions;      /* All OFOPERATION_DELETE "ofoperation"s. */

    /* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.)
     *
     * This is deprecated.  It is only for compatibility with broken device
     * drivers in old versions of Linux that do not properly support VLANs when
     * VLAN devices are not used.  When broken device drivers are no longer in
     * widespread use, we will delete these interfaces. */
    unsigned long int *vlan_bitmap; /* 4096-bit bitmap of in-use VLANs. */
    bool vlans_changed;             /* True if new VLANs are in use. */
    int min_mtu;                    /* Current MTU of non-internal ports. */
};


struct ofport代表了一個openflow switch的port,ofport必定屬於一個ofproto

struct ofport {
    struct hmap_node hmap_node; /* In struct ofproto's "ports" hmap. */     ofport在ofproto的hash map中的位置
    struct ofproto *ofproto;    /* The ofproto that contains this port. */       ofport屬於的ofproto
    struct netdev *netdev;                   ofport對應的netdev設備
    struct ofputil_phy_port pp;
    uint16_t ofp_port;          /* OpenFlow port number. */
    unsigned int change_seq;
    int mtu;
};


/* A flow table within a "struct ofproto". */
struct oftable {
    enum oftable_flags flags;
    struct classifier cls;      /* Contains "struct rule"s. */
    char *name;                 /* Table name exposed via OpenFlow, or NULL. */

    /* Maximum number of flows or UINT_MAX if there is no limit besides any
     * limit imposed by resource limitations. */
    unsigned int max_flows;

    struct mf_subfield *eviction_fields;   這項如果爲空,那麼一旦oftable出現overflow,會直接drop新的flow,否則會選一個老的flow老化掉給新flow
    size_t n_eviction_fields;   

    uint32_t eviction_group_id_basis;
    struct hmap eviction_groups_by_id;
    struct heap eviction_groups_by_size;
};


/* An OpenFlow flow within a "struct ofproto".
 *
 * With few exceptions, ofproto implementations may look at these fields but
 * should not modify them. */
struct rule {
    struct list ofproto_node;    /* Owned by ofproto base code. */        
    struct ofproto *ofproto;     /* The ofproto that contains this rule. */   rule屬於的ofproto
    struct cls_rule cr;          /* In owning ofproto's classifier. */          ofproto->tables是一個oftable, table->cls是struct classifier,classifier會包含cls_table,裏面包含cls_rule

    struct ofoperation *pending; /* Operation now in progress, if nonnull. */

    ovs_be64 flow_cookie;        /* Controller-issued identifier. */

    long long int created;       /* Creation time. */
    long long int modified;      /* Time of last modification. */
    long long int used;          /* Last use; time created if never used. */
    uint16_t hard_timeout;       /* In seconds from ->modified. */
    uint16_t idle_timeout;       /* In seconds from ->used. */
    uint8_t table_id;            /* Index in ofproto's 'tables' array. */
    bool send_flow_removed;      /* Send a flow removed message? */

    /* Eviction groups. */
    bool evictable;              /* If false, prevents eviction. */
    struct heap_node evg_node;   /* In eviction_group's "rules" heap. */
    struct eviction_group *eviction_group; /* NULL if not in any group. */

    union ofp_action *actions;   /* OpenFlow actions. */                    actions是一個由n_actions個union ofp_action組成的數組,最終會被轉成nlattr的格式給netlink發送出去
    int n_actions;               /* Number of elements in actions[]. */
};

我的理解是,rule代表了flow match的共性,如果一條flow被rule match了,那麼就會執行rule->actions。


struct ofproto_class是一個bridge模式的implementation,目前struct ofproto的一個實現是struct ofproto_dpif,其struct ofproto_class的實例爲ofproto_dpif_class。struct ofproto代表了一個openflow switch,struct ofport代表了ofproto的port,struct rule代表了ofproto裏的flow。所有這些都是接口類,其真正的實現類是struct ofproto_dpif, struct ofport_dpif, struct rule_dpif。

同樣的struct dpif_class也是一個接口類,其實現有dpif_netdev_class, dpif_linux_class


struct ofproto_class {

    /* Enumerates the types of all support ofproto types into 'types'.  The
     * caller has already initialized 'types' and other ofproto classes might
     * already have added names to it. */
    void (*enumerate_types)(struct sset *types);

該函數列出ofproto_class的實現類,OVS全局有static const struct dpif_class *base_dpif_classes[] = {&dpif_linux_class, &dpif_netdev_class},以及一個全局的struct shash結構static struct shash dpif_classes,dp_initialize函數會把base_dpif_classes數組裏的dpif_class結構通過調用dp_register_provider註冊到全局的shash結構dpif_classes中。其中dpif_linux_class的type是system,而dpif_netdev_class的type是netdev

全局struct shash的key是type,value是struct registered_dpif_class*,該結構包含了一個const struct dpif_class*,以及這個struct dpif_class的一個refcount

最後回到enumerate_types,該函數就是把全局dpif_class的types取出來存到一個sset裏面


    int (*del)(const char *type, const char *name);      

該函數刪除名字爲name,類型爲type的datapath。datapath在用戶態的類型結構定義在/lib/dpif-provider.h中,dpif也包含了一個struct dpif_class的接口。

該函數首先調用dpif_open獲取datapath對應的struct dpif結構,該函數實際調用的是dpif_class->open,然後調用dpif_delete刪除struct dpif對應的datapath,該操作調用了dpif_class->destroy。

struct dpif {
    const struct dpif_class *dpif_class;
    char *base_name;                                                                                  
    char *full_name;                                                                                  
    uint8_t netflow_engine_type;                                                                      
    uint8_t netflow_engine_id;                                                                        
};

這個struct dpif是一個抽象的接口類

struct dpif_linux {
    struct dpif dpif;
    int dp_ifindex;    應該是/sys/classs/net/xxxx/ifindex的值,每個設備都會對應一個
    
    /* Upcall messages. */
    struct nl_sock *upcall_socks[N_UPCALL_SOCKS];
    uint32_t ready_mask;        /* 1-bit for each sock with unread messages. */
    int epoll_fd;               /* epoll fd that includes the upcall socks. */

    /* Change notification. */
    struct sset changed_ports;  /* Ports that have changed. */
    struct nln_notifier *port_notifier;
    bool change_error;
        
    /* Port number allocation. */
    uint16_t alloc_port_no;
};  

struct dpif_linux是具體的實例結構,其中包含了struct dpif


    int (*construct)(struct ofproto *ofproto);

    void (*destruct)(struct ofproto *ofproto);

首先要提及struct ofproto_dpif這個結構,由於struct ofproto只是一個接口類,因此其實現類爲struct ofproto_dpif,定義如下

struct ofproto_dpif {                                                                                 
    struct hmap_node all_ofproto_dpifs_node; /* In 'all_ofproto_dpifs'. */          全局的ofproto_dpif會形成一個hmap,爲all_ofproto_dpifs                            
    struct ofproto up;                 對應的ofproto結構
    struct dpif *dpif;                   對應的dpif,其實際上是dpif_linux/dpif_netdev                            
    int max_ports;                     ofproto_dpif最多可以有多少個port                                                             

    /* Special OpenFlow rules. */                                                                     
    struct rule_dpif *miss_rule; /* Sends flow table misses to controller. */                         
    struct rule_dpif *no_packet_in_rule; /* Drops flow table misses. */

    /* Statistics. */                                                                                 
    uint64_t n_matches;

    /* Bridging. */
    struct netflow *netflow;                                                                          
    struct dpif_sflow *sflow;                                                                         
    struct hmap bundles;        /* Contains "struct ofbundle"s. */                                    
    struct mac_learning *ml;          mac/port對應關係的cam表
    struct ofmirror *mirrors[MAX_MIRRORS];
    bool has_mirrors;
    bool has_bonded_bundles;                                                                          
    
    /* Expiration. */                                                                                 
    struct timer next_expiration;    

    /* Facets. */
    struct hmap facets;                    struct facet代表了match的flow,所有這些facet組織在facets的hmap中                                                             
    struct hmap subfacets;
    struct governor *governor;

    /* Revalidation. */
    struct table_dpif tables[N_TABLES];      
    bool need_revalidate;                                                                             
    struct tag_set revalidate_set;

    /* Support for debugging async flow mods. */                                                      
    struct list completions;

    bool has_bundle_action; /* True when the first bundle action appears. */
    struct netdev_stats stats; /* To account packets generated and consumed in
                                * userspace. */

    /* Spanning tree. */
    struct stp *stp;
    long long int stp_last_tick;

    /* VLAN splinters. */
    struct hmap realdev_vid_map; /* (realdev,vid) -> vlandev. */
    struct hmap vlandev_map;     /* vlandev -> (realdev,vid). */
};

construct函數用來初始化struct ofproto中一系列的成員,然後hmap_insert到all_ofproto_dpifs的全局hmap中。destruct則是相反的操作


    int (*run)(struct ofproto *ofproto);

    int (*run_fast)(struct ofproto *ofproto);   

該函數會包含多個xxx_run的子函數,e.g.

dpif_run,對於dpif_linux_class而言即dpif_linux_run,代碼上看似乎是做一些netlink notifier的callback

run_fast,反覆調用handle_upcalls,最多FLOW_MISS_MAX_BATCH,e.g. 處理flow miss的upcall

對於ofproto下的所有ofport,調用port_run

對於cam表,調用mac_learning_run,該函數通過lru查找已經time expire的mac_entry,並把這些entry老化


    int (*port_construct)(struct ofport *ofport);  

    void (*port_destruct)(struct ofport *ofport);    

struct ofport的接口類的實現是struct ofport_dpif

struct ofport_dpif {
    struct ofport up;
                                                                                                      
    uint32_t odp_port;    我猜ofp_port是openflow視圖下的port,odp_port應該是datapath視圖下的port
    struct ofbundle *bundle;    /* Bundle that contains this port, if any. */    bundle是用來提供類似lacp的bonding,一個bonding會有多個ofport
    struct list bundle_node;    /* In struct ofbundle's "ports" list. */                              
    struct cfm *cfm;            /* Connectivity Fault Management, if any. */                          
    tag_type tag;               /* Tag associated with this port. */
    uint32_t bond_stable_id;    /* stable_id to use as bond slave, or 0. */                           
    bool may_enable;            /* May be enabled in bonds. */                                        
    long long int carrier_seq;  /* Carrier status changes. */                                         
    
    /* Spanning tree. */
    struct stp_port *stp_port;  /* Spanning Tree Protocol, if any. */                                 
    enum stp_state stp_state;   /* Always STP_DISABLED if STP not in use. */                          
    long long int stp_state_entered;

    struct hmap priorities;     /* Map of attached 'priority_to_dscp's. */                            

    /* Linux VLAN device support (e.g. "eth0.10" for VLAN 10.)                                        
     *                        
     * This is deprecated.  It is only for compatibility with broken device                           
     * drivers in old versions of Linux that do not properly support VLANs when                       
     * VLAN devices are not used.  When broken device drivers are no longer in                        
     * widespread use, we will delete these interfaces. */                                            
    uint16_t realdev_ofp_port;
    int vlandev_vid;
};  


    void (*port_modified)(struct ofport *ofport);     

    void (*port_reconfigured)(struct ofport *ofport,  enum ofputil_port_config old_config);     

port_reconfigured函數,每次port的配置ofputil_port_config改變,需要使得ofproto->need_revalidate = true。因爲ofport的改變會造成ofproto原有的flow table的相關表項失效

OVS定義的port的配置和狀態碼如下,

enum ofputil_port_config {
    /* OpenFlow 1.0 and 1.1 share these values for these port config bits. */
    OFPUTIL_PC_PORT_DOWN    = 1 << 0, /* Port is administratively down. */
    OFPUTIL_PC_NO_RECV      = 1 << 2, /* Drop all packets received by port. */
    OFPUTIL_PC_NO_FWD       = 1 << 5, /* Drop packets forwarded to port. */
    OFPUTIL_PC_NO_PACKET_IN = 1 << 6, /* No send packet-in msgs for port. */
    /* OpenFlow 1.0 only. */
    OFPUTIL_PC_NO_STP       = 1 << 1, /* No 802.1D spanning tree for port. */
    OFPUTIL_PC_NO_RECV_STP  = 1 << 3, /* Drop received 802.1D STP packets. */
    OFPUTIL_PC_NO_FLOOD     = 1 << 4, /* Do not include port when flooding. */
    /* There are no OpenFlow 1.1-only bits. */
};  

enum ofputil_port_state {
    /* OpenFlow 1.0 and 1.1 share this values for these port state bits. */
    OFPUTIL_PS_LINK_DOWN   = 1 << 0, /* No physical link present. */
    /* OpenFlow 1.1 only. */
    OFPUTIL_PS_BLOCKED     = 1 << 1, /* Port is blocked */
    OFPUTIL_PS_LIVE        = 1 << 2, /* Live for Fast Failover Group. */
    /* OpenFlow 1.0 only. */
    OFPUTIL_PS_STP_LISTEN  = 0 << 8, /* Not learning or relaying frames. */
    OFPUTIL_PS_STP_LEARN   = 1 << 8, /* Learning but not relaying frames. */
    OFPUTIL_PS_STP_FORWARD = 2 << 8, /* Learning and relaying frames. */
    OFPUTIL_PS_STP_BLOCK   = 3 << 8, /* Not part of spanning tree. */
    OFPUTIL_PS_STP_MASK    = 3 << 8  /* Bit mask for OFPPS10_STP_* values. */
};


    int (*port_add)(struct ofproto *ofproto, struct netdev *netdev, uint16_t *ofp_portp);       

    int (*port_del)(struct ofproto *ofproto, uint16_t ofp_port);        

port_add,構造一個struct dpif_linux_vport的request,通過dpif_linux_vport_transact發送netlink消息給ofproto,添加一個port。port_del和port_add原理一樣。


    int (*port_dump_start)(const struct ofproto *ofproto, void **statep);       

    int (*port_dump_next)(const struct ofproto *ofproto, void *state, struct ofproto_port *port); 

    int (*port_dump_done)(const struct ofproto *ofproto, void *state);    

這三個函數用來遍歷ofproto裏的所有port


    int (*port_poll)(const struct ofproto *ofproto, char **devnamep);    

    void (*port_poll_wait)(const struct ofproto *ofproto);   

port_port用來查詢ofproto中已經變化了的port,實際上是查詢dpif->changed_ports


    enum ofperr (*rule_construct)(struct rule *rule);

    void (*rule_destruct)(struct rule *rule);            

    /* Applies the actions in 'rule' to 'packet'.  (This implements sending
     * buffered packets for OpenFlow OFPT_FLOW_MOD commands.)                                         
     *                                                                                                
     * Takes ownership of 'packet' (so it should eventually free it, with                             
     * ofpbuf_delete()).                                                                              
     *                                                                                                
     * 'flow' reflects the flow information for 'packet'.  All of the                                 
     * information in 'flow' is extracted from 'packet', except for                                   
     * flow->tun_id and flow->in_port, which are assigned the correct values                          
     * for the incoming packet.  The register values are zeroed.  'packet''s                          
     * header pointers (e.g. packet->l3) are appropriately initialized.                               
     *                                                                                                
     * The implementation should add the statistics for 'packet' into 'rule'.                         
     *                                                                                                
     * Returns 0 if successful, otherwise an OpenFlow error code. */                                  
    enum ofperr (*rule_execute)(struct rule *rule, const struct flow *flow,                           
                                struct ofpbuf *packet);                                               
                                                                                                         

rule_execute給flow添加actions,這裏packet應該存的是skb的結構體,flow是從packet中extract出來的流


下面來看struct dpif,這是一個接口類,實現全在struct dpif_class裏,

struct dpif {
    const struct dpif_class *dpif_class;
    char *base_name;
    char *full_name;
    uint8_t netflow_engine_type;
    uint8_t netflow_engine_id;
};

struct dpif_class {
    /* Type of dpif in this class, e.g. "system", "netdev", etc.
     * 
     * One of the providers should supply a "system" type, since this is
     * the type assumed if no type is specified when opening a dpif. */
    const char *type;

    int (*enumerate)(struct sset *all_dps);

   /* Attempts to open an existing dpif called 'name', if 'create' is false,
     * or to open an existing dpif or create a new one, if 'create' is true.
     *
     * 'dpif_class' is the class of dpif to open.
     *
     * If successful, stores a pointer to the new dpif in '*dpifp', which must
     * have class 'dpif_class'.  On failure there are no requirements on what
     * is stored in '*dpifp'. */
    int (*open)(const struct dpif_class *dpif_class,
                const char *name, bool create, struct dpif **dpifp);

    /* Closes 'dpif' and frees associated memory. */
    void (*close)(struct dpif *dpif);

    /* Attempts to destroy the dpif underlying 'dpif'.
     *
     * If successful, 'dpif' will not be used again except as an argument for
     * the 'close' member function. */
    int (*destroy)(struct dpif *dpif);

    /* Performs periodic work needed by 'dpif', if any is necessary. */
    void (*run)(struct dpif *dpif);

    /* Arranges for poll_block() to wake up if the "run" member function needs
     * to be called for 'dpif'. */
    void (*wait)(struct dpif *dpif);

   /* Adds 'netdev' as a new port in 'dpif'.  If successful, sets '*port_no'
     * to the new port's port number. */
    int (*port_add)(struct dpif *dpif, struct netdev *netdev,
                    uint16_t *port_no);

    /* Removes port numbered 'port_no' from 'dpif'. */
    int (*port_del)(struct dpif *dpif, uint16_t port_no);

    int (*flow_get)(const struct dpif *dpif,
                    const struct nlattr *key, size_t key_len,
                    struct ofpbuf **actionsp, struct dpif_flow_stats *stats);

    int (*flow_put)(struct dpif *dpif, const struct dpif_flow_put *put);

    int (*flow_del)(struct dpif *dpif, const struct dpif_flow_del *del);

    /* Performs the 'execute->actions_len' bytes of actions in
     * 'execute->actions' on the Ethernet frame specified in 'execute->packet'
     * taken from the flow specified in the 'execute->key_len' bytes of
     * 'execute->key'.  ('execute->key' is mostly redundant with
     * 'execute->packet', but it contains some metadata that cannot be
     * recovered from 'execute->packet', such as tun_id and in_port.) */
    int (*execute)(struct dpif *dpif, const struct dpif_execute *execute);

};

全局的struct dpif_class數組包含了兩個dpif_class實例,dpif_linux_class以及dpif_netdev_class

tatic const struct dpif_class *base_dpif_classes[] = {
#ifdef HAVE_NETLINK
    &dpif_linux_class,
#endif
    &dpif_netdev_class,
};  

dp_initialize會在初始化的時候,調用dp_register_provider來註冊base_dpif_classes,同時全局還有shash dpif_classe

dpif_open, dpif_create都會調用dp_open來打開/創建一個struct dp_if,一般都是調用dpif_linux_class->open,dpif_close則是關閉datapath,通過一個registered_dpif_class->refcount來計算type類型的datapath的引用計數

dpif_port_add, dpif_port_del,通過dpif_linux_class->port_add, dpif_linux_class->port_del執行port的增刪

dpif_flow_flush,刪除datapath流表的所有flow

dpif_flow_get,通過flow key在datapath裏查詢flow,如果flow同時有action,填充到struct ofpbuf** actionsp裏

dpif_flow_put, dpif_flow_put__,修改flow的action

dpif_flow_del, dpif_flow_del__,刪除flow key爲key的流


struct dpif_class的一個實例是struct dpif_class dpif_linux_class,

const struct dpif_class dpif_linux_class = {
    "system",
    dpif_linux_enumerate,
    dpif_linux_open, 
    dpif_linux_close,
    dpif_linux_destroy,
    dpif_linux_run,
    dpif_linux_wait,
    dpif_linux_get_stats,
    dpif_linux_port_add,
    dpif_linux_port_del,
    dpif_linux_port_query_by_number,
    dpif_linux_port_query_by_name,
    dpif_linux_get_max_ports,
    dpif_linux_port_get_pid,
    dpif_linux_port_dump_start,
    dpif_linux_port_dump_next,
    dpif_linux_port_dump_done,
    dpif_linux_port_poll,
    dpif_linux_port_poll_wait,
    dpif_linux_flow_get,                  
    dpif_linux_flow_put,
    dpif_linux_flow_del,
    dpif_linux_flow_flush,
    dpif_linux_flow_dump_start,
    dpif_linux_flow_dump_next,
    dpif_linux_flow_dump_done,
    dpif_linux_execute,
    dpif_linux_operate,
    dpif_linux_recv_set,
    dpif_linux_queue_to_priority,
    dpif_linux_recv,
    dpif_linux_recv_wait,
    dpif_linux_recv_purge,   
};

這些操作大多數都需要通過netlink和內核通信來完成,因此定義了一些消息格式如下,

struct dpif_linux_dp {                                                                                
    /* Generic Netlink header. */                                                                     
    uint8_t cmd;                                                                                      
                                                                                                      
    /* struct ovs_header. */                                                                          
    int dp_ifindex;                                                                                   
                                                                                                      
    /* Attributes. */                                                                                 
    const char *name;                  /* OVS_DP_ATTR_NAME. */                                        
    const uint32_t *upcall_pid;        /* OVS_DP_UPCALL_PID. */                                       
    struct ovs_dp_stats stats;         /* OVS_DP_ATTR_STATS. */                                       
};                         

struct dpif_linux_flow {                                                                              
    /* Generic Netlink header. */                                                                     
    uint8_t cmd;                                                                                      
                                                                                                      
    /* struct ovs_header. */                                                                          
    unsigned int nlmsg_flags;                                                                         
    int dp_ifindex;                                                                                   
                                                                                                      
    /* Attributes.                                                                                    
     *                                                                                                
     * The 'stats' member points to 64-bit data that might only be aligned on                         
     * 32-bit boundaries, so get_unaligned_u64() should be used to access its                         
     * values.                                                                                        
     *                                                                                                
     * If 'actions' is nonnull then OVS_FLOW_ATTR_ACTIONS will be included in                         
     * the Netlink version of the command, even if actions_len is zero. */                            
    const struct nlattr *key;           /* OVS_FLOW_ATTR_KEY. */                                      
    size_t key_len;                                                                                   
    const struct nlattr *actions;       /* OVS_FLOW_ATTR_ACTIONS. */                                  
    size_t actions_len;                                                                               
    const struct ovs_flow_stats *stats; /* OVS_FLOW_ATTR_STATS. */                                    
    const uint8_t *tcp_flags;           /* OVS_FLOW_ATTR_TCP_FLAGS. */                                
    const ovs_32aligned_u64 *used;      /* OVS_FLOW_ATTR_USED. */                                     
    bool clear;                         /* OVS_FLOW_ATTR_CLEAR. */                                    
};                                         

和內核進行netlink通信的函數是dpif_linux_dp_transaction,該函數傳遞一個struct dpif_linux_dp做爲request,reply,這是一個同步的transaction


在linux還有個結構struct dpif_linux,用來把struct dpif封裝一層

/* Datapath interface for the openvswitch Linux kernel module. */                                     
struct dpif_linux {                                                                                   
    struct dpif dpif;                                                                                 
    int dp_ifindex;                                                                                   
                                                                                                      
    /* Upcall messages. */                                                                            
    struct nl_sock *upcall_socks[N_UPCALL_SOCKS];                                                     
    uint32_t ready_mask;        /* 1-bit for each sock with unread messages. */                       
    int epoll_fd;               /* epoll fd that includes the upcall socks. */                        
                                                                                                      
    /* Change notification. */                                                                        
    struct sset changed_ports;  /* Ports that have changed. */                                        
    struct nln_notifier *port_notifier;                                                               
    bool change_error;                                                                                
                                                                                                      
    /* Port number allocation. */                                                                     
    uint16_t alloc_port_no;                                                                           
};                                 


dpif_linux_open,首先通過dpif_linux_dp_transaction發送OVS_DP_CMD_NEW或OVS_DP_CMD_GET命令,並獲得回覆。之後調用open_dpif,基於reply構造一個struct dpif

dpif_linux_close,close和內核通信的netlink socket,銷燬之前創建的netlink notifier,表示對port的變化不再關心

dpif_linux_destroy,調用dpif_linux_dp_transact,向內核發送OVS_DP_CMD_DEL命令,銷燬datapath

static int                                                                                            
dpif_linux_port_add(struct dpif *dpif_, struct netdev *netdev,                                        
                    uint16_t *port_nop)                                                               把一個struct netdev加到datapath的port裏
{                                                                                                     
    struct dpif_linux *dpif = dpif_linux_cast(dpif_);                                                 
    const char *name = netdev_get_name(netdev);                                                  
    const char *type = netdev_get_type(netdev);                   ovs_vport_type裏是所有vport的類型                                   
    struct dpif_linux_vport request, reply;                                                           
    const struct ofpbuf *options;                                                                     
    struct ofpbuf *buf;                                                                               
    int error, i = 0, max_ports = MAX_PORTS;                                                          
                                                                                                      
    dpif_linux_vport_init(&request);                                                                  
    request.cmd = OVS_VPORT_CMD_NEW;                                                                  
    request.dp_ifindex = dpif->dp_ifindex;                     dpif->dp_ifindex是/sys/class/net裏datapath設備的ifindex號                                      
    request.type = netdev_vport_get_vport_type(netdev);                                               
    if (request.type == OVS_VPORT_TYPE_UNSPEC) {                                                      
        VLOG_WARN_RL(&error_rl, "%s: cannot create port `%s' because it has "                         
                     "unsupported type `%s'",                                                         
                     dpif_name(dpif_), name, type);                                                   
        return EINVAL;                                                                                
    }                                                                                                 
    request.name = name;                                                                              
                                                                                                      
    options = netdev_vport_get_options(netdev);                                                       
    if (options && options->size) {                                                                   
        request.options = options->data;                                                              
        request.options_len = options->size;                                                          
    }                                                                                                 
                                                                                                      
    if (request.type == OVS_VPORT_TYPE_NETDEV) {                                                      
        netdev_linux_ethtool_set_flag(netdev, ETH_FLAG_LRO, "LRO", false);         禁用LRO                   
    }                                                                                                 
                                                                                                      

    /* Loop until we find a port that isn't used. */                                                  
    do {                                                                                              
        uint32_t upcall_pid;                                                                          
                                                                                                      
        request.port_no = ++dpif->alloc_port_no;                                                 alloc_port_no是已經分配的port no
        upcall_pid = dpif_linux_port_get_pid(dpif_, request.port_no);                                 
        request.upcall_pid = &upcall_pid;
        error = dpif_linux_vport_transact(&request, &reply, &buf);

        if (!error) {
            *port_nop = reply.port_no;
            VLOG_DBG("%s: assigning port %"PRIu32" to netlink pid %"PRIu32,
                     dpif_name(dpif_), request.port_no, upcall_pid);
        } else if (error == EFBIG) {
            /* Older datapath has lower limit. */
            max_ports = dpif->alloc_port_no;
            dpif->alloc_port_no = 0;
        }

        ofpbuf_delete(buf);
    } while ((i++ < max_ports)
             && (error == EBUSY || error == EFBIG));

    return error;     
}

dpif_linux_port_del,這裏又涉及了一個消息結構體struct dpif_linux_vport

struct dpif_linux_vport {
    /* Generic Netlink header. */
    uint8_t cmd;
        
    /* ovs_vport header. */
    int dp_ifindex;
    uint32_t port_no;                      /* UINT32_MAX if unknown. */
    enum ovs_vport_type type;

    /* Attributes.
     *
     * The 'stats' member points to 64-bit data that might only be aligned on
     * 32-bit boundaries, so use get_unaligned_u64() to access its values.
     */
    const char *name;                      /* OVS_VPORT_ATTR_NAME. */
    const uint32_t *upcall_pid;            /* OVS_VPORT_ATTR_UPCALL_PID. */
    const struct ovs_vport_stats *stats;   /* OVS_VPORT_ATTR_STATS. */
    const uint8_t *address;                /* OVS_VPORT_ATTR_ADDRESS. */
    const struct nlattr *options;          /* OVS_VPORT_ATTR_OPTIONS. */
    size_t options_len;
};  

dpif_linux_port_del配置vport.cmd = OVS_VPORT_CMD_DEL, vport.dp_ifindex = dpif->dp_ifindex vport.port_no = port_no,調用dpif_linux_vport_transact和內核通信


dpif_linux_flow_get,所有flow相關消息結構爲struct dpif_linux_flow, 調用的通信函數爲dpif_linux_flow_transaction,命令爲OVS_FLOW_CMD_GET

struct dpif_linux_flow {
    /* Generic Netlink header. */
    uint8_t cmd;

    /* struct ovs_header. */
    unsigned int nlmsg_flags;
    int dp_ifindex;

    /* Attributes.
     *
     * The 'stats' member points to 64-bit data that might only be aligned on
     * 32-bit boundaries, so get_unaligned_u64() should be used to access its
     * values.
     *
     * If 'actions' is nonnull then OVS_FLOW_ATTR_ACTIONS will be included in
     * the Netlink version of the command, even if actions_len is zero. */
    const struct nlattr *key;           /* OVS_FLOW_ATTR_KEY. */
    size_t key_len;
    const struct nlattr *actions;       /* OVS_FLOW_ATTR_ACTIONS. */
    size_t actions_len;
    const struct ovs_flow_stats *stats; /* OVS_FLOW_ATTR_STATS. */
    const uint8_t *tcp_flags;           /* OVS_FLOW_ATTR_TCP_FLAGS. */
    const ovs_32aligned_u64 *used;      /* OVS_FLOW_ATTR_USED. */
    bool clear;                         /* OVS_FLOW_ATTR_CLEAR. */
};

dpif_linux_flow_put,其命令爲OVS_FLOW_CMD_NEW or OVS_FLOW_CMD_SET,dpif_linux_flow_del,命令爲OVS_FLOW_CMD_DEL




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章