• OVN 流表基础 -- 基于 kubeOVN (一)


    Kubectl ko 工具分析

    Kubectl ko 下可以执行 nbctl,sbctl,vsctl,ofctl,trace 等等命令

    # kubectl ko --help
    kubectl ko {subcommand} [option...]
    Available Subcommands:
      [nb|sb] [status|kick|backup|dbstatus|restore]     ovn-db operations show cluster status, kick stale server, backup database, get db consistency status or restore ovn nb db when met 'inconsistent data' error
      nbctl [ovn-nbctl options ...]    invoke ovn-nbctl
      sbctl [ovn-sbctl options ...]    invoke ovn-sbctl
      vsctl {nodeName} [ovs-vsctl options ...]   invoke ovs-vsctl on the specified node
      ofctl {nodeName} [ovs-ofctl options ...]   invoke ovs-ofctl on the specified node
      dpctl {nodeName} [ovs-dpctl options ...]   invoke ovs-dpctl on the specified node
      appctl {nodeName} [ovs-appctl options ...]   invoke ovs-appctl on the specified node
      tcpdump {namespace/podname} [tcpdump options ...]     capture pod traffic
      trace {namespace/podname} {target ip address} [target mac address] {icmp|tcp|udp} [target tcp or udp port]    trace ovn microflow of specific packet
      diagnose {all|node} [nodename]    diagnose connectivity of all nodes or a specific node
      env-check check the environment configuration
      tuning {install-fastpath|local-install-fastpath|remove-fastpath|install-stt|local-install-stt|remove-stt} {centos7|centos8}} [kernel-devel-version]  deploy  kernel optimisation components to the system
      reload restart all kube-ovn components
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    Nbctl Sbctl

    先获取到 leader pod,如 ovn-nb-leader,和 ovn-sb-leader

    kubectl get pod -n kube-system -l ovn-nb-leader=true | grep ovn-central | head -n 1 | awk '{print $1}'
    kubectl get pod -n kube-system -l ovn-sb-leader=true | grep ovn-central | head -n 1 | awk '{print $1}'
    
    • 1
    • 2

    然后进入到 leader pod 中执行

    kubectl exec "$OVN_NB_POD" -n $KUBE_OVN_NS -c ovn-central -- ovn-nbctl "$@"
    kubectl exec "$OVN_SB_POD" -n $KUBE_OVN_NS -c ovn-central -- ovn-sbctl "$@"
    
    • 1
    • 2

    即查看流表命令 ovn-sbctl lflow-list == kubectl ko sbctl lflow-list

    Trace

    展示流表匹配流程
    Kubectl ko trace {namespace/podname} {target ip address} [target mac address] {icmp|tcp|udp} [target tcp or udp port]

    • namespace/podname 选定 pod
    • Target ip address 访问的 IP 地址
    • Target mac address 访问的 Mac 地址
    • 协议类型,icmp/tcp/udp
    • Target tcp or udp port,tcp 或 udp 端口号
    kubectl ko trace default/pod8 10.96.0.10 udp 53
    
    // 即 trace pod8 访问 10.96.0.10 的 udp 53 端口
    
    • 1
    • 2
    • 3
    • Ko 背后逻辑
      先找到 ovn-sb-leader $OVN_SB_POD

    ls: 通过 pod annotation 获取到的 logical_switch
    Inport:pod 的 logical_switch_port 的命名
    Mac:通过 annotation 获取到的 mac
    gwMac:通过 ovn 查询 logical_route_port 看 mac (underlay 模式,需要访问网关获取)
    af:4 or 6
    type:tcp or udp

    kubectl exec "$OVN_SB_POD" -n $KUBE_OVN_NS -c ovn-central -- ovn-trace --ct=new "$ls" "inport == \"$podName.$namespace\" && ip.ttl == 64 && eth.src == $mac && ip$af.src == $podIP && eth.dst == $gwMac && ip$af.dst == $dst && $type.src == 10000 && $type.dst == $4"
    
    kubectl exec ovn-central-7f7c579877-qlkbb -n kube-system -c ovn-central -- ovn-trace --ct=new ovn-default "inport == \"pod8.default\" && ip.ttl == 64 && eth.src == 00:00:00:1B:3E:8B && ip4.src == 10.16.3.47 && eth.dst == 00:00:00:63:B3:F9 && ip4.dst == 10.96.0.10 && udp.src == 10000 && udp.dst == 53" 
    
    
    # udp,reg14=0x1e,vlan_tci=0x0000,dl_src=00:00:00:1b:3e:8b,dl_dst=00:00:00:63:b3:f9,nw_src=10.16.3.47,nw_dst=10.96.0.10,nw_tos=0,nw_ecn=0,nw_ttl=64,tp_src=10000,tp_dst=53
    
    ingress(dp="ovn-default", inport="pod8.default")
    ------------------------------------------------
     0. ls_in_port_sec_l2 (northd.c:5607): inport == "pod8.default", priority 50, uuid 59013590
        next;
     6. ls_in_pre_lb (northd.c:6002): ip4 && ip4.dst == 10.96.0.0/12, priority 100, uuid 95834334
        reg0[2] = 1;
        next;
     7. ls_in_pre_stateful (northd.c:6042): reg0[2] == 1 && ip4 && udp, priority 120, uuid b6ecaf0b
        reg1 = ip4.dst;
        reg2[0..15] = udp.dst;
        ct_lb_mark;
    
    ct_lb_mark
    ----------
     8. ls_in_acl_hint (northd.c:6119): ct.new && !ct.est, priority 7, uuid 325fce8b
        reg0[7] = 1;
        reg0[9] = 1;
        next;
     9. ls_in_acl (northd.c:6713): ip && (!ct.est || (ct.est && ct_mark.blocked == 1)), priority 1, uuid 6a459a10
        reg0[1] = 1;
        next;
    12. ls_in_lb (northd.c:7028): ct.new && ip4.dst == 10.96.0.10 && udp.dst == 53, priority 120, uuid fba6a6c7
        reg0[1] = 0;
        reg1 = 10.96.0.10;
        reg2[0..15] = 53;
        ct_lb_mark(backends=10.16.0.48:53,10.16.0.5:53);
    
    ct_lb_mark /* default (use --ct to customize) */
    ------------------------------------------------
    14. ls_in_after_lb (northd.c:7165): ip4.dst == 10.16.0.5, priority 50, uuid 92e4c3a1
        eth.dst = 00:00:00:3e:8e:4f;
        next;
    16. ls_in_pre_hairpin (northd.c:7253): ip && ct.trk, priority 100, uuid 2478265d
        reg0[6] = chk_lb_hairpin();
        reg0[12] = chk_lb_hairpin_reply();
        *** chk_lb_hairpin_reply action not implemented
        next;
    25. ls_in_l2_lkup (northd.c:8674): eth.dst == 00:00:00:3e:8e:4f, priority 50, uuid 49fad5a5
        outport = "coredns-6d8c4cb4d-6c4rb.kube-system";
        output;
    
    egress(dp="ovn-default", inport="pod8.default", outport="coredns-6d8c4cb4d-6c4rb.kube-system")
    ----------------------------------------------------------------------------------------------
     0. ls_out_pre_lb (northd.c:6012): ip, priority 100, uuid dfdb1c2b
        reg0[2] = 1;
        next;
     2. ls_out_pre_stateful (northd.c:6062): reg0[2] == 1, priority 110, uuid 953b71bb
        ct_lb_mark;
    
    ct_lb_mark /* default (use --ct to customize) */
    ------------------------------------------------
     3. ls_out_acl_hint (northd.c:6182): ct.est && ct_mark.blocked == 0, priority 1, uuid bc0f3aca
        reg0[10] = 1;
        next;
     9. ls_out_port_sec_l2 (northd.c:5704): outport == "coredns-6d8c4cb4d-6c4rb.kube-system", priority 50, uuid c717b137
        output;
        /* output to "coredns-6d8c4cb4d-6c4rb.kube-system", type "" */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    稍后会结合功能讲解流表,这里不再赘述

    Ovn 流表

    流表示例如下:

    Datapath: "join" (73d52ffc-620d-4fc5-b6b3-19becad7623c)  Pipeline: ingress
      table=0 (ls_in_port_sec_l2  ), priority=100  , match=(eth.src[40]), action=(drop;)
      table=0 (ls_in_port_sec_l2  ), priority=100  , match=(vlan.present), action=(drop;)
      table=0 (ls_in_port_sec_l2  ), priority=50   , match=(inport == "join-ovn-cluster"), action=(next;)
      table=0 (ls_in_port_sec_l2  ), priority=50   , match=(inport == "node-master"), action=(next;)
      table=0 (ls_in_port_sec_l2  ), priority=50   , match=(inport == "node-worker"), action=(next;)
      table=0 (ls_in_port_sec_l2  ), priority=50   , match=(inport == "node-worker2"), action=(next;)
      table=1 (ls_in_port_sec_ip  ), priority=0    , match=(1), action=(next;)
      table=2 (ls_in_port_sec_nd  ), priority=0    , match=(1), action=(next;)
      table=3 (ls_in_lookup_fdb   ), priority=0    , match=(1), action=(next;)
      table=4 (ls_in_put_fdb      ), priority=0    , match=(1), action=(next;)
      table=5 (ls_in_pre_acl      ), priority=110  , match=(eth.dst == $svc_monitor_mac), action=(next;)
      ......
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    Datapath:logical_router or logical_switch
    Pipeline:ingress or egress,代表流方向。 Ovn 流水线 pipleline 的方式处理
    table=0(ls_in_port_sec_l2 ):table 索引和名称
    priority:在该 table 里的优先级,越大越高
    match:匹配规则
    action:操作

    Ovs 流表查看:kubectl ko ofctl master dump-flows br-int

    Match

    • eth.src[40] 源地址广播/组播

    广播/组播 源 mac 为 xxxxxxx1 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx,广播是特殊的组播包为全1,像 01:02:03:04:05:06 就是组播包源 mac;
    match=(eth.src[40]), action=(drop;) 第一条意思就是 组播包丢弃

    • vlan.present 带 vlan 的包
      match=(vlan.present), action=(drop;) 带 vlan 丢弃

    • inport == “node-master” 从 node-master port 进来的包

    Node-master 是 logical_switch_port

    # kubectl ko vsctl master list Interface  | grep -C 20 ovn0 // 在 master 看 ovs port
    // 缩略
    external_ids        : {iface-id=node-master, ip="100.64.0.2,dd:100:64::2", ovn-installed="true", ovn-installed-ts="1663729450921"}
    mac_in_use          : "00:00:00:21:85:b9"
    mtu                 : 1400
    name                : ovn0
    type                : internal
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    可以看到 在 master 节点 ovn0 在 ovs 上的配置,iface-id 即 lsp 标记。
    match=(inport == “node-master”), action=(next;) 从 node-master 进来的包送到下一张表。

    • outport == “coredns-6d8c4cb4d-6c4rb.kube-system” 从 lsp 出去的包

    • ip4.dst == 100.64.0.2 / ip6.dst == dd: 100:64::2 / eth.dst == 00:00:00:21:85:b9 / ip6.src == dd: 100:64::/64 / ip4.src == 127.0.0.0/8 / ip4.src == {10.16.0.1, 10.16.255.255} / icmp4.type == 8 && icmp4.code == 0 / icmp6.type == 128 && icmp6.code == 0 / ip4 / ip6 / arp/ arp.tpa == 100.64.0.1 / arp.op == 1

      • ipv4 or ipv6 的源,目的网段
      • 源或目的 mac 地址
      • ipv4 or ipv6 的地址范围
      • icmp4.type == 8 && icmp4.code == 0 icmp4 request;icmp6.type == 128 && icmp6.code == 0 icmp6 request。
      • 单纯只写 ip4 or ip6
      • Arp
      • arp.tpa or arp.op
        arp.op:ARP请求(1),ARP应答(2),RARP请求(3),RARP应答(4)。
        arp.tpa:target protocol address 目标 IP 地址
        unsigned char arp_sha[6]; /* sender hardware address /
        unsigned long arp_spa; /
        sender protocol address /
        unsigned char arp_tha[6]; /
        target hardware address /
        unsigned long arp_tpa; /
        target protocol address */
    • nd || nd_rs || nd_ra || mldv1 || mldv2

      • ND 邻居发现(Neighbor Discovery)
      • RS 路由请求(Router Soliciation)
      • RA 路由回答(Router Adertisment)
      • mldv1 || mldv2 MulticastListenerDiscovery 协议 IPv6组播
    • reg0[2] == 1 && ip4 && tcp
      reg0: 寄存器0[2] 是 1 且 是 ipv4 tcp。

    • ct.new, ct.est,ct.trk,ct.rel,ct.rpl

    ct:ovs 的 conntrack 功能增加了 ct 流表的概念,将需要跟踪状态的报文提交进 ct 里去,标记连接状态,供后续报文查询连接状态使用。
    参考:http://www.openvswitch.org//support/dist-docs/ovs-fields.7.txt

    new:数据包来自一个新的连接
    est:数据包来自一个已经建立的连接
    rel:数据包与一个已存在的连接相关联
    rpl:数据包来自一个连接的回复方向
    ink:数据包状态是无效的
    trk:数据包已经过 conntrack

    Action

    • Drop 丢弃
    • Next 到下一张 table
    • Output
    Datapath: "join" (73d52ffc-620d-4fc5-b6b3-19becad7623c)  Pipeline: ingress
    ...
    
    table=25(ls_in_l2_lkup      ), priority=50   , match=(eth.dst == 00:00:00:21:85:b9), action=(outport = "node-master"; output;)
    
    • 1
    • 2
    • 3
    • 4

    示例流表即到 join 上的流,如果目的 mac 是 master 节点的 ovn0 网卡的 mac,就从 node-master 这个 lsp 丢出。

    • 其他
      • ct_lb_mark / ct_lb_mark(backends=10.16.0.48:9153,10.16.0.5:9153) 连接 LB 跟踪器
      • ct_snat / (ct_snat(10.16.0.1); ) 源 nat 成 10.16.0.1
      • (arp { eth.dst = ff:ff:ff:ff:ff:ff; arp.spa = reg1; arp.tpa = reg0; arp.op = 1; output; }; )
        构造 arp 请求包。
      • action=(reg0 = 0; handle_dhcpv6_reply;) 向代理路由器发送 IPv6 前缀代理消息
      • (ip4.dst <-> ip4.src; ip.ttl = 255; icmp4.type = 0; flags.loopback = 1; next; )
        回复 icmp4
      • (reg9[2] = lookup_arp(inport, arp.spa, arp.sha); next;)
        reg9[2] will be 1 if the lookup_arp in the previous table was successful or skipped, meaning no need to learn mac binding from the packet
        这里是标记 reg9[2] 的意思。
      • (reg0[6] = chk_lb_hairpin(); reg0[12] = chk_lb_hairpin_reply(); next;)
        检查 lb_hairpin,标记 reg0[6] 和 reg0[12],主要是判断 访问者本身就是 endpoint,经过 lb 后源 和 目的 IP 相同;后面看 hairping LB 再详细介绍。

    Register

    ovs 寄存器,当一个数据包进入交换机时,所有的寄存器都被清零,用户可以通过 Action 的指令修改寄存器中的值。
    一般有 reg(32 bits,reg0-reg15),xreg(64 bits,xreg0-xreg7),xxreg(128 bits,xxreg0-xxreg3);

    xxreg ipv6 广泛使用

    路由时lb 时Arp or ipv6 ND其他
    reg0下一跳 ipv4 地址reg0[11],查询是否 inport 是否在 mac learning tablereg0[1] :是否需要 tracker 流 for acl;reg0[2]:是否需要 tracker 流 for LB;reg0[3]:dhcp 配置是否获取成功;reg0[4]:dns 是否解析成功;reg0[5]:IPv6 RA 配置是否获取成功;reg0[6] 和reg0[12]:一个是 检查 lb_hairpin,一个是检查 lb_hairpin_reply。reg0[7]:acl 可能 allow,且流是被追踪的;reg0[8]:acl 可能 allow,且流是未被追踪的;reg0[9]:acl 可能 drop;reg0[10]:acl 可能是 drop,且需要改 tracker;reg0[13]:stateful 流
    reg1网关 ipv4 地址,所选择的 router_port IPv4 地址。vip
    reg2前 16 位:vip_port;transport port
    reg7从 Logical Router Port 来的流量在 lr_in_ip_routing_pre table 中设置为 0;
    reg8前 16 位:ecmp group id;后 16 位:ecmp number id
    reg9reg9[16…31]:目的端口,LB 的 DNAT 会通过此值获得端口。 (不止 lb,发往 ovs contrack 的所有)去 OVN_Southbound mac_binding 表查询;reg9[2] = 1,代表已有或者 不需要 mac_learn。

    具体使用介绍 https://github.com/ovn-org/ovn/blob/main/northd/ovn-northd.8.xml

    Table 介绍

    分为 logical switch 和 logical router 表,且 ingress 和 egress 不同。

    Logical Switch Datapaths

    • Ingress
      table=0 (ls_in_port_sec_l2 ):准入控制
      • 如之前所说的带 vlan 或 组播包丢弃
      • 如 match=(inport == “pod8.default”), action=(next;) 允许 pod8 的流量进入

    table=1 (ls_in_port_sec_ip ):准入控制

    • 如 inport == “kata2.default” && eth.src == 00:00:00:fb:cc:14 && ip4.src == {192.166.100.5}), action=(next;) 要求 kata2 进来的包 mac 和 ip 匹配到到下一个表 (匹配不到会有 drop)

    table=5/6 (ls_in_pre_acl/ls_in_pre_lb): prepares flows acl 和 lb

    • 如 match=(ip4 && ip4.dst == 10.96.0.0/12), action=(reg0[2] = 1; next;)目的地址到 10.96.0.0/12 网段的流量标记 reg0[2]
      table=7 (ls_in_pre_stateful):prepares flows for 有状态流
    • 如 match=(reg0[2] == 1 && ip4 && udp), action=(reg1 = ip4.dst; reg2[0…15] = udp.dst; ct_lb_mark;) reg[0] == 1 就是上面 lb 标记的流量,加上匹配 udp/tcp/sctp 过滤后,reg1 存目的地址,reg2[0…15] 16 存端口号

    table=8 (ls_in_acl_hint):设置 hints,为后续处理设置一些提示

    • 如 match=(ct.new && !ct.est), action=(reg0[7] = 1; reg0[9] = 1; next;) 如果 conntrack 是一个新连接,则标记 reg0[7],[9];并不是为了判断 ct 状态,而是不同 ct 状态有不同的 acl 不需要匹配。

    table=9 (ls_in_acl):acl

    • 如配置安全组后流表 match=(reg0[9] == 1 && (inport==@ovn.sg.sg1 && ip4 && ip4.dst==0.0.0.0/0 && icmp4)), action=(/* drop */) 从 sg1 安全组的端口发出的 icmp 包 被drop 掉。
      注:在 ls inport 等于实际 pod 或 vm 的 egress 策略

    table=10/11 (ls_in_qos_mark/ls_in_qos_meter):nbctl 里的 qos table,关于 dscp marking。未使用

    table=12 (ls_in_lb):LB 规则

    • 如 match=(ct.new && ip4.dst == 10.96.0.10 && tcp.dst == 53), action=(reg0[1] = 0; reg1 = 10.96.0.10; reg2[0…15] = 53; ct_lb_mark(backends=10.16.0.48:53,10.16.0.5:53); ) 设置寄存器并 ct_lb 处理。
      对应 service 如下:
      Name:              kube-dns
      Namespace:         kube-system
      Selector:          k8s-app=kube-dns
      Type:              ClusterIP
      IP Family Policy:  SingleStack
      IP Families:       IPv4
      IP:                10.96.0.10
      IPs:               10.96.0.10
      Port:              dns  53/UDP
      TargetPort:        53/UDP
      Endpoints:         10.16.0.48:53,10.16.0.5:53
      Port:              dns-tcp  53/TCP
      TargetPort:        53/TCP
      Endpoints:         10.16.0.48:53,10.16.0.5:53
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    table=13 (ls_in_acl_after_lb):配置 acl 时选项 --apply-after-lb,字面意思,未使用
    table=14 (ls_in_after_lb):after lb 的操作

    • 如 match=(ip4.dst == 10.16.4.138), action=(eth.dst = 00:00:00:c6:a4:f9; next;) 目的 IP 为 10.16.4.138,目的 mac 设为 00:00:00:c6:a4:f9

    table=16 (ls_in_pre_hairpin) 配置 lb 后,如果 匹配 ip && ct.trk(ct 跟踪),则需要 hairpinned

    • 如 match=(ip && ct.trk), action=(reg0[6] = chk_lb_hairpin(); reg0[12] = chk_lb_hairpin_reply(); next;);

    table=17 (ls_in_nat_hairpin):需要 hairpin 的,snat 操作

    • 如 match=(ip && ct.new && ct.trk && reg0[6] == 1), action=(ct_snat_to_vip; next;) action 为 ct_snat_to_vip

    table=18 (ls_in_hairpin):hairpin 操作

    • 如 match=((reg0[6] == 1 || reg0[12] == 1)), action=(eth.dst <-> eth.src; outport = inport; flags.loopback = 1; output;) ,源目的 mac 互换,output 直接设置为 input,然后返回

    table=19 (ls_in_arp_rsp):arp 回复流表

    • 如 match=(arp.tpa == 192.168.2.1 && arp.op == 1), action=(eth.dst = eth.src; eth.src = 00:00:00:c1:89:90; arp.op = 2; /* ARP reply */ arp.tha = arp.sha; arp.sha = 00:00:00:c1:89:90; arp.tpa = arp.spa; arp.spa = 192.168.2.1; outport = inport; flags.loopback = 1; output;),如果请求 192.168.2.1 的 mac,action 目的 mac 设为请求包的源 mac,源 mac 设为 00:00:00:c1:89:90,output 设为 inport。然后发出完成 arp 回复

    table=20 (ls_in_dhcp_options):获取 dhcp 配置,对应 subnet 创建时的 dhcpOptions 的配置。

    • 如 match=(inport == “pod9.default” && eth.src == 00:00:00:08:7e:05 && ip4.src == 0.0.0.0 && ip4.dst == 255.255.255.255 && udp.src == 68 && udp.dst == 67), action=(reg0[3] = put_dhcp_opts(offerip = 192.168.2.16, lease_time = 3600, netmask = 255.255.255.0, router = 192.168.2.1, server_id = 169.254.0.254); next;),如果是 pod9,源 mac 为 00:00:00:08:7e:05,源 0.0.0.0/68,目的 255.255.255.255/67,获取到 action 中的 dhcp 配置

    table=21 (ls_in_dhcp_response):dhcp 回复

    • 如 match=(inport == “pod9.default” && eth.src == 00:00:00:08:7e:05 && ip4 && udp.src == 68 && udp.dst == 67 && reg0[3]), action=(eth.dst = eth.src; eth.src = 00:00:02:2E:2F:B8; ip4.src = 169.254.0.254; udp.src = 67; udp.dst = 68; outport = inport; flags.loopback = 1; output;),匹配条件加上了 table=20 action 中的 reg0[3] 的标识;然后构造 dhcp 回复报文发回。

    table=22/23 (ls_in_dns_lookup/ls_in_dns_response):查询 dns 配置和回复 dns 请求,未使用

    table=24 (ls_in_external_port):从 external logical ports,如 localnet 来的流,未使用

    table=25 (ls_in_l2_lkup):实际的交换行为

    • 如 match=(eth.dst == 00:00:00:08:7e:05), action=(outport = “pod9.default”; output;),目的 mac 是 00:00:00:08:7e:05 从 pod9 的 lsp 发出

    table=26 (ls_in_l2_unknown):未知流的处理

    • 如 match=(outport == “none”), action=(drop;),未设置 output 的流,drop。

    • Egress
      table=0 (ls_out_pre_lb):与 ingress ls_in_pre_lb 类似

      • 如 match=(ip && outport == “subnet-ovn-cluster”), action=(next;),output 为到 router port,next
        table=1-9 (ls_out_pre_acl,ls_out_pre_stateful,ls_out_acl_hint,ls_out_acl,ls_out_stateful,ls_out_port_sec_ip,ls_out_port_sec_l2)与 ingress 对应流表功能类似

    Logical Router Datapaths

    • ingress
      table=0 (lr_in_admission):准入控制
      • 如 match=(vlan.present || eth.src[40]), action=(drop;) vlan 或组播丢弃

    table=1 (lr_in_lookup_neighbor):arp 和 ipv6 nd,判断是否需要学习 mac binding。如果已有不需要学习,设置 reg9[2]。

    • 如 match=(inport == “ovn-cluster-subnet” && arp.spa == 192.168.2.0/24 && arp.op == 1), action=(reg9[2] = lookup_arp(inport, arp.spa, arp.sha); next;) subnet 上发出的 arp 请求,如果已记录的进行 reg9[2] 设置。

    table=2 (lr_in_learn_neighbor):判断是否 reg9[2] == 1,如果不为 1,则需要记录 mac binding。

    • priority=100 , match=(reg9[2] == 1), action=(next;)
    • priority=90 , match=(arp), action=(put_arp(inport, arp.spa, arp.sha); next;)
    • reg9[2] 不为 1 时,learn mac binding

    table=3 (lr_in_ip_input):logical_router 核心表,基础功能。

    • 如 match=(ip4.dst == 192.168.2.1 && icmp4.type == 8 && icmp4.code == 0), action=(ip4.dst <-> ip4.src; ip.ttl = 255; icmp4.type = 0; flags.loopback = 1; next; ),ping 网关,lr 回复的流表

    ipv6 icmp:match=(ip6.dst == {dd:10:16::1, fe80::200:ff:fe63:b3f9} && icmp6.type == 128 && icmp6.code == 0), action=(ip6.dst <-> ip6.src; ip.ttl = 255; icmp6.type = 129; flags.loopback = 1; next; )

    • 如 match=(ip4.dst == {192.168.2.1}), action=(drop;),如果到网关的其他流量 drop。

    table=4(lr_in_unsnat):egress 时做了 snat,那么回包需要取消之前的 snat

    table=5(lr_in_defrag):将数据包发送到连接跟踪器进行跟踪,如在 lr 上的 三层,四层 lb

    kubectl ko nbctl lb-add lb0 10.96.99.99:8080 10.16.0.10:80,10.16.0.15:80,10.16.0.2:80 tcp
    kubectl ko nbctl lr-lb-add ovn-cluster lb0

    • 如 match=(ip && ip4.dst == 10.96.99.99 && tcp), action=(reg0 = 10.96.99.99; reg9[16…31] = tcp.dst; ct_dnat;),如果匹配到 lb vip,reg0 存 vip 地址,reg9[16…31] 存目的 port (8080),然后 ct_dnat 进行处理。

    table=6(lr_in_dnat):数据包 dnat 处理

    • 如 match=(ct.new && ip4 && reg0 == 10.96.99.99 && tcp && reg9[16…31] == 8080), action=(ct_lb_mark(backends=10.16.0.10:80,10.16.0.15:80,10.16.0.2:80); ),匹配到 table5 的标记后,ct_lb_mark 后端几个 endpoint。

    table=7(lr_in_ecmp_stateful):ECMP对称应答处理

    table=8(lr_in_nd_ra_options):IPv6 ND RA 配置处理

    • 如 match=(inport == “ovn-cluster-subnet-dual” && ip6.dst == ff02::2 && nd_rs), action=(reg0[5] = put_nd_ra_opts(addr_mode = “dhcpv6_stateful”, slla = 00:00:00:37:30:f1, prefix = bb00::/64); next;) ,其中 subnet-dual 是个在 ovn-cluster vpc 里的双栈子网,从 subnet-dual port 来的流量,目的 mac 是 ff02::2(rs 报文目的 mac),回复 dhcp,mode 为 dhcpv6_stateful,前缀 bb00::/64。

    table=9(lr_in_nd_ra_response) :IPv6 RA 回复

    • 如 match=(inport == “ovn-cluster-subnet-dual” && ip6.dst == ff02::2 && nd_ra && reg0[5]), action=(eth.dst = eth.src; eth.src = 00:00:00:37:30:f1; ip6.dst = ip6.src; ip6.src = fe80::200:ff:fe37:30f1; outport = inport; flags.loopback = 1; output;),对应上条 table 进行回复。

    table=10(lr_in_ip_routing_pre):路由转发前配置,通常为 logical_route_port 带 options:route_table 配置的使用,未使用

    table=11(lr_in_ip_routing):路由转发

    • 如 match=(ip4.dst == 192.168.2.0/24), action=(ip.ttl–; reg8[0…15] = 0; reg0 = ip4.dst; reg1 = 192.168.2.1; eth.src = 00:00:00:c1:89:90; outport = “ovn-cluster-subnet”; flags.loopback = 1; next;),目的地址是 lr 下的子网,ttl 减1,设置寄存器,设置源 mac 为网关(192.168.2.1 lrp)的 mac,设置 outport 为 lrp。

    table=12(lr_in_ip_routing_ecmp):ECMP 路由

    table=13(lr_in_policy):策略路由

    • 如 match=(ip4.src == $subnet.master_ip4), action=(reg0 = 100.64.0.2; reg1 = 100.64.0.1; eth.src = 00:00:00:e9:15:ff; outport = “ovn-cluster-join”; flags.loopback = 1; reg8[0…15] = 0; next;),如果源地址是 master 节点上 subnet 子网上的 port,源 mac 改为 join 网络网关的 mac,然后 outport 设置为 join 子网的网关 port。

    由此可以看出先匹配静态路由,再匹配策略路由,静态路由会先设置 outport,但不会发出,然后过策略路由表,策略路由匹配上会更改 outport。

    table=14(lr_in_policy_ecmp):ECMP 策略路由

    table=15(lr_in_arp_resolve):arp/nd 决定,根据之前路由设置的 outport 和 寄存器 reg0 存的 ip 设置目的 mac

    • 如 match=(outport == “ovn-cluster-join” && reg0 == 100.64.0.2), action=(eth.dst = 00:00:00:21:85:b9; next;),匹配到上面 table13 的流量,设置目的 mac 为 100.64.0.2 的 mac,即 master 节点上 ovn0 的 mac。

    table=16(lr_in_chk_pkt_len):检查包长度,在给 gw_port 设置 options:gateway_mtu 时生效。

    table=17(lr_in_larger_pkts):同上,为从 logical_router_port 到 gw_port 的流量使用。

    table=18(lr_in_gw_redirect):分布式逻辑路由中,当 logical_router_port 设置 chassises 时使用。

    table=19(lr_in_arp_request):如果 目的 mac 未确定,通过 reg0 和 reg1 存的 ip 地址进行 arp 请求;如果确定 mac,则发出。

    • match=(eth.dst == 00:00:00:00:00:00 && ip4), action=(arp { eth.dst = ff:ff:ff:ff:ff:ff; arp.spa = reg1; arp.tpa = reg0; arp.op = 1; output; }; )

    • match=(1), action=(output;)

    • Egress
      table=0(lr_out_chk_dnat_local):检查是否需要 DNAT,通常与 ingress 中 SNAT 对应
      table=1(lr_out_undnat):已经建立连接后的流是否做 dnat,不如创建 lr-lb,就会多出下面流表

      • match=(ip), action=(flags.loopback = 1; ct_dnat;),去 ct_dnat 里检查
        table=2(lr_out_post_undnat):相对于上一张表,如果是新连接 ct_commit。
        table=3(lr_out_snat):查询 LR Snat 的配置
        table=4(lr_out_post_snat):执行 Snat,通常设置 force_snat_for_lb 时使用,后续有机会介绍。
        table=5(lr_out_egr_loop):分布式 LR,且一个 LRP 配置了 gw chassis 时使用。
        table=6(lr_out_delivery):发送。
      • match=(outport == “ovn-cluster-join”), action=(output;),outport + output

    参考文档:https://www.mankier.com/8/ovn-northd

  • 相关阅读:
    基于PyQt5和OpenCV库的简单的文档对齐扫描应用程序
    Cookie、Session、Token的关系和区别
    【vue】el-carousel实现视频自动播放与自动切换到下一个视频功能:
    C++中的深拷贝和浅拷贝
    MySQL索引常见面试题(2022版)
    GroundingDINO(一种开集目标检测算法)服务化,根据文本生成检测框
    One bite of Stream(8)
    python自动更新pom文件
    vue中如何全屏显示系统内容
    手把手带你编写一个规范的字符设备驱动
  • 原文地址:https://blog.csdn.net/mr1jie/article/details/127914644