ovs patch端口实现原理

ovs的patch端口,用于连接两个网桥,命令如下

ovs-vsctl add-port  br10 patch3  -- set interface patch3 type=patch options:peer=patch4 -- add-port br12 patch4 -- set interface patch4 type=patch options:peer=patch3

添加patch端口流程

添加端口时,会先后调用 port_construct 和 port_add,下面看一下这两个函数对于patch端口的特殊处理
a. port_construct

static int
port_construct(struct ofport *port_)
    if (netdev_vport_is_patch(netdev)) {
        /* By bailing out here, we don't submit the port to the sFlow module
         * to be considered for counter polling export.  This is correct
         * because the patch port represents an interface that sFlow considers
         * to be "internal" to the switch as a whole, and therefore not a
         * candidate for counter polling. */
        port->odp_port = ODPP_NONE;
        ofport_update_peer(port);
        return 0;
    }

更新peer设备,比如上面的命令,给br10添加patch3,给br12添加patch4,
会调用两次ofport_update_peer,第一次调用给br10添加patch3时,遍历所有bridge,寻找peer patch4时会失败,因为patch4还没有添加
到bridge;第二次调用给br12添加patch4时,遍历所有bridge,寻找peer patch3会成功,此时会同时设置patch3和patch4的peer设备。
static void
ofport_update_peer(struct ofport_dpif *ofport)
{
    const struct ofproto_dpif *ofproto;
    struct dpif_backer *backer;
    char *peer_name;

    if (!netdev_vport_is_patch(ofport->up.netdev)) {
        return;
    }

    backer = ofproto_dpif_cast(ofport->up.ofproto)->backer;
    backer->need_revalidate = REV_RECONFIGURE;

    if (ofport->peer) {
        ofport->peer->peer = NULL;
        ofport->peer = NULL;
    }

    peer_name = netdev_vport_patch_peer(ofport->up.netdev);
    if (!peer_name) {
        return;
    }

    //遍历所有bridge,寻找peer
    HMAP_FOR_EACH (ofproto, all_ofproto_dpifs_node, &all_ofproto_dpifs) {
        struct ofport *peer_ofport;
        struct ofport_dpif *peer;
        char *peer_peer;

        //patch peer必须在同一个datapath中
        if (ofproto->backer != backer) {
            continue;
        }

        peer_ofport = shash_find_data(&ofproto->up.port_by_name, peer_name);
        if (!peer_ofport) {
            continue;
        }

        //找到peer设备后,设置本设备的peer为peer设备,同时也要设置peer设备的peer为本设备
        peer = ofport_dpif_cast(peer_ofport);
        peer_peer = netdev_vport_patch_peer(peer->up.netdev);
        if (peer_peer && !strcmp(netdev_get_name(ofport->up.netdev),
                                 peer_peer)) {
            ofport->peer = peer;
            ofport->peer->peer = ofport;
        }
        free(peer_peer);

        break;
    }
    free(peer_name);
}

b. port_add
如果是patch类型端口,不会将其添加到datapath中,所以通过 ovs-appctl dpctl/show 是看不到patch端口的

static int
port_add(struct ofproto *ofproto_, struct netdev *netdev)
{
    struct ofproto_dpif *ofproto = ofproto_dpif_cast(ofproto_);
    const char *devname = netdev_get_name(netdev);
    char namebuf[NETDEV_VPORT_NAME_BUFSIZE];
    const char *dp_port_name;

    //如果是patch类型端口,则返回。不会将其添加到datapath中
    if (netdev_vport_is_patch(netdev)) {
        sset_add(&ofproto->ghost_ports, netdev_get_name(netdev));
        return 0;
    }
    
    dp_port_name = netdev_vport_get_dpif_port(netdev, namebuf, sizeof namebuf);
    if (!dpif_port_exists(ofproto->backer->dpif, dp_port_name)) {
        odp_port_t port_no = ODPP_NONE;
        int error;

        将端口添加到datapath中
        error = dpif_port_add(ofproto->backer->dpif, netdev, &port_no);
        if (error) {
            return error;
        }
        if (netdev_get_tunnel_config(netdev)) {
            simap_put(&ofproto->backer->tnl_backers,
                      dp_port_name, odp_to_u32(port_no));
        }
    }

    if (netdev_get_tunnel_config(netdev)) {
        sset_add(&ofproto->ghost_ports, devname);
    } else {
        sset_add(&ofproto->ports, devname);
    }
}

