ovs-vswitchd的启动分析

2023-10-26

ovs-vswitchd的启动分析(无修改源码)

(一)主要数据结构和概念了解

1.概念

在 OVS 中, 有几个非常重要的概念:

Bridge: Bridge 代表一个以太网交换机(Switch),一个主机中可以创建一个或者多个 Bridge 设备。 Port: 端口与物理交换机的端口概念类似,每个 Port 都隶属于一个 Bridge。 Interface: 连接到 Port 的网络接口设备。在通常情况下,Port 和 Interface 是一对一的关系, 只有在配置 Port 为 bond 模式后,Port 和 Interface 是一对多的关系。

当我们创建了一个交换机(网桥)以后,此时网络功能不受影响,但是会产生一个虚拟网卡,名字就是brname,之所以会产生一个虚拟网卡,是为了实现接下来的网桥(交换机)功能。

有了这个交换机以后,我还需要为这个交换机增加端口(port),一个端口,就是一个物理网卡,当网卡加入到这个交换机之后,其工作方式就和普通交换机的一个端口的工作方式类似了。

这里要特别注意,网卡加入网桥以后,要按照网桥的工作标准工作,那么加入的一个端口就必须是以混杂模式工作,工作在链路层,处理2层的帧,所以这个port就不需要配置IP了。(你没见过哪个交换的端口有IP的吧) 那么接下来你可能会问,通常的交换机不都是有一个管理接口,可以telnet到交换机上进行配置吧,那么在OVS中创建的虚拟交换机有木有这种呢,有的!上面既然创建交换机brname的时候产生了一个虚拟网口brname,那么,你给这个虚拟网卡配置了IP以后,就相当于给交换机的管理接口配置了IP,此时一个正常的虚拟交换机就搞定了。

2.数据结构

在ofproto-provider.h中注释里是这样说的。 这里定义了四类数据结构:

Struct ofproto表示一个交换机

Struct ofport表示交换机上的一个端口

Struct rule表示交换机上的一条flow规则

Struct ofgroup表示一个flow规则组

(二)main主函数分析 在文件openvswitch-2.11.4/vswitchd/ovs-vswitchd.c中! Openvswitch主要管理两种类型的设备,一个是创建的虚拟网桥,一个是连接到虚拟网桥上的设备。

int
main(int argc, char *argv[])
{
    ...   
    线程开启、网桥初始化等等
    ...

    while (!exiting) {
        memory_run();
        if (memory_should_report()) {
            struct simap usage;

            simap_init(&usage);
            bridge_get_memory_usage(&usage);
            memory_report(&usage);
            simap_destroy(&usage);
        }
        bridge_run();  //其中bridge_run就是初始化数据库中已经创建的虚拟网桥。
        unixctl_server_run(unixctl);
        netdev_run();  //在内核中添加虚拟网卡

        memory_wait();
        bridge_wait();
        unixctl_server_wait(unixctl);
        netdev_wait();
        if (exiting) {
            poll_immediate_wake();
        }
        poll_block();
        if (should_service_stop()) {
            exiting = true;
        }
    }
    
    ...
    退出和销毁操作
    ...

    return 0;
}

(三)虚拟网桥的初始化bridge_run OVS源码--openflow(四) - 程序员大本营 在文件openvswitch-2.11.4/vswitchd/bridge.c中! bridge_run --调用-->

void
bridge_run(void)
{

    bridge_run__();
  if (ovsdb_idl_get_seqno(idl) != idl_seqno ||
        if_notifier_changed(ifnotifier)) {
      ...        bridge_reconfigure(cfg ? cfg : &null_cfg);
      ...    }
}

分支(一):主要会用到 bridge_run__ --调用--> ofproto_run让每一个网桥去执行他对应的功能

static void bridge_run__(void) { ...... 其他操作 ...... /* Let each bridge do the work that it needs to do. */ HMAP_FOR_EACH (br, node, &all_bridges) { ofproto_run(br->ofproto); } }

在文件openvswitch-2.11.4/ofproto/ofproto-dpif.c中! ofproto_run --回调--> run方法

const struct ofproto_class ofproto_dpif_class = {
    ...
    run,
    ...  port_add,
   ...};

int ofproto_run(struct ofproto *p) //--------------重点 { error = p->ofproto_class->run(p); ...... connmgr_run(p->connmgr, handle_openflow); return error; }

在文件openvswitch-2.11.4/ofproto/ofproto-dpif.c中! run方法会初始化netflow, sflow, ipfix,stp, rstp, mac address learning等一系列操作。 同时也会去校验流表项规则是否过期!!!

