热门标签 | 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交换机的总体行为,并和标准交换机进行对比。


推荐阅读
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • 开发笔记:实验7的文件读写操作
    本文介绍了使用C++的ofstream和ifstream类进行文件读写操作的方法,包括创建文件、写入文件和读取文件的过程。同时还介绍了如何判断文件是否成功打开和关闭文件的方法。通过本文的学习,读者可以了解如何在C++中进行文件读写操作。 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • 本文讨论了在openwrt-17.01版本中,mt7628设备上初始化启动时eth0的mac地址总是随机生成的问题。每次随机生成的eth0的mac地址都会写到/sys/class/net/eth0/address目录下,而openwrt-17.01原版的SDK会根据随机生成的eth0的mac地址再生成eth0.1、eth0.2等,生成后的mac地址会保存在/etc/config/network下。 ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • SpringBoot uri统一权限管理的实现方法及步骤详解
    本文详细介绍了SpringBoot中实现uri统一权限管理的方法,包括表结构定义、自动统计URI并自动删除脏数据、程序启动加载等步骤。通过该方法可以提高系统的安全性,实现对系统任意接口的权限拦截验证。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 有没有一种方法可以在不继承UIAlertController的子类或不涉及UIAlertActions的情况下 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 本文介绍了深入浅出Linux设备驱动编程的重要性,以及两种加载和删除Linux内核模块的方法。通过一个内核模块的例子,展示了模块的编译和加载过程,并讨论了模块对内核大小的控制。深入理解Linux设备驱动编程对于开发者来说非常重要。 ... [详细]
  • WhenIusepythontoapplythepymysqlmoduletoaddafieldtoatableinthemysqldatabase,itdo ... [详细]
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社区 版权所有