br10和br12必须在同一个datapath,否则寻找peer就会失败,ofport->peer就会为空,后面数据转发时也不会从peer设备发出。

patch端口数据转发的处理

假如出端口为patch port,流程如下:

static void
compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port,
                        const struct xlate_bond_recirc *xr, bool check_stp)
    //如果patch端口有peer,则发送给peer
    if (xport->peer) {
       apply_nested_clone_actions(ctx, xport, xport->peer);
       return;
    }
    
    //如果patch端口没有peer
    if (xport->is_tunnel) {
        ...
    } else {
        //因为xport没有peer,所以xport->odp_port 为ODPP_NONE。
        //比如通过patch端口连接两个不同类型datapath的网桥,patch端口的peer就是空的,所以也不能互相转发。
        odp_port = xport->odp_port;
        out_port = odp_port;
    }

    if (out_port != ODPP_NONE) {
    }
    
static void
apply_nested_clone_actions(struct xlate_ctx *ctx, const struct xport *in_dev,
              struct xport *out_dev)
    //in_port 修改成出端口的id
    flow->in_port.ofp_port = out_dev->ofp_port;
    //临时设置成peer端口所在的桥
    ctx->xbridge = out_dev->xbridge;
    //查找peer端口所在的桥的openflow table,相当于peer端口收到报文的处理
    xlate_table_action(ctx, flow->in_port.ofp_port, 0, true, true,false);
        //查找 openflow table
        rule = rule_dpif_lookup_from_table();
        xlate_recursively(ctx, rule, table_id <= old_table_id);
            actions = rule_get_actions(&rule->up);
            //执行action
            do_xlate_actions(actions->ofpacts, actions->ofpacts_len, ctx);
    //恢复出端口所在桥
    ctx->xbridge = in_dev->xbridge;

最后会将流表和action安装到datapath中,指导后续报文转发。

实验

下面做一个小实验,拓扑图如下:

image.png

创建三个网桥br10,br11和br12,其中br10和br12为netdev类型,br11为system类型。
br10和br11通过patch端口patch1/patch2相连接,br10和br12通过patch端口patch3/patch4相连接。
br10上还添加了一个物理网卡enp129s0f0,其直连的网卡enp129s0f1配置ip地址2.2.2.2/24。

root@ubuntu:~# ovs-vsctl add-br br10 -- set bridge br10 datapath_type=netdev
root@ubuntu:~# ovs-vsctl add-br br12 -- set bridge br12 datapath_type=netdev
root@ubuntu:~# ovs-vsctl add-port  br10 patch3  -- set interface patch3 type=patch options:peer=patch4 -- add-port br12 patch4 -- set interface patch4 type=patch options:peer=patch3
root@ubuntu:~# ovs-vsctl add-port  br10 enp129s0f0
root@ubuntu:~# ovs-vsctl add-br br11 -- set bridge br11 datapath_type=system
root@ubuntu:~# ovs-vsctl add-port  br10 patch1  -- set interface patch1 type=patch options:peer=patch2 -- add-port br11 patch2 -- set interface patch2 type=patch options:peer=patch1

root@ubuntu:~# ovs-vsctl show
e436075d-bffe-4cce-8cab-91cf99f0a4b2
    Bridge "br10"
        Port "enp129s0f0"
            Interface "enp129s0f0"
        Port "patch3"
            Interface "patch3"
                type: patch
                options: {peer="patch4"}
        Port "patch1"
            Interface "patch1"
                type: patch
                options: {peer="patch2"}
        Port "br10"
            Interface "br10"
                type: internal
    Bridge "br11"
        Port "br11"
            Interface "br11"
                type: internal
        Port "patch2"
            Interface "patch2"
                type: patch
                options: {peer="patch1"}
    Bridge "br12"
        Port "patch4"
            Interface "patch4"
                type: patch
                options: {peer="patch3"}
        Port "br12"
            Interface "br12"
                type: internal

