Openvswith 源码阅读只learn action

July 28, 2017 | 3 Minute Read

learn action 是根据packet模式学习,再创建和添加新的flow到flow table的一个action

“learn action” 和”Resubmit action” 还有 “metadata registers” 、”stack操作” 是openvswitch 可编程flow table机制得以实现的关键吧。 有了这几样才能建立复杂的 “flow pipeline”

learn action的执行

./lib/learn.c:
./ofproto/ofproto-dpif-xlate.c:


upcall_xlate
  xlate_actions
    do_xlate_actions
      case OFPACT_LEARN:
        xlate_learn_action(ctx, ofpact_get_LEARN(a));
          learn_execute  // 这个函数会执行ovs的learn action的那些动作,设置寄存器等,应该在这里准备好了要新建的flow
          ofproto_flow_mod_learn  // 这个好像会把新建的流加到table里面去。

/* Composes 'fm' so that executing it will implement 'learn' given that the
 * packet being processed has 'flow' as its flow.
 *
 * Uses 'ofpacts' to store the flow mod's actions.  The caller must initialize
 * 'ofpacts' and retains ownership of it.  'fm->ofpacts' will point into the
 * 'ofpacts' buffer.
 *
 * The caller has to actually execute 'fm'. */
void
learn_execute(const struct ofpact_learn *learn, const struct flow *flow,
              struct ofputil_flow_mod *fm, struct ofpbuf *ofpacts)
    mf_read_subfield
    ofpact_put_reg_load
    ofpact_put_OUTPUT


learn action执行的时候的信息,应该是parse之后就解析出来的一些属性都保存在这里了吧
/* OFPACT_LEARN.
 *
 * Used for NXAST_LEARN. */
struct ofpact_learn {
    OFPACT_PADDED_MEMBERS(
        struct ofpact ofpact;

        uint16_t idle_timeout;     /* Idle time before discarding (seconds). */
        uint16_t hard_timeout;     /* Max time before discarding (seconds). */
        uint16_t priority;         /* Priority level of flow entry. */
        uint8_t table_id;          /* Table to insert flow entry. */
        enum nx_learn_flags flags; /* NX_LEARN_F_*. */
        ovs_be64 cookie;           /* Cookie for new flow. */
        uint16_t fin_idle_timeout; /* Idle timeout after FIN, if nonzero. */
        uint16_t fin_hard_timeout; /* Hard timeout after FIN, if nonzero. */
        /* If the number of flows on 'table_id' with 'cookie' exceeds this,
         * the action will not add a new flow. 0 indicates unlimited. */
        uint32_t limit;
        /* Used only if 'flags' has NX_LEARN_F_WRITE_RESULT.  If the execution
         * failed to install a new flow because 'limit' is exceeded,
         * result_dst will be set to 0, otherwise to 1. */
        struct mf_subfield result_dst;
    );

    struct ofpact_learn_spec specs[];
};



这个ofproto_flow_mod结构保存了要新加的flow是什么样子的flow吧,应该是复用了
mod-flows 这个action的结构了,文档也是这个写的
learn(argument[,argument]...)
       This action adds or modifies a flow in an OpenFlow table,
       similar  to  ovs-ofctl --strict mod-flows.

/* flow_mod with execution context. */
struct ofproto_flow_mod {
    /* Allocated by 'init' phase, may be freed after 'start' phase, as these
     * are not needed for 'revert' nor 'finish'. */
    struct rule *temp_rule;
    struct rule_criteria criteria;
    struct cls_conjunction *conjs;
    size_t n_conjs;

    /* Replicate needed fields from ofputil_flow_mod to not need it after the
     * flow has been created. */
    uint16_t command;
    bool modify_cookie;
    /* Fields derived from ofputil_flow_mod. */
    bool modify_may_add_flow;
    bool modify_keep_counts;
    enum nx_flow_update_event event;

    /* These are only used during commit execution.
     * ofproto_flow_mod_uninit() does NOT clean these up. */
    ovs_version_t version;              /* Version in which changes take
                                         * effect. */
    bool learn_adds_rule;               /* Learn execution adds a rule. */
    struct rule_collection old_rules;   /* Affected rules. */
    struct rule_collection new_rules;   /* Replacement rules. */
};



/* Executes the flow modification specified in 'fm'.  Returns 0 on success, or
 * an OFPERR_* OpenFlow error code on failure.
 *
 * This is a helper function for in-band control and fail-open. */
enum ofperr
ofproto_flow_mod(struct ofproto *ofproto, const struct ofputil_flow_mod *fm)
    OVS_EXCLUDED(ofproto_mutex)
{
    return handle_flow_mod__(ofproto, fm, NULL);
}

learn action的解析

ofpacts_parse
  ofpact_parse
    parse_LEARN
      learn_parse

ofpact_parse 用X macro额写法,解析所有的action的都在 ofp-actions.c
文件里面parse_XXX函数里面做的。


static char * OVS_WARN_UNUSED_RESULT
ofpact_parse(enum ofpact_type type, char *value,
             const struct ofputil_port_map *port_map, struct ofpbuf *ofpacts,
             enum ofputil_protocol *usable_protocols)
{
    switch (type) {
#define OFPACT(ENUM, STRUCT, MEMBER, NAME)                              \
        case OFPACT_##ENUM:                                             \
            return parse_##ENUM(value, port_map, ofpacts, usable_protocols);
        OFPACTS
#undef OFPACT
    default:
        OVS_NOT_REACHED();
    }
}



/* Returns NULL if successful, otherwise a malloc()'d string describing the
 * error.  The caller is responsible for freeing the returned string. */
static char * OVS_WARN_UNUSED_RESULT
learn_parse__(char *orig, char *arg, const struct ofputil_port_map *port_map,
              struct ofpbuf *ofpacts)




这部分代码看上去是解析openflow controller的返回结果,
看来controler里面也是可以返回这个learn action的。

ovs 内置的的mac learning


xlate_normal(struct xlate_ctx *ctx)
  /* Learn source MAC. */
  update_learning_table
    update_learning_table__
      mac_learning_update

   mac = mac_learning_lookup(ctx->xbridge->ml, flow->dl_dst, vlan);   // mac表的学习 vlan + mac
   mac_port = mac ? mac_entry_get_port(ctx->xbridge->ml, mac) : NULL;
   if (mac_port) {
     output_normal(ctx, mac_xbundle, &xvlan);
   } else {
     xlate_normal_flood(ctx, in_xbundle, &xvlan);
   }


xlate_mac_learning_update
  update_learning_table__
     mac_learning_update

最近没时间细看了,其实还不太清楚这个learn action。 从上面的代码知道learn_execute函数应该是 learn action的执行过程没错,这个是在用户空间执行的, 跟通常的直接在内核执行的aciton不一样,这种learn 每个网络包都要发到用户空间来学习一下,context switch会比较多影响性能? 不过这个learn添加flow的 动作确实在用户空间来做比较好。现在对learn action是怎么编译成内核上方用户空间那个动作还不是很清楚,其实是还没怎么搞懂 openvswitch代码里面 xlate 和 ofproto的代码,看上去ovs会把用户配置的流规则转成内部的二进制描述结构和openflow的标准消息的结构之类的吧。以后有时间再看一下。