static int
run(struct ofproto *ofproto_)
{
    stp_run(ofproto);
    rstp_run(ofproto);

    new_dump_seq = seq_read(udpif_dump_seq(ofproto->backer->udpif));

    if (ofproto->dump_seq != new_dump_seq) {
        struct rule *rule, *next_rule;
if (ofproto->dump_seq != new_dump_seq) {
        struct rule *rule, *next_rule;
        long long now = time_msec();
        /* We know stats are relatively fresh, so now is a good time to do some
         * periodic work. */
        ofproto->dump_seq = new_dump_seq;

        ovs_mutex_lock(&ofproto_mutex);
        LIST_FOR_EACH_SAFE (rule, next_rule, expirable,
                            &ofproto->up.expirable) {
            rule_expire(rule_dpif_cast(rule), now); //-------这里校验是否超时
        }
        ovs_mutex_unlock(&ofproto_mutex);

    }
    return 0;
}

分支(二):不会被用到,了解即可 bridge_reconfigure(从ovsdb-server里面读取出来的配置) --调用--> bridge_add_ports对于每一个网桥,将网卡添加进去

 

static void bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg) { HMAP_FOR_EACH (br, node, &all_bridges) { bridge_add_ports(br, &br->wanted_ports); shash_destroy(&br->wanted_ports); } bridge_run__(); }

bridge_add_ports --调用--> bridge_add_ports__ --调用--> iface_create --调用--> iface_do_create --调用--> netdev_open和ofproto_port_add

 

int ofproto_port_add(struct ofproto *ofproto, struct netdev *netdev, ofp_port_t *ofp_portp) { error = ofproto->ofproto_class->port_add(ofproto, netdev); }

ofproto_port_add --回调--> port_add --调用--> dpif_port_add

int dpif_port_add(struct dpif *dpif, struct netdev *netdev, odp_port_t *port_nop) { error = dpif->dpif_class->port_add(dpif, netdev, &port_no); }

dpif_port_add --回调--> port_add/dpif_netdev_port_add

const struct dpif_class dpif_netdev_class = { "netdev", ....   dpif_netdev_port_add,    ....};

const struct dpif_class dpif_netdev_class = { "netdev", ....   dpif_netdev_port_add,    ....};

dpif_netdev_port_add --调用--> do_add_port

static int
dpif_netdev_port_add(struct dpif *dpif, struct netdev *netdev,
                     odp_port_t *port_nop)
{
    struct dp_netdev *dp = get_dp_netdev(dpif);

    ovs_mutex_lock(&dp->port_mutex);
    dpif_port = netdev_vport_get_dpif_port(netdev, namebuf, sizeof namebuf);
if (!error) {
        *port_nop = port_no;
        error = do_add_port(dp, dpif_port, netdev_get_type(netdev), port_no);
    }
    ovs_mutex_unlock(&dp->port_mutex);

    return error;
}

do_add_port这里会调用内核模块openvswitch.ko,在内核中添加虚拟网卡。

static int
do_add_port(struct dp_netdev *dp, const char *devname, const char *type,
            odp_port_t port_no)
    OVS_REQUIRES(dp->port_mutex)
{
    struct dp_netdev_port *port;
    int error;

    /* Reject devices already in 'dp'. */
    if (!get_port_by_name(dp, devname, &port)) {
        return EEXIST;
    }

    error = port_create(devname, type, port_no, &port);
    if (error) {
        return error;
    }

    hmap_insert(&dp->ports, &port->node, hash_port_no(port_no));
    seq_change(dp->port_seq);

    reconfigure_datapath(dp);

    return 0;
}

(四)虚拟网卡的初始化(与设备关联)netdev_run

在文件openvswitch-2.11.4/lib/netdev.c:中!

void netdev_run(void)
    OVS_EXCLUDED(netdev_mutex)
{
    netdev_initialize();

    struct netdev_registered_class *rc;
    CMAP_FOR_EACH (rc, cmap_node, &netdev_classes) {
        if (rc->class->run) {
            rc->class->run(rc->class);
        }
    }
}

依次循环调用netdev_classes中的每一个run。 对于不同类型的虚拟网卡,都有对应的netdev_class。 #define NETDEV_LINUX_CLASS_COMMON \ .run = netdev_linux_run, \    ...

NETDEV_LINUX_CLASS_COMMON