//查看datapath信息
//可看到有两个datapath:netdev和system,并且datapath中不包含任何patch端口
root@ubuntu:~# ovs-appctl dpctl/show
netdev@ovs-netdev:
        lookups: hit:2022051 missed:25 lost:0
        flows: 1
        port 0: ovs-netdev (tap)
        port 1: br10 (tap)
        port 2: enp129s0f0
        port 3: br12 (tap)
system@ovs-system:
        lookups: hit:0 missed:0 lost:0
        flows: 0
        masks: hit:0 total:1 hit/pkt:0.00
        port 0: ovs-system (internal)
        port 1: br11 (internal)

//给enp129s0f1 配置ip,并ping任意同网段的ip,目的是发送arp广播报文
root@ubuntu:~# ip link set dev enp129s0f1 2.2.2.2/24
root@ubuntu:~# ping 2.2.2.7
PING 2.2.2.7 (2.2.2.7) 56(84) bytes of data.
From 2.2.2.2 icmp_seq=1 Destination Host Unreachable
...

//只有netdev datapath有流表,system datapath没有
root@ubuntu:~# ovs-appctl dpctl/dump-flows netdev@ovs-netdev
flow-dump from non-dpdk interfaces:
recirc_id(0),in_port(2),packet_type(ns=0,id=0),eth(src=3c:fd:fe:a2:1f:a7,dst=ff:ff:ff:ff:ff:ff),eth_type(0x0806),arp(sip=2.2.2.2,tip=2.2.2.7,op=1/0xff), packets:1522, bytes:91320, used:0.030s, actions:1,3
root@ubuntu:~#
root@ubuntu:~# ovs-appctl dpctl/dump-flows system@ovs-system
root@ubuntu:~#

//查看网桥br10上端口统计信息,enp129s0f0收到arp广播报文后,flood到br10上所有端口(patch1没有,因为它连接的是不同类型的网桥)
root@ubuntu:~# ovs-ofctl dump-ports br10
OFPST_PORT reply (xid=0x2): 4 ports
  port  patch1: rx pkts=0, bytes=0, drop=?, errs=?, frame=?, over=?, crc=?
           tx pkts=0, bytes=0, drop=?, errs=?, coll=?
  port  patch3: rx pkts=0, bytes=0, drop=?, errs=?, frame=?, over=?, crc=?
           tx pkts=17260, bytes=1045224, drop=?, errs=?, coll=?
  port LOCAL: rx pkts=34, bytes=2780, drop=0, errs=0, frame=0, over=0, crc=0
           tx pkts=2025556, bytes=147632755, drop=0, errs=0, coll=0
  port  enp129s0f0: rx pkts=1035304, bytes=64562809, drop=0, errs=0, frame=0, over=0, crc=0
           tx pkts=0, bytes=0, drop=0, errs=0, coll=0
//查看网桥br11上端口统计信息,因为patch2没收到任何报文,所有br11上端口统计信息全0
root@ubuntu:~# ovs-ofctl dump-ports br11
OFPST_PORT reply (xid=0x2): 2 ports
  port LOCAL: rx pkts=0, bytes=0, drop=0, errs=0, frame=0, over=0, crc=0
           tx pkts=0, bytes=0, drop=0, errs=0, coll=0
  port  patch2: rx pkts=0, bytes=0, drop=?, errs=?, frame=?, over=?, crc=?
           tx pkts=0, bytes=0, drop=?, errs=?, coll=?
//查看网桥br12上端口统计信息,patch4从patch3收到报文,flood到其他端口
root@ubuntu:~# ovs-ofctl dump-ports br12
OFPST_PORT reply (xid=0x2): 2 ports
  port LOCAL: rx pkts=8, bytes=648, drop=0, errs=0, frame=0, over=0, crc=0
           tx pkts=19764, bytes=1197319, drop=0, errs=0, coll=0
  port  patch4: rx pkts=17430, bytes=1055750, drop=?, errs=?, frame=?, over=?, crc=?
           tx pkts=0, bytes=0, drop=?, errs=?, coll=?

结论:
a. patch端口只存在网桥上,datapath中不会存在
b. 如果出端口为patch端口,则相当于其peer设备收到报文,在peer设备所在网桥查找openflow流表进行转发
c. 不同类型datapath的网桥不能通过patch端口相连接

也可参考:ovs patch端口实现原理 - 简书 (jianshu.com)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值