热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【OVS2.5.0源码分析】vlan&trunk实现原理分析(1)

传统交换机的端口可以按照vlan可以划分为access、trunk和hybrid三类接口。首先,我们先看OVS的VLAN实现原理,最后对比OVS与传统交换机的差异。OVS中,数据面的转发流

传统交换机的端口可以按照vlan可以划分为access、trunk和hybrid三类接口。 首先,我们先看OVS的VLAN实现原理,最后对比OVS与传统交换机的差异。

OVS中,数据面的转发流表都是从用户态下发的,所以流表生成的入口是upcall_actions函数(该函数不是upcall的总入口,由于层次比较多,以该函数作为分析的入口是合适的)。

1、xlate_actions函数

            mirror_ingress_packet(&ctx);
do_xlate_actions(ofpacts, ofpacts_len, &ctx); //openflow流表转化为精确流表
if (ctx.error) {
goto exit;
}

2、do_xlate_actons函数

        case OFPACT_OUTPUT:
xlate_output_action(ctx, ofpact_get_OUTPUT(a)->port, //normal规则也是output的一种
ofpact_get_OUTPUT(a)->max_len, true);
break;

3、xlate_output_action函数

static void
xlate_output_action(struct xlate_ctx *ctx,
ofp_port_t port, uint16_t max_len, bool may_packet_in)
{
ofp_port_t prev_nf_output_iface = ctx->nf_output_iface;

ctx->nf_output_iface = NF_OUT_DROP;

switch (port) {
case OFPP_IN_PORT:
compose_output_action(ctx, ctx->xin->flow.in_port.ofp_port, NULL);
break;
case OFPP_TABLE:
xlate_table_action(ctx, ctx->xin->flow.in_port.ofp_port,
0, may_packet_in, true);
break;
case OFPP_NORMAL:
xlate_normal(ctx); //normal规则流表转化为精确流表
break;
case OFPP_FLOOD:
flood_packets(ctx, false);
break;
case OFPP_ALL:
flood_packets(ctx, true);
break;
case OFPP_CONTROLLER:
execute_controller_action(ctx, max_len,
(ctx->in_group ? OFPR_GROUP
: ctx->in_action_set ? OFPR_ACTION_SET
: OFPR_ACTION),
0);
break;
case OFPP_NONE:
break;
case OFPP_LOCAL:
default:
if (port != ctx->xin->flow.in_port.ofp_port) {
compose_output_action(ctx, port, NULL);
} else {
xlate_report(ctx, "skipping output to input port");
}
break;
}

if (prev_nf_output_iface == NF_OUT_FLOOD) {
ctx->nf_output_iface = NF_OUT_FLOOD;
} else if (ctx->nf_output_iface == NF_OUT_DROP) {
ctx->nf_output_iface = prev_nf_output_iface;
} else if (prev_nf_output_iface != NF_OUT_DROP &&
ctx->nf_output_iface != NF_OUT_FLOOD) {
ctx->nf_output_iface = NF_OUT_MULTI;
}
}

4、xlate_normal函数