const struct netdev_class netdev_linux_class = {
    NETDEV_LINUX_CLASS_COMMON,
    LINUX_FLOW_OFFLOAD_API,
    .type = "system",
    .construct = netdev_linux_construct,
    .get_stats = netdev_linux_get_stats,
    .get_features = netdev_linux_get_features,
    .get_status = netdev_linux_get_status,
    .get_block_id = netdev_linux_get_block_id
};

const struct netdev_class netdev_tap_class = {
    NETDEV_LINUX_CLASS_COMMON,
    .type = "tap",
    .construct = netdev_linux_construct_tap,
    .get_stats = netdev_tap_get_stats,
    .get_features = netdev_linux_get_features,
    .get_status = netdev_linux_get_status,
};

const struct netdev_class netdev_internal_class = {
    NETDEV_LINUX_CLASS_COMMON,
    .type = "internal",
    .construct = netdev_linux_construct,
    .get_stats = netdev_internal_get_stats,
    .get_status = netdev_internal_get_status,
};

添加图片注释,不超过 140 字(可选)

netdev_run --调用--> netdev_linux_run 会调用netlink的sock得到虚拟网卡的状态,并且更新状态。

static void
netdev_linux_run(const struct netdev_class *netdev_class OVS_UNUSED)
{
    struct nl_sock *sock;
    sock = netdev_linux_notify_sock();do {
        ofpbuf_use_stub(&buf, buf_stub, sizeof buf_stub);
        error = nl_sock_recv(sock, &buf, &nsid, false);
        if (!error) {
            struct rtnetlink_change change;

            if (rtnetlink_parse(&buf, &change)) {
                struct netdev *netdev_ = NULL;
                char dev_name[IFNAMSIZ];

                if (!change.ifname) {
                     change.ifname = if_indextoname(change.if_index, dev_name);
                }

                if (change.ifname) {
                    netdev_ = netdev_from_name(change.ifname);
                }
                if (netdev_ && is_netdev_linux_class(netdev_->netdev_class)) {
                    struct netdev_linux *netdev = netdev_linux_cast(netdev_);

                    ovs_mutex_lock(&netdev->mutex);
                    netdev_linux_update(netdev, nsid, &change);
                    ovs_mutex_unlock(&netdev->mutex);
                }

                if (change.ifname &&
                    rtnetlink_type_is_rtnlgrp_link(change.nlmsg_type)) {

                    /* Need to try updating the LAG information. */
                    ovs_mutex_lock(&lag_mutex);
                    netdev_linux_update_lag(&change);
                    ovs_mutex_unlock(&lag_mutex);
                }
                netdev_close(netdev_);
            }
        } 
    } while (!error);
}

转自:https://www.cnblogs.com/ssyfj/p/14767851.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ovs-vswitchd的启动分析 的相关文章

