【OVS2.5.0源码分析】upcall处理线程分析(3)

第一篇介绍了netlink消息的接收,接收到的消息是最原始的消息格式,本篇介绍如何把原始的消息,转化为upcall对象。

1、parse_odp_packet函数

static int
parse_odp_packet(const struct dpif_netlink *dpif, struct ofpbuf *buf,
                 struct dpif_upcall *upcall, int *dp_ifindex)
{
    static const struct nl_policy ovs_packet_policy[] = {
        /* Always present. */
        [OVS_PACKET_ATTR_PACKET] = { .type = NL_A_UNSPEC,
                                     .min_len = ETH_HEADER_LEN },
        [OVS_PACKET_ATTR_KEY] = { .type = NL_A_NESTED },

        /* OVS_PACKET_CMD_ACTION only. */
        [OVS_PACKET_ATTR_USERDATA] = { .type = NL_A_UNSPEC, .optional = true },
        [OVS_PACKET_ATTR_EGRESS_TUN_KEY] = { .type = NL_A_NESTED, .optional = true },
        [OVS_PACKET_ATTR_ACTIONS] = { .type = NL_A_NESTED, .optional = true },
        [OVS_PACKET_ATTR_MRU] = { .type = NL_A_U16, .optional = true }
    };

    struct ovs_header *ovs_header;
    struct nlattr *a[ARRAY_SIZE(ovs_packet_policy)];
    struct nlmsghdr *nlmsg;
    struct genlmsghdr *genl;
    struct ofpbuf b;
    int type;

    ofpbuf_use_const(&b, buf->data, buf->size);

    nlmsg = ofpbuf_try_pull(&b, sizeof *nlmsg);   //获取nlmsghdr信息
    genl = ofpbuf_try_pull(&b, sizeof *genl);     //获取genlmsghdr信息
    ovs_header = ofpbuf_try_pull(&b, sizeof *ovs_header);  //获取ovs_header信息
    if (!nlmsg || !genl || !ovs_header
        || nlmsg->nlmsg_type != ovs_packet_family
        || !nl_policy_parse(&b, 0, ovs_packet_policy, a,       //获取其他属性信息
                            ARRAY_SIZE(ovs_packet_policy))) {
        return EINVAL;
    }

    type = (genl->cmd == OVS_PACKET_CMD_MISS ? DPIF_UC_MISS
            : genl->cmd == OVS_PACKET_CMD_ACTION ? DPIF_UC_ACTION
            : -1);
    if (type < 0) {
        return EINVAL;
    }

    /* (Re)set ALL fields of '*upcall' on successful return. */
    upcall->type = type;
    upcall->key = CONST_CAST(struct nlattr *,
                             nl_attr_get(a[OVS_PACKET_ATTR_KEY]));
    upcall->key_len = nl_attr_get_size(a[OVS_PACKET_ATTR_KEY]);
    dpif_flow_hash(&dpif->dpif, upcall->key, upcall->key_len, &upcall->ufid);
    upcall->userdata = a[OVS_PACKET_ATTR_USERDATA];
    upcall->out_tun_key = a[OVS_PACKET_ATTR_EGRESS_TUN_KEY];
    upcall->actions = a[OVS_PACKET_ATTR_ACTIONS];
    upcall->mru = a[OVS_PACKET_ATTR_MRU];

    /* Allow overwriting the netlink attribute header without reallocating. */
    dp_packet_use_stub(&upcall->packet,       //构建upcall->packet对象,包括base指针、data指针、长度等信息
                    CONST_CAST(struct nlattr *,
                               nl_attr_get(a[OVS_PACKET_ATTR_PACKET])) - 1,
                    nl_attr_get_size(a[OVS_PACKET_ATTR_PACKET]) +
                    sizeof(struct nlattr));
    dp_packet_set_data(&upcall->packet,
                    (char *)dp_packet_data(&upcall->packet) + sizeof(struct nlattr));
    dp_packet_set_size(&upcall->packet, nl_attr_get_size(a[OVS_PACKET_ATTR_PACKET]));

    *dp_ifindex = ovs_header->dp_ifindex;

    return 0;
}
2、nl_policy_parse函数

/* Parses the 'msg' starting at the given 'nla_offset' as a sequence of Netlink
 * attributes.  'policy[i]', for 0 <= i < n_attrs, specifies how the attribute
 * with nla_type == i is parsed; a pointer to attribute i is stored in
 * attrs[i].  Returns true if successful, false on failure.
 *
 * If the Netlink attributes in 'msg' follow a Netlink header and a Generic
 * Netlink header, then 'nla_offset' should be NLMSG_HDRLEN + GENL_HDRLEN. */
bool
nl_policy_parse(const struct ofpbuf *msg, size_t nla_offset,
                const struct nl_policy policy[],
                struct nlattr *attrs[], size_t n_attrs)
{
    struct nlattr *nla;
    size_t left;
    size_t i;

    memset(attrs, 0, n_attrs * sizeof *attrs);

    if (msg->size < nla_offset) {
        VLOG_DBG_RL(&rl, "missing headers in nl_policy_parse");
        return false;
    }