xlate_normal(struct xlate_ctx *ctx)
{
.......

/* Check VLAN. */
vid = vlan_tci_to_vid(flow->vlan_tci); //计算报文的vlan值
if (!input_vid_is_valid(vid, in_xbundle, ctx->xin->packet != NULL)) { //判断报文是否满足vlan要求,如果不满足则丢球
xlate_report(ctx, "disallowed VLAN VID for this input port, dropping");
return;
}
vlan = input_vid_to_vlan(in_xbundle, vid); //计算报文进入OVS桥之后的VLAN值,该VLAN会贯穿报文在OVS内处理的全流程

......
    /* Determine output bundle. */    if (mcast_snooping_enabled(ctx->xbridge->ms)        ......    } else {        ovs_rwlock_rdlock(&ctx->xbridge->ml->rwlock);        mac = mac_learning_lookup(ctx->xbridge->ml, flow->dl_dst, vlan);     //根据目标mac和vlan值寻找目的端口        mac_port = mac ? mac_entry_get_port(ctx->xbridge->ml, mac) : NULL;        ovs_rwlock_unlock(&ctx->xbridge->ml->rwlock);        if (mac_port) {            struct xlate_cfg *xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp);            struct xbundle *mac_xbundle = xbundle_lookup(xcfg, mac_port);            if (mac_xbundle && mac_xbundle != in_xbundle) {                xlate_report(ctx, "forwarding to learned port");                output_normal(ctx, mac_xbundle, vlan);                 //找到目的端口,从该端口发送报文            } else if (!mac_xbundle) {                xlate_report(ctx, "learned port is unknown, dropping");            } else {                xlate_report(ctx, "learned port is input port, dropping");            }        } else {            xlate_report(ctx, "no learned MAC for destination, flooding");            xlate_normal_flood(ctx, in_xbundle, vlan);     //没找到目的端口,flood报文        }    }}

 
 
static bool
input_vid_is_valid(uint16_t vid, struct xbundle *in_xbundle, bool warn)
{
/* Allow any VID on the OFPP_NONE port. */
if (in_xbundle == &ofpp_none_bundle) {
return true;
}

switch (in_xbundle->vlan_mode) {
case PORT_VLAN_ACCESS:
if (vid) { //如果入端口为ACCESS口,且报文包含VLAN,那么丢弃该报文
if (warn) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
VLOG_WARN_RL(&rl, "dropping VLAN %"PRIu16" tagged "
"packet received on port %s configured as VLAN "
"%"PRIu16" access port", vid, in_xbundle->name,
in_xbundle->vlan);
}
return false;
}
return true;

case PORT_VLAN_NATIVE_UNTAGGED:
case PORT_VLAN_NATIVE_TAGGED:
if (!vid) { //如果端口类型为native-untagged和native-tagged,如果报文不包含VLAN,则接受该报文;如果包含VLAN,那么VLAN必须包含在端口的VLAN中。
/* Port must always carry its native VLAN. */
return true;
}
/* Fall through. */
case PORT_VLAN_TRUNK:
if (!xbundle_includes_vlan(in_xbundle, vid)) { //如果端口类型为trunk,如果报文不包含VLAN,则接受该报文;如果包含VLAN,那么VLAN必须包含在端口的VLAN中。
if (warn) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
VLOG_WARN_RL(&rl, "dropping VLAN %"PRIu16" packet "
"received on port %s not configured for trunking "
"VLAN %"PRIu16, vid, in_xbundle->name, vid);
}
return false;
}
return true;

default:
OVS_NOT_REACHED();
}

}

static uint16_t
input_vid_to_vlan(const struct xbundle *in_xbundle, uint16_t vid) //计算报文进入OVS交换机之后,报文的VLAN值
{
switch (in_xbundle->vlan_mode) {
case PORT_VLAN_ACCESS:
return in_xbundle->vlan; //如果端口是ACCESS口,则报文进入OVS交换机后,VLAN值=端口的VLAN值;
break;

case PORT_VLAN_TRUNK: //如果端口是trunk口,则报文的VLAN值不变;
return vid;

case PORT_VLAN_NATIVE_UNTAGGED:
case PORT_VLAN_NATIVE_TAGGED:
return vid ? vid : in_xbundle->vlan; //如果端口是native-tagged和native-untagged,当报文没有vlan时,报文的VLAN等于端口vlan,否则不变;

default:
OVS_NOT_REACHED();
}
}

5、output_normal函数

    ......

vid = output_vlan_to_vid(out_xbundle, vlan); //计算报文发出OVS交换机之后的VLAN值

......

old_tci = *flow_tci;
tci = htons(vid);
if (tci || out_xbundle->use_priority_tags) {
tci |= *flow_tci & htons(VLAN_PCP_MASK);
if (tci) {
tci |= htons(VLAN_CFI);
}
}
*flow_tci = tci;

compose_output_action(ctx, xport->ofp_port, use_recirc ? &xr : NULL); //生成流表

static uint16_t
output_vlan_to_vid(const struct xbundle *out_xbundle, uint16_t vlan)
{
switch (out_xbundle->vlan_mode) {
case PORT_VLAN_ACCESS: //access端口,报文没有vlan
return 0;

case PORT_VLAN_TRUNK:
case PORT_VLAN_NATIVE_TAGGED:
return vlan; //trunk和native-tagged端口,出口报文的VLAN等于过程中的VLAN值

case PORT_VLAN_NATIVE_UNTAGGED: //native-untagged端口,如果vlan值等于端口的vlan值,那么剥掉vlan值,否则保留vlan值
return vlan == out_xbundle->vlan ? 0 : vlan;

default:
OVS_NOT_REACHED();
}
}

6、xlate_normal_flood函数

static void
xlate_normal_flood(struct xlate_ctx *ctx, struct xbundle *in_xbundle,
uint16_t vlan)
{
struct xbundle *xbundle;

LIST_FOR_EACH (xbundle, list_node, &ctx->xbridge->xbundles) {
if (xbundle != in_xbundle
&& xbundle_includes_vlan(xbundle, vlan) //端口包含该vlan值
&& xbundle->floodable
&& !xbundle_mirror_out(ctx->xbridge, xbundle)) {
output_normal(ctx, xbundle, vlan);
}
}
ctx->nf_output_iface = NF_OUT_FLOOD;
}

下一篇将系统总结下,OVS交换机的总体行为,并和标准交换机进行对比。


推荐阅读
author-avatar
夏目nyako酱丶
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有