ovs vlan

作者: 分享放大价值 | 来源:发表于2021-02-11 00:04 被阅读0次

    默认情况下,每个端口为trunk_mode,并且trunk的vlan为空,报文处理可参考下面的trunk模式。

    ovs支持设置的vlan mode如下,可参考ovs-vswitchd.conf.db.5的VLAN Configuration部分。

    access模式:
        收:
            报文带vlan,drop
            报文不带vlan,接收,并携带端口的vlan进行后续转发    
        发:
            发出去的报文不带vlan
            
    trunk模式:
        收:
            报文带vlan,vlan必须在trunk vlan指定的范围或者trunk vlan为空(不用考虑报文携带的vlan)
            报文不带vlan,如果trunk vlan不为空,则drop。如果trunk vlan为空,则接收,此为默认情况下的处理。
        发:
            发出去的报文,带原始vlan
            
    native tagged模式:
        收:
            报文带vlan,vlan必须在trunk vlan指定的范围或者trunk vlan为空(不用考虑报文携带的vlan)
            报文不带vlan,接收,并携带端口的native vlan进行后续转发
        发:
            发出去的报文,如果报文本来带vlan,则携带原始vlan。如果报文不带vlan,则携带native vlan
            
    native untagged模式:
        收:
            报文带vlan,vlan必须在trunk vlan指定的范围或者trunk vlan为空(不用考虑报文携带的vlan)
            报文不带vlan,接收,并携带端口的native vlan进行后续转发
        发:
            发出去的报文,如果报文本来带vlan,则携带原始vlan。如果报文不带vlan,则报文也不带vlan
    

    配置vlan流程

    通过命令行配置端口的vlan模式和tag信息时,最终会调用port_configure,此函数会将从ovsdb-server接收的端口配置信息提取出来,再调用bundle_set将相关信息填充到struct ofbundle,并将struct ofbundle插入hash表ofproto->bundles。

    static void
    port_configure(struct port *port)
    {
        const struct ovsrec_port *cfg = port->cfg;
        struct ofproto_bundle_settings s;
        struct iface *iface;
    
        /* Get name. */
        s.name = port->name;
    
        /* Get slaves. */
        s.n_slaves = 0;
        s.slaves = xmalloc(ovs_list_size(&port->ifaces) * sizeof *s.slaves);
        LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
            s.slaves[s.n_slaves++] = iface->ofp_port;
        }
    
        /* Get VLAN tag. */
        s.vlan = -1;
        if (cfg->tag && *cfg->tag >= 0 && *cfg->tag <= 4095) {
            s.vlan = *cfg->tag;
        }
    
        /* Get VLAN trunks. */
        s.trunks = NULL;
        if (cfg->n_trunks) {
            s.trunks = vlan_bitmap_from_array(cfg->trunks, cfg->n_trunks);
        }
    
        s.cvlans = NULL;
        if (cfg->n_cvlans) {
            s.cvlans = vlan_bitmap_from_array(cfg->cvlans, cfg->n_cvlans);
        }
    
        /* Get VLAN mode. */
        if (cfg->vlan_mode) {
            if (!strcmp(cfg->vlan_mode, "access")) {
                s.vlan_mode = PORT_VLAN_ACCESS;
            } else if (!strcmp(cfg->vlan_mode, "trunk")) {
                s.vlan_mode = PORT_VLAN_TRUNK;
            } else if (!strcmp(cfg->vlan_mode, "native-tagged")) {
                s.vlan_mode = PORT_VLAN_NATIVE_TAGGED;
            } else if (!strcmp(cfg->vlan_mode, "native-untagged")) {
                s.vlan_mode = PORT_VLAN_NATIVE_UNTAGGED;
            } else if (!strcmp(cfg->vlan_mode, "dot1q-tunnel")) {
                s.vlan_mode = PORT_VLAN_DOT1Q_TUNNEL;
            } else {
                /* This "can't happen" because ovsdb-server should prevent it. */
                VLOG_WARN("port %s: unknown VLAN mode %s, falling "
                          "back to trunk mode", port->name, cfg->vlan_mode);
                s.vlan_mode = PORT_VLAN_TRUNK;
            }
        } else {
            if (s.vlan >= 0) {
                s.vlan_mode = PORT_VLAN_ACCESS;
                if (cfg->n_trunks || cfg->n_cvlans) {
                    VLOG_WARN("port %s: ignoring trunks in favor of implicit vlan",
                              port->name);
                }
            } else {
                s.vlan_mode = PORT_VLAN_TRUNK;
            }
        }
        ...
        ...
        /* Register. */
        ofproto_bundle_register(port->bridge->ofproto, port, &s);
    }
    

    最后在函数type_run中,将ofproto->bundles转换到struct xbundle中,并插入new_xcfg->xbundles。

    static int
    type_run(const char *type)
    {
        HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
            HMAP_FOR_EACH (bundle, hmap_node, &ofproto->bundles) {
                    xlate_bundle_set(ofproto, bundle, bundle->name,
                                     bundle->vlan_mode, bundle->qinq_ethtype,
                                     bundle->vlan, bundle->trunks, bundle->cvlans,
                                     bundle->use_priority_tags,
                                     bundle->bond, bundle->lacp,
                                     bundle->floodable, bundle->protected);
                }
        }
    }
    

    报文入方向vlan处理

    网桥端口接收到报文后,如果快速路径查找不到相关流表,就会开始慢速路径的处理,最终会根据fdb表转发,调用xlate_normal。此函数中调用input_vid_is_valid根据入端口vlan模式配置,判断报文的去留。

    //vid为报文携带的vlan id,如果报文不带vlan,则为0
    //in_xbundle为报文入端口,其中包括端口上vlan相关的设置
    static bool
    input_vid_is_valid(const struct xlate_ctx *ctx,
                       uint16_t vid, struct xbundle *in_xbundle)
    {
        /* Allow any VID on the OFPP_NONE port. */
        if (in_xbundle == &ofpp_none_bundle) {
            return true;
        }
    
        switch (in_xbundle->vlan_mode) {
         //access模式,如果报文带vlan,则drop
        case PORT_VLAN_ACCESS:
            if (vid) {
                xlate_report_error(ctx, "dropping VLAN %"PRIu16" tagged "
                                   "packet received on port %s configured as VLAN "
                                   "%d access port", vid, in_xbundle->name,
                                   in_xbundle->vlan);
                return false;
            }
            return true;
        //native tagged和native untagged模式下,如果报文不带vlan,则接收此报文。
        //如果报文携带vlan,则就要判断此vlan是否在trunk vlan中。
        //trunk vlan中会自动加入native vlan
        case PORT_VLAN_NATIVE_UNTAGGED:
        case PORT_VLAN_NATIVE_TAGGED:
            if (!vid) {
                /* Port must always carry its native VLAN. */
                return true;
            }
            /* Fall through. */
        //native tagged,native untagged和trunk模式都会走此流程,判断
        //报文携带vlan是否在trunk vlan中
        case PORT_VLAN_TRUNK:
            if (!xbundle_trunks_vlan(in_xbundle, vid)) {
                xlate_report_error(ctx, "dropping VLAN %"PRIu16" packet "
                                   "received on port %s not configured for "
                                   "trunking VLAN %"PRIu16,
                                   vid, in_xbundle->name, vid);
                return false;
            }
            return true;
    
        case PORT_VLAN_DOT1Q_TUNNEL:
            if (!xbundle_allows_cvlan(in_xbundle, vid)) {
                xlate_report_error(ctx, "dropping VLAN %"PRIu16" packet received "
                                   "on dot1q-tunnel port %s that excludes this "
                                   "VLAN", vid, in_xbundle->name);
                return false;
            }
            return true;
    
        default:
            OVS_NOT_REACHED();
        }
    
    }
    

    如果报文通过了input_vid_is_valid的检查,则调用xvlan_input_translate获取vlan信息,以便报文使用此vlan信息在后续流程中进行转发。

    //in_xbundle为入端口
    //in_xvlan为报文携带的vlan,如果报文不带vlan,则为空
    //xvlan输出参数
    static void
    xvlan_input_translate(const struct xbundle *in_xbundle,
                          const struct xvlan *in_xvlan, struct xvlan *xvlan)
    {
    
        switch (in_xbundle->vlan_mode) {
        //access模式下,使用in_xbundle->vlan,此vlan为用户配置。
        case PORT_VLAN_ACCESS:
            memset(xvlan, 0, sizeof(*xvlan));
            xvlan->v[0].tpid = in_xvlan->v[0].tpid ? in_xvlan->v[0].tpid :
                                                     ETH_TYPE_VLAN_8021Q;
            xvlan->v[0].vid = in_xbundle->vlan;
            xvlan->v[0].pcp = in_xvlan->v[0].pcp;
            break;
        //trunk模式下,使用in_xvlan即可
        case PORT_VLAN_TRUNK:
            xvlan_copy(xvlan, in_xvlan);
            break;
        //native_tagged和native_untagged模式下,也使用in_xvlan,
        //如果in_xvlan为空,则说明报文不带vlan,使用native vlan
        case PORT_VLAN_NATIVE_UNTAGGED:
        case PORT_VLAN_NATIVE_TAGGED:
            xvlan_copy(xvlan, in_xvlan);
            if (!in_xvlan->v[0].vid) {
                xvlan->v[0].tpid = in_xvlan->v[0].tpid ? in_xvlan->v[0].tpid :
                                                         ETH_TYPE_VLAN_8021Q;
                xvlan->v[0].vid = in_xbundle->vlan;
                xvlan->v[0].pcp = in_xvlan->v[0].pcp;
            }
            break;
    
        case PORT_VLAN_DOT1Q_TUNNEL:
            xvlan_copy(xvlan, in_xvlan);
            xvlan_push_uninit(xvlan);
            xvlan->v[0].tpid = in_xbundle->qinq_ethtype;
            xvlan->v[0].vid = in_xbundle->vlan;
            xvlan->v[0].pcp = 0;
            break;
    
        default:
            OVS_NOT_REACHED();
        }
    }
    

    报文出方向vlan处理

    经过fdb表后,如果找到出端口,则调用output_normal将报文从出端口发出,如果没有找到出端口,则将报文发送到所有的端口,最终也会调用output_normal。此函数中调用xvlan_output_translate根据出端口vlan模式选择最终的vlan信息。

    //out_xbundle出端口
    //xvlan在xvlan_input_translate输出的vlan信息
    //out_xvlan最终使用的vlan,报文会携带此vlan
    static void
    xvlan_output_translate(const struct xbundle *out_xbundle,
                           const struct xvlan *xvlan, struct xvlan *out_xvlan)
    {
        switch (out_xbundle->vlan_mode) {
        //access模式,则设置out_xvlan为0,即报文发出去时不携带vlan
        case PORT_VLAN_ACCESS:
            memset(out_xvlan, 0, sizeof(*out_xvlan));
            break;
        //trunk和native_tagged模式下,报文发出去时携带vlan
        case PORT_VLAN_TRUNK:
        case PORT_VLAN_NATIVE_TAGGED:
            xvlan_copy(out_xvlan, xvlan);
            break;
        //native_untagged模式下,如果报文out_xvlan中包含native vlan, 
        //则需要将此vlan去掉,即报文发出去时不携带vlan
        case PORT_VLAN_NATIVE_UNTAGGED:
            xvlan_copy(out_xvlan, xvlan);
            if (xvlan->v[0].vid == out_xbundle->vlan) {
                xvlan_pop(out_xvlan);
            }
            break;
    
        case PORT_VLAN_DOT1Q_TUNNEL:
            xvlan_copy(out_xvlan, xvlan);
            xvlan_pop(out_xvlan);
            break;
    
        default:
            OVS_NOT_REACHED();
        }
    }
    

    配置

    和vlan相关的配置有如下几个参数,每种vlan mode需要的参数不一样,下面分别实践。
    vlan_mode 为 access时,只需要tag参数。
    vlan_mode 为trunk时,只需要trunks参数。
    vlan_mode为native−tagged或者native−untagged时,同时需要tag和trunks。

    image.png

    具体的命令如下

    ovs-vsctl set port vetha tag=101
    ovs-vsctl set port vetha vlan_mode=trunk
    ovs-vsctl set port vetha trunks=101-102
    

    相关文章

      网友评论

          本文标题:ovs vlan

          本文链接:https://www.haomeiwen.com/subject/qztlxltx.html