    NL_ATTR_FOR_EACH (nla, left, ofpbuf_at(msg, nla_offset, 0),    //获取netlink的其他属性
                      msg->size - nla_offset)
    {
        uint16_t type = nl_attr_type(nla);
        if (type < n_attrs && policy[type].type != NL_A_NO_ATTR) {
            const struct nl_policy *e = &policy[type];
            if (!nl_attr_validate(nla, e)) {
                return false;
            }
            if (attrs[type]) {
                VLOG_DBG_RL(&rl, "duplicate attr %"PRIu16, type);
            }
            attrs[type] = nla;
        }
    }
    if (left) {
        VLOG_DBG_RL(&rl, "attributes followed by garbage");
        return false;
    }

    for (i = 0; i < n_attrs; i++) {   //检查必须字段是否有值
        const struct nl_policy *e = &policy[i];
        if (!e->optional && e->type != NL_A_NO_ATTR && !attrs[i]) {
            VLOG_DBG_RL(&rl, "required attr %"PRIuSIZE" missing", i);
            return false;
        }
    }
    return true;
}

<think>好的,我现在需要帮助用户分析Open vSwitch(OVS)的源码实现。用户已经提供了一些相关的引用资料,我需要结合这些资料来组织一个详细的回答。首先,我应该先理解用户的需求,他们可能是一个刚开始接触OVS源码的开发者或研究人员,想要了解源码的结构和关键模块。 根据引用资料,OVS的源码结构包括ovs-vswitchd守护进程、ovsdb-server数据库、内核模块等。用户提供的引用[1]提到了源码文件布局,我需要先介绍这一点,让用户有一个整体的认识。然后,根据引用[2]、[3]、[4],网桥(Bridge)的实现、vswitchd的功能以及主循环的结构都是关键部分。我需要将这些部分逐一展开,说明它们的功能和相互关系。 接下来,我需要确保回答符合用户给出的格式要求,比如使用中文,正确应用行内和独立公式的LaTeX格式(虽然目前可能用不上数学公式,但如果有需要的话要记得)。另外,用户要求生成相关问题,这些问题需要与源码分析相关,能够引导用户进一步探索。 在组织内容时,可能需要分模块介绍,比如源码结构、核心组件、数据路径处理、主循环机制等。每个部分需要结合引用中的具体信息,比如bridge_run()和bridge_wait()的作用,以及ovs-vswitchd的主循环结构。同时,要确保引用标识正确添加到对应的段落末尾,例如引用[^1]、[^2]等。 还要注意用户提到的“系统级指令”中的要求,比如行内数学表达式用$...$,独立公式用$$...$$,不过当前问题可能涉及较少的数学内容,但如果有涉及的地方需要正确应用。另外,回答需要结构清晰,分步骤说明,保证真实可靠,所以需要准确引用提供的资料,并正确标注来源。 最后,生成的相关问题应该针对OVS源码的深入分析,比如数据路径实现、与内核模块的交互、性能优化等,这样可以帮助用户进一步研究。需要确保问题具有针对性,能够引导用户探索源码的不同方面。 总结来说,回答的结构应该包括源码结构概述、核心模块分析(如网桥、vswitchd)、主循环机制、数据路径处理,以及源码分析方法,每个部分结合引用资料进行说明,并在最后生成相关问题。需要注意格式正确,引用标识正确放置,语言简洁明了,帮助用户逐步理解OVS的源码实现。</think>### Open vSwitch源码实现分析 #### 1. 源码结构概述 Open vSwitch(OVS)的源码组织主要分为以下模块: - **ovs-vswitchd**:用户空间守护进程,负责处理交换逻辑和流表管理[^4] - **ovsdb-server**:轻量级数据库,存储交换机配置和状态信息 - **内核模块**:实现快速数据路径(如`datapath/linux`目录) - **协议支持**:包括OpenFlow、NetFlow等协议实现 引用[1]指出,2.17.1版本源码目录结构清晰,核心模块集中在`lib`、`ofproto`、`vswitchd`等目录中[^1]。 #### 2. 核心组件分析 **(1) 网桥(Bridge)实现** - 通过`struct bridge`表示虚拟网桥,包含端口、流表、QoS策略等配置[^2] - 功能实现比Linux Bridge更强大,支持: $$ \text{OpenFlow协议} + \text{多级流表} + \text{SDN扩展} $$ - 关键数据结构关联: ```c struct bridge → struct ofproto → struct flow_table ``` **(2) vswitchd主循环** 主程序通过事件驱动模型运行: ```c while (!exiting) { bridge_run(); // 处理网桥状态更新 unixctl_server_run();// 执行控制命令 netdev_run(); // 处理网络设备事件 poll_block(); // 等待事件触发[^4] } ``` 其中`bridge_wait()`实现同步机制,协调OVSDB事务、接口通知等事件[^3]。 #### 3. 数据路径处理 - **快速路径**:内核模块处理常规数据包转发 - **慢速路径**:用户空间处理复杂匹配和OpenFlow规则 - 流量匹配公式: 设数据包特征为$P=\{src\_mac, dst\_mac, ...\}$,流表规则为$F=\{match\_fields, actions\}$,则: $$ \exists F_i \in FlowTable,\ s.t.\ P \subseteq match\_fields(F_i) $$ #### 4. 源码分析方法论 建议采用以下步骤: 1. 从`ovs-vswitchd/main.c`跟踪主循环 2. 分析`bridge_init()`初始化流程 3. 研究`ofproto`模块的OpenFlow实现 4. 对照OVSDB模式文件`vswitch.ovsschema` ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值