随机推荐

  • uniapp切片-可视化设计工具(一套代码编译到7个平台iOS、Android、H5、小程序)

    uni app 是一个使用 Vue js 开发跨平台应用的前端框架 开发者编写一套代码 可编译到iOS Android H5 小程序等多个平台 一套代码编到7个平台 难以置信吗 依次扫描7个二维码 亲自体验最全面的跨平台效果 uni app
  • C++之sort()函数详解,刷题必备~

    顾名思义 sort就是用来排序的函数 它根据具体情形使用不同的排序方法 效率较高 一般来说 不推荐使用C语言中的qsort函数 原因是qsort用起来比较烦琐 涉及很多指针的操作 而且sort在实现中规避了经典快速排序中可能出现的会导致实际
  • C# 中的sealed修饰符学习

    转载原地址 http developer 51cto com art 200908 147327 htm C 语言还是比较常见的东西 这里我们主要介绍C sealed修饰符 包括介绍两个修饰符在含义上互相排斥用于方法和属性等方面 C sea
  • python爬虫网络出错怎么办_Python爬虫异常处理

    100 继续 客户端应当继续发送请求 客户端应当继续发送请求的剩余部分 或者如果请求已经完成 忽略这个响应 101 转换协议 在发送完这个响应最后的空行后 服务器将会切换到在Upgrade 消息头中定义的那些协议 只有在切换新的协议更有好处
  • linux上redis常用命令以及遇到的问题

    1 在linux上解压缩后使用make命令进行编译的时候 错误类型 zmalloc h 50 31 致命错误 jemalloc jemalloc h 没有那个文件或目录 原因是因为编译的时候Linux默认内存分配器是jemalloc 而Re
  • 添加商品到购物车 Vuex

    商品详情 购物车页面 code
  • OFDM插入导频过程详解

    ofdm符号的长度 有效数据 cp的长度 cp就是将有效数据的后半部分1 4截取并添加到有效数据的开始部分 比如一个ofdm符号的长度为4us 那么有效数据的长度为3 2us cp的长度为0 8us 子载波的间隔 1 有效数据的长度 就是有
  • Unity使用c#开发遇上的问题(十三)(unity平台下使用 Vuforia 以及 ARFoundiation 的总结,根据个人观点)

    文章目录 前言 一 Vuforia的使用感觉 二 ARfoundiation的使用感觉 总结 前言 有一段时间没有更新系列的内容 上次更新完又重新思考了一下以后进行的方向 这里就目前接触的Vuforia 和 unity 自带的AR 之前叫A
  • 自动生成根据mysql表创建hive表脚本

    bin bash source etc profile 该脚本为手动传参根据MySQL表信息创建hive表 输入参数判断逻辑 必须数据两个参数 一个是MySQL库名 第二个是表名 if eq 2 then db name 1 mysql 库
  • 浅析java垃圾回收机制

    一 什么是垃圾回收 1 垃圾回收 顾名思义 便是将已经分配出去的 但却不再使用的内存回收回来 以便能够再次分配 在 Java 虚拟机的语境下 垃圾指的是死亡的对象所占据的堆空间 垃圾回收只会负责释放那些对象占有的内存 此时对象也就被销毁 2
  • 0长度数组的使用,重点掌握的知识

    0长度的数组在ISO C和C 的规格说明书中是不允许的 但是GCC的C99支持的这种用法 GCC对0长度数组的文档参考 Arrays of Length Zero 如下代码片段 哪个更简洁更灵活 看一眼就知道了 include
  • 用vscode开发autojs,输出窗口不显示任何输出结果

    我的情况是 我vscode开发autojs 程序 之前在一切正常的情况下 输出窗口可以正常显示程序运行结果 右侧红圈里可以选择我连接的手机型号 如下图 但是现在出现问题 就是输出窗口不显示任何结果 在右侧的选项卡里也找不到我的手机型号 之前
  • 2021年全球与中国龙胆苦苷行业市场规模及发展前景分析

    2021年全球与中国龙胆苦苷行业市场规模及发展前景分析 本报告研究全球与中国市场龙胆苦苷的发展现状及未来发展趋势 分别从生产和消费的角度分析龙胆苦苷的主要生产地区 主要消费地区以及主要的生产商 重点分析全球与中国市场的主要厂商产品特点 产品
  • (R,线性回归)R语言里的模型诊断图(Residuals vs Fitted,Normal QQ , Scale-Location ,Residuals Leverage)

    线性回归 是概率统计学里最重要的统计方法 也是机器学习中一类非常重要的算法 线性模型简单理解非常容易 但是内涵是非常深奥的 尤其是线性回归模型中的Diagnostics plot的阅读与理解一直被认为是线性回归中的一个难点 在任何线性模型中
  • 获取微信公众号地址的图片不能正常显示的问题

    获取微信公众号地址的图片不能正常显示的问题 目前已经获取微信公众号发布的图片 但不能正常显示 提示 此图片来自微信公众平台 未经允许不得引用 看了一下他的地址是这样的 https mmbiz qpic cn mmbiz jpg ic70qV
  • Codeforces Round #291 (Div. 2)

    题目链接contest 514 A Chewba ca and Number 不允许有前导零 所以如果第一位是9的话 需要特别考虑 一开始理解错了题意 又WA了呜呜呜 include
  • 弱密码测试工具blaster使用演示

    声明 出品 安全客 以下内容 来自安全客作者原创 由于传播 利用此文所提供的信息而造成的任何直接或间接的后果和损失 均由使用者本人负责 长白山攻防实验室以及文章作者不承担任何责任 关于blaster blaster是一款强大的弱密码隐患检测
  • RabbitMQ-Java 简单使用

    RabbitMQ Java 入门案例 参考非常详细的博主教程 https www cnblogs com dtdx p 14362760 html SpringBoot Java 版教程 https blog csdn net lgl782
  • 【C/C++】智能指针

    文章目录 1 智能指针的原理 1 1RAII 1 2实现一个自己的智能指针 1 2 1拷贝出现的二次析构问题 2 标准库中的智能指针 2 1std auto ptr 2 2std unique ptr 2 3std shared ptr 2
  • ovs-vswitchd的启动分析

    ovs vswitchd的启动分析 无修改源码 一 主要数据结构和概念了解 1 概念 在 OVS 中 有几个非常重要的概念 Bridge Bridge 代表一个以太网交换机 Switch 一个主机中可以创建一个或者多个 Bridge 设备