Hi Ihar, Thanks for your inputs. I think I have found the issue in the test. In the test case I had missed one thing. Below are the details
The log which you shared as the last message in ovs-vswitchd log led me to it. > 2022-11-15T16:31:05.280Z|00541|ofproto_dpif_xlate|WARN|over max > translation depth 64 on bridge br-int while processing > arp,in_port=LOCAL,vlan_tci=0x0000,dl_src=00:00:00:01:02:00,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=192.168.1.11,arp_tpa=192.168.1.12,arp_op=1,arp_sha=00:00:00:01:02:00,arp_tha=00:00:00:00:00:00 What I had missed is, the creation of hv2, setting ip to br-phys and ARP resolving. (See code at the end for what I have added). Once I added this now everything is passing fine. I don't see any failures. Also everything works in a single test case. So, I will revert back to the old method of having 1 bulk updates test case and verify all cases there and with below code, will submit a patch soon. Since in the bulk updates test case we were not verifying the actual packet flow (already covered in a separate test) I had thought the below code was not needed. But that wasn't the case! FYI, what I added now... net_add n2 sim_add hv2 as hv2 ovs-vsctl add-br br-phys -- set bridge br-phys other-config:hwaddr=\"00:00:00:02:02:00\" ovn_attach n2 br-phys 192.168.1.12 OVN_POPULATE_ARP Thanks & Regards, Abhiram R N On Wed, Nov 16, 2022 at 4:39 AM Ihar Hrachyshka <ihrac...@redhat.com> wrote: > On 11/15/22 5:42 PM, Ihar Hrachyshka wrote: > > I think there's a problem with the bulk tests added in this patch. I > > will cover this issue in this email, and I'll send my code review > > tomorrow as promised, since it's getting late here. > > > > > > When running the whole suite locally, I get the following failures: > > > > > > 401: Mirror test bulk swap attachments -- ovn-northd -- > > parallelization=no -- ovn_monitor_all=yes FAILED (ovn.at:16425) > > 402: Mirror test bulk swap attachments -- ovn-northd -- > > parallelization=no -- ovn_monitor_all=no FAILED (ovn.at:16425) > > 403: Mirror test bulk attach multiple -- ovn-northd -- > > parallelization=yes -- ovn_monitor_all=yes FAILED (ovn.at:16537) > > 410: Mirror test bulk more detach and less attach -- ovn-northd -- > > parallelization=no -- ovn_monitor_all=no ok > > 412: Mirror test bulk attach more than detach -- ovn-northd -- > > parallelization=yes -- ovn_monitor_all=no ok > > 416: Mirror test bulk detach multiple -- ovn-northd -- > > parallelization=yes -- ovn_monitor_all=no ok > > 408: Mirror test bulk more detach and less attach -- ovn-northd -- > > parallelization=yes -- ovn_monitor_all=no FAILED (ovn.at:16650) > > 417: Mirror test bulk detach multiple -- ovn-northd -- > > parallelization=no -- ovn_monitor_all=yes ok > > 409: Mirror test bulk more detach and less attach -- ovn-northd -- > > parallelization=no -- ovn_monitor_all=yes FAILED (ovn.at:16650) > > 411: Mirror test bulk attach more than detach -- ovn-northd -- > > parallelization=yes -- ovn_monitor_all=yes FAILED (ovn.at:16766) > > 418: Mirror test bulk detach multiple -- ovn-northd -- > > parallelization=no -- ovn_monitor_all=no ok > > 413: Mirror test bulk attach more than detach -- ovn-northd -- > > parallelization=no -- ovn_monitor_all=yes FAILED (ovn.at:16766) > > 415: Mirror test bulk detach multiple -- ovn-northd -- > > parallelization=yes -- ovn_monitor_all=yes FAILED (ovn.at:16879) > > 414: Mirror test bulk attach more than detach -- ovn-northd -- > > parallelization=no -- ovn_monitor_all=no FAILED (ovn.at:16766) > > > > > > Note that some of the test case variants passed, and I don't think > > there's a clear pattern as to which of variants in the test matrix do > > fail. > > > > > > The error that triggers the failure is during ovs-vswitchd cleanup: > > > > > > ./ovn.at:16425: ovs-appctl --timeout=10 -t ovs-vswitchd exit --cleanup > > --- /dev/null 2022-11-04 04:09:25.869645998 +0000 > > +++ /home/vagrant/ovn/tests/testsuite.dir/at-groups/401/stderr > > 2022-11-15 16:31:15.557479369 +0000 > > @@ -0,0 +1,2 @@ > > +2022-11-15T16:31:15Z|00001|fatal_signal|WARN|terminating with signal > > 14 (Alarm clock) > > +/home/vagrant/ovn/tests/testsuite.dir/at-groups/401/test-source: line > > 282: 1033659 Alarm clock ovs-appctl --timeout=10 -t > > ovs-vswitchd exit --cleanup > > ./ovn.at:16425: exit code was 142, expected 0 > > > > > > The very last message in ovs-vswitchd log on hv1 is exactly 10 seconds > > before the alarm clock error: > > > > > > 2022-11-15T16:31:05.280Z|00541|ofproto_dpif_xlate|WARN|over max > > translation depth 64 on bridge br-int while processing > > > arp,in_port=LOCAL,vlan_tci=0x0000,dl_src=00:00:00:01:02:00,dl_dst=ff:ff:ff:ff:ff:ff,arp_spa=192.168.1.11,arp_tpa=192.168.1.12,arp_op=1,arp_sha=00:00:00:01:02:00,arp_tha=00:00:00:00:00:00 > > > > > > I don't see coredumps generated for any of test processes, so it's > > probably not the case of ovs-vswitchd crashing on exit request. > > > > > > I tried to adjust your test cases to a minimal reproducer and I found > > that if a test case creates two mirrors, both of to-lport type, then > > ovs-vswitchd freezes (?) - f.e. it no longer responds to appctl > > requests, nor it handles new ports. But if I merely change the type of > > one of mirrors in the test to from-lport, the test passes. > > > > > > On the other hand, a consistent way to trigger the failure is adding a > > 'sleep 3' at the end of a test case just before cleanup, apparently to > > allow vswitchd to catch on the mirror updates and lock somewhere in > > the code. I see vswitchd spinning at ~100% cpu in 'top' output when it > > gets into this state. It's clearly doing SOMETHING, not just sleeping. :) > > > > > > I suspect there's some bug inside vswitchd that makes it lock / spin > > for a particular setup of mirrors. Whatever OVN sets up in vswitchd > > database, the latter should not freeze. It would be helpful to provide > > a short ovs-only reproducer for the situation that would not involve > > OVN so that our OVS friends can take a look. > > > > > > For the record, the mirrors in ovsdb are: > > > > > > _uuid : 491d0282-5e03-417c-b8cf-57f72a9a4c81 > > external_ids : {} > > name : mirror0 > > output_port : 4d0865e8-85a6-42a9-a005-faaecd88fb1c > > output_vlan : [] > > select_all : false > > select_dst_port : [0a610fb2-86c2-4e0a-9611-788283b839ab, > > 7c3e23f4-d42b-457c-89e4-6ccb6e16baaf] > > select_src_port : [] > > select_vlan : [] > > snaplen : [] > > statistics : {} > > > > _uuid : ad391ceb-450a-45d2-9b43-665e2515a148 > > external_ids : {} > > name : mirror1 > > output_port : b1cecc0c-8290-448e-b21a-0b3df8724697 > > output_vlan : [] > > select_all : false > > select_dst_port : [17958da9-8bea-450e-982b-1842f6a8f6e6, > > 4cbc1c03-9e14-419e-81c0-9b1dc49fc528] > > select_src_port : [] > > select_vlan : [] > > snaplen : [] > > statistics : {} > > > > > > Bridge output here: > > > > > > 8d4b8f3c-41c0-4cf0-a10f-1f0420a93971 > > Bridge br-int > > fail_mode: secure > > datapath_type: system > > Port vif4 > > Interface vif4 > > Port vif2 > > Interface vif2 > > Port br-int > > Interface br-int > > type: internal > > Port vif1 > > Interface vif1 > > Port ovn-mirror0 > > Interface ovn-mirror0 > > type: gre > > options: {key="0", remote_ip="192.168.1.12"} > > Port vif3 > > Interface vif3 > > Port ovn-mirror1 > > Interface ovn-mirror1 > > type: gre > > options: {key="1", remote_ip="192.168.1.12"} > > Port patch-br-int-to-ln-public > > Interface patch-br-int-to-ln-public > > type: patch > > options: {peer=patch-ln-public-to-br-int} > > Bridge br-phys > > Port br-phys > > Interface br-phys > > type: internal > > options: > > > {rxq_pcap="/home/vagrant/ovn/tests/testsuite.dir/0401/hv1/br-phys-rx.pcap", > > tx_pcap="/home/vagrant/ovn/tests/testsuite.dir/0401/hv1/br-phys-tx.pcap"} > > Port br-phys_n1 > > Interface br-phys_n1 > > options: > > > {rxq_pcap="/home/vagrant/ovn/tests/testsuite.dir/0401/hv1/br-phys_n1-rx.pcap", > > > > stream="unix:/home/vagrant/ovn/tests/testsuite.dir/0401/main/hv1_br-phys.sock", > > > > tx_pcap="/home/vagrant/ovn/tests/testsuite.dir/0401/hv1/br-phys_n1-tx.pcap"} > > > > > Port patch-ln-public-to-br-int > > Interface patch-ln-public-to-br-int > > type: patch > > options: {peer=patch-br-int-to-ln-public} > > > Perhaps this may be of relevance: when I attach gdb to vswitchd process, > I can see a never-ending stack of calls in thread 1 that looks like: > > #0 0x0000000000406760 in memcpy@plt () > #1 0x0000000000499d88 in dp_packet_clone_with_headroom > (buffer=0x1c0d620, headroom=0) at lib/dp-packet.c:191 > #2 0x000000000049bd4d in dp_packet_batch_clone (dst=0x7fffcbf54740, > src=0x7fffcbf55230) at lib/dp-packet.h:863 > #3 0x00000000004b15ef in dp_execute_output_action (pmd=0x7f638115f010, > packets_=0x7fffcbf55230, should_steal=false, port_no=3) at > lib/dpif-netdev.c:8696 > #4 0x00000000004b19a9 in dp_execute_cb (aux_=0x7fffcbf55200, > packets_=0x7fffcbf55230, a=0x7fffcbf555c0, should_steal=false) at > lib/dpif-netdev.c:8787 > #5 0x0000000000507834 in odp_execute_actions (dp=0x7fffcbf55200, > batch=0x7fffcbf55230, steal=false, actions=0x7fffcbf55578, > actions_len=88, dp_execute_action=0x4b18e6 <dp_execute_cb>) > at lib/odp-execute.c:993 > #6 0x00000000004b251e in dp_netdev_execute_actions (pmd=0x7f638115f010, > packets=0x7fffcbf55230, should_steal=false, flow=0x7fffcbf55d60, > actions=0x7fffcbf55578, actions_len=88) > at lib/dpif-netdev.c:9105 > #7 0x00000000004a6354 in dpif_netdev_execute (dpif=0x17af630, > execute=0x7fffcbf55458) at lib/dpif-netdev.c:4557 > #8 0x00000000004a64d0 in dpif_netdev_operate (dpif=0x17af630, > ops=0x7fffcbf554a8, n_ops=1, offload_type=DPIF_OFFLOAD_AUTO) at > lib/dpif-netdev.c:4606 > #9 0x00000000004bb9e8 in dpif_operate (dpif=0x17af630, > ops=0x7fffcbf554a8, n_ops=1, offload_type=DPIF_OFFLOAD_AUTO) at > lib/dpif.c:1372 > #10 0x00000000004bb8e1 in dpif_execute (dpif=0x17af630, > execute=0x7fffcbf55500) at lib/dpif.c:1326 > #11 0x000000000043e46d in ofproto_dpif_execute_actions__ > (ofproto=0x17a8a10, version=7, flow=0x7fffcbf55d60, rule=0x0, > ofpacts=0x7fffcbf56000, ofpacts_len=16, depth=58, resubmits=1924, > packet=0x7fffcbf56050) at ofproto/ofproto-dpif.c:4294 > #12 0x0000000000467e8a in compose_table_xlate (ctx=0x7fffcbf5f240, > out_dev=0x1879430, packet=0x7fffcbf56050) at > ofproto/ofproto-dpif-xlate.c:3526 > #13 0x0000000000468020 in tnl_send_arp_request (ctx=0x7fffcbf5f240, > out_dev=0x1879430, eth_src=..., ip_src=184658112, ip_dst=201435328) at > ofproto/ofproto-dpif-xlate.c:3555 > #14 0x0000000000468710 in native_tunnel_output (ctx=0x7fffcbf5f240, > xport=0x1839bc0, flow=0x7fffcbf60930, tunnel_odp_port=7, truncate=false, > is_last_action=false) > at ofproto/ofproto-dpif-xlate.c:3721 > #15 0x000000000046a78e in compose_output_action__ (ctx=0x7fffcbf5f240, > ofp_port=7, xr=0x0, check_stp=true, is_last_action=false, > truncate=false) at ofproto/ofproto-dpif-xlate.c:4356 > #16 0x000000000046aa38 in compose_output_action (ctx=0x7fffcbf5f240, > ofp_port=7, xr=0x0, is_last_action=false, truncate=false) at > ofproto/ofproto-dpif-xlate.c:4416 > #17 0x00000000004653f4 in output_normal (ctx=0x7fffcbf5f240, > out_xbundle=0x18918d0, xvlan=0x7fffcbf57160) at > ofproto/ofproto-dpif-xlate.c:2533 > #18 0x0000000000464799 in mirror_packet (ctx=0x7fffcbf5f240, > xbundle=0x1880d00, mirrors=2) at ofproto/ofproto-dpif-xlate.c:2190 > #19 0x000000000046a96b in compose_output_action__ (ctx=0x7fffcbf5f240, > ofp_port=1, xr=0x0, check_stp=true, is_last_action=false, > truncate=false) at ofproto/ofproto-dpif-xlate.c:4396 > #20 0x000000000046aa38 in compose_output_action (ctx=0x7fffcbf5f240, > ofp_port=1, xr=0x0, is_last_action=false, truncate=false) at > ofproto/ofproto-dpif-xlate.c:4416 > #21 0x000000000046cf3e in xlate_output_action (ctx=0x7fffcbf5f240, > port=1, controller_len=0, may_packet_in=true, is_last_action=false, > truncate=false, group_bucket_action=false) > at ofproto/ofproto-dpif-xlate.c:5361 > #22 0x0000000000471069 in do_xlate_actions (ofpacts=0x1860638, > ofpacts_len=16, ctx=0x7fffcbf5f240, is_last_action=false, > group_bucket_action=false) at ofproto/ofproto-dpif-xlate.c:7026 > #23 0x000000000046ab3b in xlate_recursively (ctx=0x7fffcbf5f240, > rule=0x1860480, deepens=false, is_last_action=false, > actions_xlator=0x470caf <do_xlate_actions>) > at ofproto/ofproto-dpif-xlate.c:4439 > #24 0x000000000046b0de in xlate_table_action (ctx=0x7fffcbf5f240, > in_port=5, table_id=65 'A', may_packet_in=false, honor_table_miss=false, > with_ct_orig=false, is_last_action=false, > xlator=0x470caf <do_xlate_actions>) at > ofproto/ofproto-dpif-xlate.c:4568 > #25 0x000000000046bbd3 in xlate_ofpact_resubmit (ctx=0x7fffcbf5f240, > resubmit=0x185cfb8, is_last_action=false) at > ofproto/ofproto-dpif-xlate.c:4879 > #26 0x0000000000471796 in do_xlate_actions (ofpacts=0x185cfb8, > ofpacts_len=16, ctx=0x7fffcbf5f240, is_last_action=false, > group_bucket_action=false) at ofproto/ofproto-dpif-xlate.c:7173 > #27 0x000000000046ab3b in xlate_recursively (ctx=0x7fffcbf5f240, > rule=0x185ce00, deepens=false, is_last_action=false, > actions_xlator=0x470caf <do_xlate_actions>) > at ofproto/ofproto-dpif-xlate.c:4439 > #28 0x000000000046b0de in xlate_table_action (ctx=0x7fffcbf5f240, > in_port=5, table_id=64 '@', may_packet_in=false, honor_table_miss=false, > with_ct_orig=false, is_last_action=false, > xlator=0x470caf <do_xlate_actions>) at > ofproto/ofproto-dpif-xlate.c:4568 > #29 0x000000000046bbd3 in xlate_ofpact_resubmit (ctx=0x7fffcbf5f240, > resubmit=0x1834458, is_last_action=false) at > ofproto/ofproto-dpif-xlate.c:4879 > #30 0x0000000000471796 in do_xlate_actions (ofpacts=0x1834458, > ofpacts_len=16, ctx=0x7fffcbf5f240, is_last_action=false, > group_bucket_action=false) at ofproto/ofproto-dpif-xlate.c:7173 > > it goes like that over and over and over for hundreds if not thousands > of calls down the stack until > > #5738 0x000000000046b0de in xlate_table_action (ctx=0x7fffcc09b330, > in_port=65533, table_id=9 '\t', may_packet_in=false, > honor_table_miss=false, with_ct_orig=false, is_last_action=true, > xlator=0x470caf <do_xlate_actions>) at ofproto/ofproto-dpif-xlate.c:4568 > #5739 0x000000000046bbd3 in xlate_ofpact_resubmit (ctx=0x7fffcc09b330, > resubmit=0x1831ac0, is_last_action=true) at > ofproto/ofproto-dpif-xlate.c:4879 > #5740 0x0000000000471796 in do_xlate_actions (ofpacts=0x1831a68, > ofpacts_len=104, ctx=0x7fffcc09b330, is_last_action=true, > group_bucket_action=false) at ofproto/ofproto-dpif-xlate.c:7173 > #5741 0x000000000046ab3b in xlate_recursively (ctx=0x7fffcc09b330, > rule=0x18318b0, deepens=false, is_last_action=true, > actions_xlator=0x470caf <do_xlate_actions>) at > ofproto/ofproto-dpif-xlate.c:4439 > #5742 0x000000000046b0de in xlate_table_action (ctx=0x7fffcc09b330, > in_port=65533, table_id=8 '\b', may_packet_in=false, > honor_table_miss=false, with_ct_orig=false, is_last_action=true, > xlator=0x470caf <do_xlate_actions>) at ofproto/ofproto-dpif-xlate.c:4568 > #5743 0x000000000046bbd3 in xlate_ofpact_resubmit (ctx=0x7fffcc09b330, > resubmit=0x187bcc0, is_last_action=true) at > ofproto/ofproto-dpif-xlate.c:4879 > #5744 0x0000000000471796 in do_xlate_actions (ofpacts=0x187bc80, > ofpacts_len=80, ctx=0x7fffcc09b330, is_last_action=true, > group_bucket_action=false) at ofproto/ofproto-dpif-xlate.c:7173 > #5745 0x0000000000473b7a in xlate_actions (xin=0x7fffcc09c5c0, > xout=0x7fffcc09c910) at ofproto/ofproto-dpif-xlate.c:8033 > #5746 0x000000000043fac3 in packet_xlate (ofproto_=0x17c10e0, > opo=0x7fffcc09ce00) at ofproto/ofproto-dpif.c:4877 > #5747 0x00000000004250d7 in ofproto_packet_out_start (ofproto=0x17c10e0, > opo=0x7fffcc09ce00) at ofproto/ofproto.c:3698 > #5748 0x00000000004252bd in handle_packet_out (ofconn=0x17fde40, > oh=0x17fdfa0) at ofproto/ofproto.c:3764 > #5749 0x000000000042fce2 in handle_single_part_openflow > (ofconn=0x17fde40, oh=0x17fdfa0, type=OFPTYPE_PACKET_OUT) at > ofproto/ofproto.c:8664 > #5750 0x0000000000430134 in handle_openflow (ofconn=0x17fde40, > msgs=0x7fffcc09dc40) at ofproto/ofproto.c:8851 > #5751 0x000000000047a9ae in ofconn_run (ofconn=0x17fde40, > handle_openflow=0x430072 <handle_openflow>) at ofproto/connmgr.c:1329 > #5752 0x0000000000478584 in connmgr_run (mgr=0x17e2ad0, > handle_openflow=0x430072 <handle_openflow>) at ofproto/connmgr.c:356 > #5753 0x0000000000420f78 in ofproto_run (p=0x17c10e0) at > ofproto/ofproto.c:1933 > #5754 0x00000000004101d4 in bridge_run__ () at vswitchd/bridge.c:3210 > #5755 0x00000000004103d3 in bridge_run () at vswitchd/bridge.c:3269 > #5756 0x0000000000415cf0 in main (argc=10, argv=0x7fffcc09dff8) at > vswitchd/ovs-vswitchd.c:129 > > The main thread never getting out of some processing code to reach any > other handlers (e.g. for appctl requests?) > > I'm adding Ilya to CC in case he has an idea why vswitchd could lock / > freeze / spin indefinitely on two to-lport mirror creation. > > > > On 11/8/22 1:11 PM, Ihar Hrachyshka wrote: > >> On Fri, Nov 4, 2022 at 3:09 PM Abhiram R N <abhira...@gmail.com> wrote: > >>> Mirror creation just creates the mirror. The lsp-attach-mirror > >>> triggers the sequence to create Mirror in OVS DB on compute node. > >>> OVS already supports Port Mirroring. > >>> > >>> Note: This is targeted to mirror to destinations anywhere outside the > >>> cluster where the analyser resides and it need not be an OVN node. > >>> > >>> Example commands are as below: > >>> > >>> Mirror creation > >>> ovn-nbctl mirror-add mirror1 gre 0 from-lport 10.10.10.2 > >>> > >>> Attach a logical port to the mirror. > >>> ovn-nbctl lsp-attach-mirror sw0-port1 mirror1 > >>> > >>> Detach a source from Mirror > >>> ovn-nbctl lsp-detach-mirror sw0-port1 mirror1 > >>> > >>> Mirror deletion > >>> ovn-nbctl mirror-del mirror1 > >>> > >>> Co-authored-by: Veda Barrenkala <vedabarrenk...@gmail.com> > >>> Signed-off-by: Veda Barrenkala <vedabarrenk...@gmail.com> > >>> Signed-off-by: Abhiram R N <abhira...@gmail.com> > >>> --- > >>> v12 --> V13: Made each of bulk test cases(in ovn.at) as separate > >>> test to make it pass consistently. > >>> V11 --> V12: Minor fix in ovn.at to solve intermittent failures > >>> > >>> V10 --> V11: Addressed review comments from V10 by Ihar > >>> i) Expanded bulk updates test cases in ovn.at > >>> Overall below cases are covered > >>> a) Attaches multiple mirrors (new) > >>> b) Equal detaches and attaches (same as V10) > >>> c) Detaches more than attaches (new) > >>> d) Attaches more than detaches (new) > >>> e) Detaches all (new) > >>> ii) Addressed the detach all case in mirror.c > >>> iii) Minor correction in NEWS > >>> iv) Added invalid mirror attach case in ovn-nbctl.at > >>> > >>> Files modified (V10 --> V11): > >>> Code --> mirror.c > >>> Test --> ovn.at, ovn-nbctl.at > >>> Misc --> NEWS > >>> > >>> Ihar, > >>> Regarding mirror_delete function param delete_all it is wrt the > >>> port binding and if a port binding is removed we delete all its > >>> attachment. Already that use case is covered in ovn.at. > >>> Having said that the detaches all had issue in mirror_delete which > >>> I have addressed. With all the above cases added now in bulk updates > >>> hope it should give good assurance. > >>> > >>> NEWS | 1 + > >>> controller/automake.mk | 4 +- > >>> controller/mirror.c | 538 +++++++++++++++++++++++++ > >>> controller/mirror.h | 53 +++ > >>> controller/ovn-controller.c | 266 ++++++++++-- > >>> northd/en-northd.c | 4 + > >>> northd/inc-proc-northd.c | 4 + > >>> northd/northd.c | 172 ++++++++ > >>> northd/northd.h | 2 + > >>> ovn-nb.ovsschema | 31 +- > >>> ovn-nb.xml | 63 +++ > >>> ovn-sb.ovsschema | 26 +- > >>> ovn-sb.xml | 50 +++ > >>> tests/ovn-nbctl.at | 120 ++++++ > >>> tests/ovn-northd.at | 102 +++++ > >>> tests/ovn.at | 778 > >>> ++++++++++++++++++++++++++++++++++++ > >>> utilities/ovn-nbctl.c | 357 +++++++++++++++++ > >>> utilities/ovn-sbctl.c | 4 + > >>> 18 files changed, 2547 insertions(+), 28 deletions(-) > >>> create mode 100644 controller/mirror.c > >>> create mode 100644 controller/mirror.h > >>> > >>> diff --git a/NEWS b/NEWS > >>> index 224a7b83e..84b22abdb 100644 > >>> --- a/NEWS > >>> +++ b/NEWS > >>> @@ -25,6 +25,7 @@ OVN v22.09.0 - 16 Sep 2022 > >>> any of LR's LRP IP, there is no need to create SNAT entry. > >>> Now such > >>> traffic destined to LRP IP is not dropped. > >>> - Bump python version required for building OVN to 3.6. > >>> + - Added Support for Remote Port Mirroring. > >>> > >>> OVN v22.06.0 - 03 Jun 2022 > >>> -------------------------- > >>> diff --git a/controller/automake.mk b/controller/automake.mk > >>> index c2ab1bbe6..334672b4d 100644 > >>> --- a/controller/automake.mk > >>> +++ b/controller/automake.mk > >>> @@ -41,7 +41,9 @@ controller_ovn_controller_SOURCES = \ > >>> controller/ovsport.h \ > >>> controller/ovsport.c \ > >>> controller/vif-plug.h \ > >>> - controller/vif-plug.c > >>> + controller/vif-plug.c \ > >>> + controller/mirror.h \ > >>> + controller/mirror.c > >>> > >>> controller_ovn_controller_LDADD = lib/libovn.la > >>> $(OVS_LIBDIR)/libopenvswitch.la > >>> man_MANS += controller/ovn-controller.8 > >>> diff --git a/controller/mirror.c b/controller/mirror.c > >>> new file mode 100644 > >>> index 000000000..11f2b63a6 > >>> --- /dev/null > >>> +++ b/controller/mirror.c > >>> @@ -0,0 +1,538 @@ > >>> +/* Copyright (c) 2022 Red Hat, Inc. > >>> + * > >>> + * Licensed under the Apache License, Version 2.0 (the "License"); > >>> + * you may not use this file except in compliance with the License. > >>> + * You may obtain a copy of the License at: > >>> + * > >>> + * http://www.apache.org/licenses/LICENSE-2.0 > >>> + * > >>> + * Unless required by applicable law or agreed to in writing, software > >>> + * distributed under the License is distributed on an "AS IS" BASIS, > >>> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or > >>> implied. > >>> + * See the License for the specific language governing permissions and > >>> + * limitations under the License. > >>> + */ > >>> + > >>> +#include <config.h> > >>> +#include <unistd.h> > >>> + > >>> +/* library headers */ > >>> +#include "lib/sset.h" > >>> +#include "lib/util.h" > >>> + > >>> +/* OVS includes. */ > >>> +#include "lib/vswitch-idl.h" > >>> +#include "openvswitch/vlog.h" > >>> + > >>> +/* OVN includes. */ > >>> +#include "binding.h" > >>> +#include "lib/ovn-sb-idl.h" > >>> +#include "mirror.h" > >>> + > >>> +VLOG_DEFINE_THIS_MODULE(port_mirror); > >>> + > >>> +/* Static function declarations */ > >>> + > >>> +static const struct ovsrec_port * > >>> +get_port_for_iface(const struct ovsrec_interface *iface, > >>> + const struct ovsrec_bridge *br_int) > >>> +{ > >>> + for (size_t i = 0; i < br_int->n_ports; i++) { > >>> + const struct ovsrec_port *p = br_int->ports[i]; > >>> + for (size_t j = 0; j < p->n_interfaces; j++) { > >>> + if (!strcmp(iface->name, p->interfaces[j]->name)) { > >>> + return p; > >>> + } > >>> + } > >>> + } > >>> + return NULL; > >>> +} > >>> + > >>> +static bool > >>> +mirror_create(const struct sbrec_port_binding *pb, > >>> + struct port_mirror_ctx *pm_ctx) > >>> +{ > >>> + const struct ovsrec_mirror *mirror = NULL; > >>> + > >>> + if (pb->n_up && !pb->up[0]) { > >>> + return true; > >>> + } > >>> + > >>> + if (pb->chassis != pm_ctx->chassis_rec) { > >>> + return true; > >>> + } > >>> + > >>> + if (!pm_ctx->ovs_idl_txn) { > >>> + return false; > >>> + } > >>> + > >>> + > >>> + VLOG_INFO("Mirror rule(s) present for %s ", pb->logical_port); > >>> + /* Loop through the mirror rules */ > >>> + for (size_t i =0; i < pb->n_mirror_rules; i++) { > >>> + /* check if the mirror already exists in OVS DB */ > >>> + bool create_mirror = true; > >>> + OVSREC_MIRROR_TABLE_FOR_EACH (mirror, pm_ctx->mirror_table) { > >>> + if (!strcmp(pb->mirror_rules[i]->name, mirror->name)) { > >>> + /* Mirror with same name already exists > >>> + * No need to create mirror > >>> + */ > >>> + create_mirror = false; > >>> + break; > >>> + } > >>> + } > >>> + > >>> + if (create_mirror) { > >>> + > >>> + struct smap options = SMAP_INITIALIZER(&options); > >>> + char *port_name, *key; > >>> + > >>> + key = xasprintf("%ld",(long int) > >>> pb->mirror_rules[i]->index); > >>> + smap_add(&options, "remote_ip", > >>> pb->mirror_rules[i]->sink); > >>> + smap_add(&options, "key", key); > >>> + if (!strcmp(pb->mirror_rules[i]->type, "erspan")) { > >>> + /* Set the ERSPAN index */ > >>> + smap_add(&options, "erspan_idx", key); > >>> + smap_add(&options, "erspan_ver","1"); > >>> + > >>> + } > >>> + struct ovsrec_interface *iface = > >>> + ovsrec_interface_insert(pm_ctx->ovs_idl_txn); > >>> + port_name = xasprintf("ovn-%s", > >>> + pb->mirror_rules[i]->name); > >>> + > >>> + ovsrec_interface_set_name(iface, port_name); > >>> + ovsrec_interface_set_type(iface, > >>> pb->mirror_rules[i]->type); > >>> + ovsrec_interface_set_options(iface, &options); > >>> + > >>> + struct ovsrec_port *port = > >>> + ovsrec_port_insert(pm_ctx->ovs_idl_txn); > >>> + ovsrec_port_set_name(port, port_name); > >>> + ovsrec_port_set_interfaces(port, &iface, 1); > >>> + > >>> + ovsrec_bridge_update_ports_addvalue(pm_ctx->br_int, port); > >>> + > >>> + smap_destroy(&options); > >>> + free(port_name); > >>> + free(key); > >>> + > >>> + VLOG_INFO("Creating Mirror in OVS DB"); > >>> + mirror = ovsrec_mirror_insert(pm_ctx->ovs_idl_txn); > >>> + ovsrec_mirror_set_name(mirror,pb->mirror_rules[i]->name); > >>> + ovsrec_mirror_update_output_port_addvalue(mirror, port); > >>> + ovsrec_bridge_update_mirrors_addvalue(pm_ctx->br_int, > >>> + mirror); > >>> + } > >>> + > >>> + struct local_binding *lbinding = local_binding_find( > >>> + pm_ctx->local_bindings, > >>> pb->logical_port); > >>> + const struct ovsrec_port *p = > >>> + get_port_for_iface(lbinding->iface, > >>> pm_ctx->br_int); > >>> + if (p) { > >>> + if (!strcmp(pb->mirror_rules[i]->filter,"from-lport")) { > >>> + ovsrec_mirror_update_select_src_port_addvalue(mirror, p); > >>> + } else if > >>> (!strcmp(pb->mirror_rules[i]->filter,"to-lport")) { > >>> + ovsrec_mirror_update_select_dst_port_addvalue(mirror, p); > >>> + } else { > >>> + ovsrec_mirror_update_select_src_port_addvalue(mirror, p); > >>> + ovsrec_mirror_update_select_dst_port_addvalue(mirror, p); > >>> + } > >>> + } > >>> + } > >>> + return true; > >>> +} > >>> + > >>> +static void > >>> +check_and_update_mirror_table(const struct sbrec_mirror *sb_mirror, > >>> + struct ovsrec_mirror *ovs_mirror) > >>> +{ > >>> + char *filter; > >>> + if ((ovs_mirror->n_select_dst_port) > >>> + && (ovs_mirror->n_select_src_port)) { > >>> + filter = "both"; > >>> + } else if (ovs_mirror->n_select_dst_port) { > >>> + filter = "to-lport"; > >>> + } else { > >>> + filter = "from-lport"; > >>> + } > >>> + > >>> + if (strcmp(sb_mirror->filter, filter)) { > >>> + if (!strcmp(sb_mirror->filter,"from-lport") > >>> + && !strcmp(filter,"both")) { > >>> + for (size_t i = 0; i < ovs_mirror->n_select_dst_port; > >>> i++) { > >>> + ovsrec_mirror_update_select_dst_port_delvalue(ovs_mirror, > >>> + ovs_mirror->select_dst_port[i]); > >>> + } > >>> + } else if (!strcmp(sb_mirror->filter,"to-lport") > >>> + && !strcmp(filter,"both")) { > >>> + for (size_t i = 0; i < ovs_mirror->n_select_src_port; > >>> i++) { > >>> + ovsrec_mirror_update_select_src_port_delvalue(ovs_mirror, > >>> + ovs_mirror->select_src_port[i]); > >>> + } > >>> + } else if (!strcmp(sb_mirror->filter,"both") > >>> + && !strcmp(filter,"from-lport")) { > >>> + for (size_t i = 0; i < ovs_mirror->n_select_src_port; > >>> i++) { > >>> + ovsrec_mirror_update_select_dst_port_addvalue(ovs_mirror, > >>> + ovs_mirror->select_src_port[i]); > >>> + } > >>> + } else if (!strcmp(sb_mirror->filter,"both") > >>> + && !strcmp(filter,"to-lport")) { > >>> + for (size_t i = 0; i < ovs_mirror->n_select_dst_port; > >>> i++) { > >>> + ovsrec_mirror_update_select_src_port_addvalue(ovs_mirror, > >>> + ovs_mirror->select_dst_port[i]); > >>> + } > >>> + } else if (!strcmp(sb_mirror->filter,"to-lport") > >>> + && !strcmp(filter,"from-lport")) { > >>> + for (size_t i = 0; i < ovs_mirror->n_select_src_port; > >>> i++) { > >>> + ovsrec_mirror_update_select_dst_port_addvalue(ovs_mirror, > >>> + ovs_mirror->select_src_port[i]); > >>> + ovsrec_mirror_update_select_src_port_delvalue(ovs_mirror, > >>> + ovs_mirror->select_src_port[i]); > >>> + } > >>> + } else if (!strcmp(sb_mirror->filter,"from-lport") > >>> + && !strcmp(filter,"to-lport")) { > >>> + for (size_t i = 0; i < ovs_mirror->n_select_dst_port; > >>> i++) { > >>> + ovsrec_mirror_update_select_src_port_addvalue(ovs_mirror, > >>> + ovs_mirror->select_dst_port[i]); > >>> + ovsrec_mirror_update_select_dst_port_delvalue(ovs_mirror, > >>> + ovs_mirror->select_dst_port[i]); > >>> + } > >>> + } > >>> + } > >>> +} > >>> + > >>> +static void > >>> +check_and_update_interface_table(const struct sbrec_mirror *sb_mirror, > >>> + struct ovsrec_mirror *ovs_mirror) > >>> +{ > >>> + struct smap options = SMAP_INITIALIZER(&options); > >>> + char *key, *type; > >>> + struct ovsrec_interface *iface = > >>> + ovs_mirror->output_port->interfaces[0]; > >>> + struct smap *opts = &iface->options; > >>> + > >>> + const char *erspan_ver = smap_get(opts, "erspan_ver"); > >>> + if (erspan_ver) { > >>> + type = "erspan"; > >>> + } else { > >>> + type = "gre"; > >>> + } > >>> + if (strcmp(type, sb_mirror->type)) { > >>> + ovsrec_interface_set_type(iface, sb_mirror->type); > >>> + } > >>> + > >>> + key = xasprintf("%ld",(long int) sb_mirror->index); > >>> + smap_add(&options, "remote_ip", sb_mirror->sink); > >>> + smap_add(&options, "key", key); > >>> + > >>> + if (!strcmp(sb_mirror->type, "erspan")) { > >>> + /* Set the ERSPAN index */ > >>> + smap_add(&options, "erspan_idx", key); > >>> + smap_add(&options, "erspan_ver","1"); > >>> + } > >>> + > >>> + ovsrec_interface_set_options(iface, &options); > >>> + smap_destroy(&options); > >>> + free(key); > >>> + > >>> +} > >>> + > >>> +static void > >>> +mirror_update(const struct sbrec_mirror *sb_mirror, > >>> + struct ovsrec_mirror *ovs_mirror) > >>> +{ > >>> + check_and_update_interface_table(sb_mirror, ovs_mirror); > >>> + > >>> + check_and_update_mirror_table(sb_mirror, ovs_mirror); > >>> +} > >>> + > >>> +static bool > >>> +mirror_delete(const struct sbrec_port_binding *pb, > >>> + struct port_mirror_ctx *pm_ctx, > >>> + struct shash *pb_mirror_map, > >>> + bool delete_all) > >>> +{ > >>> + > >>> + if (!pm_ctx->ovs_idl_txn) { > >>> + return false; > >>> + } > >>> + > >>> + struct sset pb_mirrors = SSET_INITIALIZER(&pb_mirrors); > >>> + > >>> + if (!delete_all) { > >>> + for (size_t i = 0; i < pb->n_mirror_rules ; i++) { > >>> + sset_add(&pb_mirrors, pb->mirror_rules[i]->name); > >>> + } > >>> + } > >>> + > >>> + if (delete_all && (shash_is_empty(pb_mirror_map)) && > >>> pb->n_mirror_rules) { > >>> + for (size_t i = 0; i < pb->n_mirror_rules ; i++) { > >>> + > >>> + struct ovsrec_mirror *ovs_mirror = NULL; > >>> + ovs_mirror = shash_find_data(pm_ctx->ovs_mirrors, > >>> + pb->mirror_rules[i]->name); > >>> + if (ovs_mirror) { > >>> + ovsrec_bridge_update_ports_delvalue(pm_ctx->br_int, > >>> + ovs_mirror->output_port); > >>> + ovsrec_bridge_update_mirrors_delvalue(pm_ctx->br_int, > >>> + ovs_mirror); > >>> + ovsrec_port_delete(ovs_mirror->output_port); > >>> + ovsrec_mirror_delete(ovs_mirror); > >>> + } > >>> + } > >>> + } > >>> + > >>> + struct shash_node *mirror_node; > >>> + const struct sbrec_port_binding *sb_pb; > >>> + int attach_cnt = 0; > >>> + SHASH_FOR_EACH (mirror_node, pb_mirror_map) { > >>> + struct ovsrec_mirror *ovs_mirror = mirror_node->data; > >>> + if (!sset_find(&pb_mirrors, ovs_mirror->name)) { > >>> + /* Find if the mirror has other sources */ > >>> + attach_cnt = 0; > >>> + SBREC_PORT_BINDING_TABLE_FOR_EACH_TRACKED (sb_pb, > >>> + pm_ctx->port_binding_table) { > >>> + for (size_t i = 0; i < sb_pb->n_mirror_rules; i++) { > >>> + if (!strcmp(sb_pb->mirror_rules[i]->name, > >>> + ovs_mirror->name)) { > >>> + attach_cnt++; > >>> + } > >>> + } > >>> + } > >>> + if (attach_cnt) { > >>> + /* More than 1 source then just > >>> + * update the mirror table > >>> + */ > >>> + bool done = false; > >>> + for (size_t i = 0; ((i < > >>> ovs_mirror->n_select_dst_port) > >>> + && (done == > >>> false)); i++) { > >>> + const struct ovsrec_port *port_rec = > >>> + ovs_mirror->select_dst_port[i]; > >>> + for (size_t j = 0; j < port_rec->n_interfaces; > >>> j++) { > >>> + const struct ovsrec_interface *iface_rec; > >>> + > >>> + iface_rec = port_rec->interfaces[j]; > >>> + const char *iface_id = > >>> + smap_get(&iface_rec->external_ids, > >>> + "iface-id"); > >>> + if (!strcmp(iface_id,pb->logical_port)) { > >>> + ovsrec_mirror_update_select_dst_port_delvalue( > >>> + ovs_mirror, port_rec); > >>> + done = true; > >>> + break; > >>> + } > >>> + } > >>> + } > >>> + done = false; > >>> + for (size_t i = 0; ((i < > >>> ovs_mirror->n_select_src_port) > >>> + && (done == > >>> false)); i++) { > >>> + const struct ovsrec_port *port_rec = > >>> + ovs_mirror->select_src_port[i]; > >>> + for (size_t j = 0; j < port_rec->n_interfaces; > >>> j++) { > >>> + const struct ovsrec_interface *iface_rec; > >>> + > >>> + iface_rec = port_rec->interfaces[j]; > >>> + const char *iface_id = > >>> + smap_get(&iface_rec->external_ids, > >>> + "iface-id"); > >>> + if (!strcmp(iface_id,pb->logical_port)) { > >>> + ovsrec_mirror_update_select_src_port_delvalue( > >>> + ovs_mirror, port_rec); > >>> + done = true; > >>> + break; > >>> + } > >>> + } > >>> + } > >>> + } else { > >>> + /* > >>> + * If only 1 source delete the output port > >>> + * and then delete the mirror completely > >>> + */ > >>> + VLOG_INFO("Only 1 source for the mirror. Hence > >>> delete it"); > >>> + ovsrec_bridge_update_ports_delvalue(pm_ctx->br_int, > >>> + ovs_mirror->output_port); > >>> + ovsrec_bridge_update_mirrors_delvalue(pm_ctx->br_int, > >>> + ovs_mirror); > >>> + ovsrec_port_delete(ovs_mirror->output_port); > >>> + ovsrec_mirror_delete(ovs_mirror); > >>> + } > >>> + } > >>> + } > >>> + > >>> + const char *used_node, *used_next; > >>> + SSET_FOR_EACH_SAFE (used_node, used_next, &pb_mirrors) { > >>> + sset_delete(&pb_mirrors, SSET_NODE_FROM_NAME(used_node)); > >>> + } > >>> + sset_destroy(&pb_mirrors); > >>> + > >>> + return true; > >>> +} > >>> + > >>> +static void > >>> +find_port_specific_mirrors (const struct sbrec_port_binding *pb, > >>> + struct port_mirror_ctx *pm_ctx, > >>> + struct shash *pb_mirror_map) > >>> +{ > >>> + const struct ovsrec_mirror *mirror = NULL; > >>> + > >>> + OVSREC_MIRROR_TABLE_FOR_EACH (mirror, pm_ctx->mirror_table) { > >>> + for (size_t i = 0; i < mirror->n_select_dst_port; i++) { > >>> + const struct ovsrec_port *port_rec = > >>> mirror->select_dst_port[i]; > >>> + for (size_t j = 0; j < port_rec->n_interfaces; j++) { > >>> + const struct ovsrec_interface *iface_rec; > >>> + iface_rec = port_rec->interfaces[j]; > >>> + const char *logical_port = > >>> + smap_get(&iface_rec->external_ids, "iface-id"); > >>> + if (!strcmp(logical_port, pb->logical_port)) { > >>> + shash_add_once(pb_mirror_map, mirror->name, > >>> mirror); > >>> + } > >>> + } > >>> + } > >>> + for (size_t i = 0; i < mirror->n_select_src_port; i++) { > >>> + const struct ovsrec_port *port_rec = > >>> mirror->select_src_port[i]; > >>> + for (size_t j = 0; j < port_rec->n_interfaces; j++) { > >>> + const struct ovsrec_interface *iface_rec; > >>> + iface_rec = port_rec->interfaces[j]; > >>> + const char *logical_port = > >>> + smap_get(&iface_rec->external_ids, "iface-id"); > >>> + if (!strcmp(logical_port, pb->logical_port)) { > >>> + shash_add_once(pb_mirror_map, mirror->name, > >>> mirror); > >>> + } > >>> + } > >>> + } > >>> + } > >>> +} > >>> + > >>> +void > >>> +mirror_register_ovs_idl(struct ovsdb_idl *ovs_idl) > >>> +{ > >>> + ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_mirrors); > >>> + > >>> + ovsdb_idl_add_table(ovs_idl, &ovsrec_table_mirror); > >>> + ovsdb_idl_add_column(ovs_idl, &ovsrec_mirror_col_name); > >>> + ovsdb_idl_add_column(ovs_idl, &ovsrec_mirror_col_output_port); > >>> + ovsdb_idl_add_column(ovs_idl, &ovsrec_mirror_col_select_dst_port); > >>> + ovsdb_idl_add_column(ovs_idl, &ovsrec_mirror_col_select_src_port); > >>> +} > >>> + > >>> + > >>> +void > >>> +ovn_port_mirror_init(struct shash *ovs_mirrors) > >>> +{ > >>> + shash_init(ovs_mirrors); > >>> +} > >>> + > >>> +void > >>> +ovn_port_mirror_run(struct port_mirror_ctx *pm_ctx) > >>> +{ > >>> + const struct sbrec_port_binding *pb; > >>> + SBREC_PORT_BINDING_TABLE_FOR_EACH (pb, > >>> + pm_ctx->port_binding_table) { > >>> + ovn_port_mirror_handle_lport(pb, false, pm_ctx); > >>> + } > >>> +} > >>> + > >>> +bool > >>> +ovn_port_mirror_handle_lport(const struct sbrec_port_binding *pb, > >>> bool removed, > >>> + struct port_mirror_ctx *pm_ctx) > >>> +{ > >>> + bool ret = true; > >>> + struct local_binding *lbinding = local_binding_find( > >>> + pm_ctx->local_bindings, > >>> pb->logical_port); > >>> + > >>> + if (strcmp(pb->type, "") && (!lbinding)) { > >>> + return ret; > >>> + } > >>> + > >>> + struct shash port_ovs_mirrors = > >>> SHASH_INITIALIZER(&port_ovs_mirrors); > >>> + > >>> + /* Need to find if mirror needs update */ > >>> + find_port_specific_mirrors(pb, pm_ctx, &port_ovs_mirrors); > >>> + if (!removed) { > >>> + if ((pb->n_mirror_rules == 0) > >>> + && (shash_is_empty(&port_ovs_mirrors))) { > >>> + /* No mirror update */ > >>> + } else if (pb->n_mirror_rules == > >>> shash_count(&port_ovs_mirrors)) { > >>> + /* Though number of mirror rules are same, > >>> + * need to verify the contents > >>> + */ > >>> + for (size_t i = 0; i < pb->n_mirror_rules; i++) { > >>> + if (!shash_find(&port_ovs_mirrors, > >>> + pb->mirror_rules[i]->name)) { > >>> + /* Mis match in OVN SB DB and OVS DB > >>> + * Delete and Create mirror(s) with proper sources > >>> + */ > >>> + ret = mirror_delete(pb, pm_ctx, > >>> + &port_ovs_mirrors, false); > >>> + if (ret) { > >>> + ret = mirror_create(pb, pm_ctx); > >>> + } > >>> + break; > >>> + } > >>> + } > >>> + } else { > >>> + /* Update Mirror */ > >>> + if (pb->n_mirror_rules > shash_count(&port_ovs_mirrors)) { > >>> + /* create mirror, > >>> + * if mirror already exists only update selection > >>> + */ > >>> + ret = mirror_create(pb, pm_ctx); > >>> + } else { > >>> + /* delete mirror, > >>> + * if mirror has other sources only update selection > >>> + */ > >>> + ret = mirror_delete(pb, pm_ctx, &port_ovs_mirrors, > >>> false); > >>> + } > >>> + } > >>> + } else { > >>> + ret = mirror_delete(pb, pm_ctx, &port_ovs_mirrors, true); > >>> + } > >>> + > >>> + struct shash_node *ovs_mirror_node, *ovs_mirror_next; > >>> + SHASH_FOR_EACH_SAFE (ovs_mirror_node, ovs_mirror_next, > >>> + &port_ovs_mirrors) { > >>> + shash_delete(&port_ovs_mirrors, ovs_mirror_node); > >>> + } > >>> + shash_destroy(&port_ovs_mirrors); > >>> + > >>> + return ret; > >>> +} > >>> + > >>> +bool > >>> +ovn_port_mirror_handle_update(struct port_mirror_ctx *pm_ctx) > >>> +{ > >>> + const struct sbrec_mirror *mirror = NULL; > >>> + struct ovsrec_mirror *ovs_mirror = NULL; > >>> + > >>> + SBREC_MIRROR_TABLE_FOR_EACH_TRACKED (mirror, > >>> pm_ctx->sb_mirror_table) { > >>> + /* For each tracked mirror entry check if OVS entry is there*/ > >>> + ovs_mirror = shash_find_data(pm_ctx->ovs_mirrors, > >>> mirror->name); > >>> + if (ovs_mirror) { > >>> + if (sbrec_mirror_is_deleted(mirror)) { > >>> + /* Need to delete the mirror in OVS */ > >>> + VLOG_INFO("Delete mirror and remove port"); > >>> + ovsrec_bridge_update_ports_delvalue(pm_ctx->br_int, > >>> + ovs_mirror->output_port); > >>> + ovsrec_bridge_update_mirrors_delvalue(pm_ctx->br_int, > >>> + ovs_mirror); > >>> + ovsrec_port_delete(ovs_mirror->output_port); > >>> + ovsrec_mirror_delete(ovs_mirror); > >>> + } else { > >>> + mirror_update(mirror, ovs_mirror); > >>> + } > >>> + } > >>> + } > >>> + > >>> + return true; > >>> +} > >>> + > >>> +void > >>> +ovn_port_mirror_destroy(struct shash *ovs_mirrors) > >>> +{ > >>> + struct shash_node *ovs_mirror_node, *ovs_mirror_next; > >>> + SHASH_FOR_EACH_SAFE (ovs_mirror_node, ovs_mirror_next, > >>> + ovs_mirrors) { > >>> + shash_delete(ovs_mirrors, ovs_mirror_node); > >>> + } > >>> + shash_destroy(ovs_mirrors); > >>> +} > >>> diff --git a/controller/mirror.h b/controller/mirror.h > >>> new file mode 100644 > >>> index 000000000..85b964f55 > >>> --- /dev/null > >>> +++ b/controller/mirror.h > >>> @@ -0,0 +1,53 @@ > >>> +/* Copyright (c) 2022 Red Hat, Inc. > >>> + * > >>> + * Licensed under the Apache License, Version 2.0 (the "License"); > >>> + * you may not use this file except in compliance with the License. > >>> + * You may obtain a copy of the License at: > >>> + * > >>> + * http://www.apache.org/licenses/LICENSE-2.0 > >>> + * > >>> + * Unless required by applicable law or agreed to in writing, software > >>> + * distributed under the License is distributed on an "AS IS" BASIS, > >>> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or > >>> implied. > >>> + * See the License for the specific language governing permissions and > >>> + * limitations under the License. > >>> + */ > >>> + > >>> +#ifndef OVN_MIRROR_H > >>> +#define OVN_MIRROR_H 1 > >>> + > >>> +struct ovsdb_idl_txn; > >>> +struct ovsrec_port_table; > >>> +struct ovsrec_bridge; > >>> +struct ovsrec_bridge_table; > >>> +struct ovsrec_open_vswitch_table; > >>> +struct sbrec_chassis; > >>> +struct ovsrec_interface_table; > >>> +struct ovsrec_mirror_table; > >>> +struct sbrec_mirror_table; > >>> +struct sbrec_port_binding_table; > >>> + > >>> +struct port_mirror_ctx { > >>> + struct shash *ovs_mirrors; > >>> + struct ovsdb_idl_txn *ovs_idl_txn; > >>> + const struct ovsrec_port_table *port_table; > >>> + const struct ovsrec_bridge *br_int; > >>> + const struct sbrec_chassis *chassis_rec; > >>> + const struct ovsrec_bridge_table *bridge_table; > >>> + const struct ovsrec_open_vswitch_table *ovs_table; > >>> + const struct ovsrec_interface_table *iface_table; > >>> + const struct ovsrec_mirror_table *mirror_table; > >>> + const struct sbrec_mirror_table *sb_mirror_table; > >>> + const struct sbrec_port_binding_table *port_binding_table; > >>> + struct shash *local_bindings; > >>> +}; > >>> + > >>> +void mirror_register_ovs_idl(struct ovsdb_idl *); > >>> +void ovn_port_mirror_init(struct shash *); > >>> +void ovn_port_mirror_destroy(struct shash *); > >>> +void ovn_port_mirror_run(struct port_mirror_ctx *pm_ctx); > >>> +bool ovn_port_mirror_handle_lport(const struct sbrec_port_binding *pb, > >>> + bool removed, > >>> + struct port_mirror_ctx *pm_ctx); > >>> +bool ovn_port_mirror_handle_update(struct port_mirror_ctx *pm_ctx); > >>> +#endif > >>> diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c > >>> index 8895c7a2b..15ab17c4a 100644 > >>> --- a/controller/ovn-controller.c > >>> +++ b/controller/ovn-controller.c > >>> @@ -78,6 +78,7 @@ > >>> #include "lib/inc-proc-eng.h" > >>> #include "lib/ovn-l7.h" > >>> #include "hmapx.h" > >>> +#include "mirror.h" > >>> > >>> VLOG_DEFINE_THIS_MODULE(main); > >>> > >>> @@ -966,6 +967,7 @@ ctrl_register_ovs_idl(struct ovsdb_idl *ovs_idl) > >>> ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_name); > >>> ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_interfaces); > >>> ovsdb_idl_track_add_column(ovs_idl, > >>> &ovsrec_port_col_external_ids); > >>> + mirror_register_ovs_idl(ovs_idl); > >>> } > >>> > >>> #define SB_NODES \ > >>> @@ -986,6 +988,7 @@ ctrl_register_ovs_idl(struct ovsdb_idl *ovs_idl) > >>> SB_NODE(load_balancer, "load_balancer") \ > >>> SB_NODE(fdb, "fdb") \ > >>> SB_NODE(meter, "meter") \ > >>> + SB_NODE(mirror, "mirror") \ > >>> SB_NODE(static_mac_binding, "static_mac_binding") > >>> > >>> enum sb_engine_node { > >>> @@ -1003,7 +1006,8 @@ enum sb_engine_node { > >>> OVS_NODE(bridge, "bridge") \ > >>> OVS_NODE(port, "port") \ > >>> OVS_NODE(interface, "interface") \ > >>> - OVS_NODE(qos, "qos") > >>> + OVS_NODE(qos, "qos") \ > >>> + OVS_NODE(mirror, "mirror") > >>> > >>> enum ovs_engine_node { > >>> #define OVS_NODE(NAME, NAME_STR) OVS_##NAME, > >>> @@ -2383,6 +2387,203 @@ load_balancers_by_dp_cleanup(struct hmap *lbs) > >>> free(lbs); > >>> } > >>> > >>> +/* Mirror Engine */ > >>> +struct ed_type_port_mirror { > >>> + struct shash ovs_mirrors; > >>> +}; > >>> + > >>> +static void * > >>> +en_port_mirror_init(struct engine_node *node OVS_UNUSED, > >>> + struct engine_arg *arg OVS_UNUSED) > >>> +{ > >>> + struct ed_type_port_mirror *port_mirror = xzalloc(sizeof > >>> *port_mirror); > >>> + ovn_port_mirror_init(&port_mirror->ovs_mirrors); > >>> + return port_mirror; > >>> +} > >>> + > >>> +static void > >>> +en_port_mirror_cleanup(void *data) > >>> +{ > >>> + struct ed_type_port_mirror *port_mirror = data; > >>> + ovn_port_mirror_destroy(&port_mirror->ovs_mirrors); > >>> +} > >>> + > >>> +static void > >>> +init_port_mirror_ctx(struct engine_node *node, > >>> + struct ed_type_runtime_data *rt_data, > >>> + struct ed_type_port_mirror *port_mirror_data, > >>> + struct port_mirror_ctx *pm_ctx) > >>> +{ > >>> + struct ovsrec_open_vswitch_table *ovs_table = > >>> + (struct ovsrec_open_vswitch_table *) EN_OVSDB_GET( > >>> + engine_get_input("OVS_open_vswitch", node)); > >>> + struct ovsrec_bridge_table *bridge_table = > >>> + (struct ovsrec_bridge_table *) EN_OVSDB_GET( > >>> + engine_get_input("OVS_bridge", node)); > >>> + const char *chassis_id = get_ovs_chassis_id(ovs_table); > >>> + const struct ovsrec_bridge *br_int = get_br_int(bridge_table, > >>> ovs_table); > >>> + > >>> + ovs_assert(br_int && chassis_id); > >>> + const struct sbrec_chassis *chassis = NULL; > >>> + struct ovsdb_idl_index *sbrec_chassis_by_name = > >>> + engine_ovsdb_node_get_index( > >>> + engine_get_input("SB_chassis", node), > >>> + "name"); > >>> + > >>> + if (chassis_id) { > >>> + chassis = chassis_lookup_by_name(sbrec_chassis_by_name, > >>> chassis_id); > >>> + } > >>> + ovs_assert(chassis); > >>> + > >>> + struct ovsrec_port_table *port_table = > >>> + (struct ovsrec_port_table *) EN_OVSDB_GET( > >>> + engine_get_input("OVS_port", node)); > >>> + > >>> + struct ed_type_ovs_interface_shadow *iface_shadow = > >>> + engine_get_input_data("ovs_interface_shadow", node); > >>> + > >>> + struct ovsrec_mirror_table *mirror_table = > >>> + (struct ovsrec_mirror_table *) EN_OVSDB_GET( > >>> + engine_get_input("OVS_mirror", node)); > >>> + > >>> + struct sbrec_port_binding_table *pb_table = > >>> + (struct sbrec_port_binding_table *) EN_OVSDB_GET( > >>> + engine_get_input("SB_port_binding", node)); > >>> + > >>> + struct sbrec_mirror_table *sb_mirror_table = > >>> + (struct sbrec_mirror_table *) EN_OVSDB_GET( > >>> + engine_get_input("SB_mirror", node)); > >>> + > >>> + struct shash_node *ovs_mirror_node, *ovs_mirror_next; > >>> + SHASH_FOR_EACH_SAFE (ovs_mirror_node, ovs_mirror_next, > >>> + &port_mirror_data->ovs_mirrors) { > >>> + shash_delete(&port_mirror_data->ovs_mirrors, ovs_mirror_node); > >>> + } > >>> + > >>> + const struct ovsrec_mirror *ovsmirror = NULL; > >>> + OVSREC_MIRROR_TABLE_FOR_EACH (ovsmirror, mirror_table) { > >>> + shash_add(&port_mirror_data->ovs_mirrors, ovsmirror->name, > >>> ovsmirror); > >>> + } > >>> + > >>> + pm_ctx->ovs_idl_txn = engine_get_context()->ovs_idl_txn; > >>> + pm_ctx->port_table = port_table; > >>> + pm_ctx->iface_table = iface_shadow->iface_table; > >>> + pm_ctx->mirror_table = mirror_table; > >>> + pm_ctx->port_binding_table = pb_table; > >>> + pm_ctx->sb_mirror_table = sb_mirror_table; > >>> + pm_ctx->br_int = br_int; > >>> + pm_ctx->chassis_rec = chassis; > >>> + pm_ctx->bridge_table = bridge_table; > >>> + pm_ctx->ovs_table = ovs_table; > >>> + pm_ctx->ovs_mirrors = &port_mirror_data->ovs_mirrors; > >>> + pm_ctx->local_bindings = &rt_data->lbinding_data.bindings; > >>> +} > >>> + > >>> +static void > >>> +en_port_mirror_run(struct engine_node *node, void *data) > >>> +{ > >>> + struct port_mirror_ctx pm_ctx; > >>> + struct ed_type_port_mirror *port_mirror_data = data; > >>> + struct ed_type_runtime_data *rt_data = > >>> + engine_get_input_data("runtime_data", node); > >>> + > >>> + init_port_mirror_ctx(node, rt_data, port_mirror_data, &pm_ctx); > >>> + > >>> + ovn_port_mirror_run(&pm_ctx); > >>> + engine_set_node_state(node, EN_UPDATED); > >>> +} > >>> + > >>> +static bool > >>> +port_mirror_runtime_data_handler(struct engine_node *node, void *data) > >>> +{ > >>> + struct ed_type_runtime_data *rt_data = > >>> + engine_get_input_data("runtime_data", node); > >>> + > >>> + /* There is no tracked data. Fall back to full recompute of > >>> port_mirror */ > >>> + if (!rt_data->tracked) { > >>> + return false; > >>> + } > >>> + > >>> + struct hmap *tracked_dp_bindings = &rt_data->tracked_dp_bindings; > >>> + if (hmap_is_empty(tracked_dp_bindings)) { > >>> + return true; > >>> + } > >>> + > >>> + struct port_mirror_ctx pm_ctx; > >>> + struct ed_type_port_mirror *port_mirror_data = data; > >>> + init_port_mirror_ctx(node, rt_data, port_mirror_data, &pm_ctx); > >>> + > >>> + struct tracked_datapath *tdp; > >>> + HMAP_FOR_EACH (tdp, node, tracked_dp_bindings) { > >>> + if (tdp->tracked_type != TRACKED_RESOURCE_UPDATED) { > >>> + /* Fall back to full recompute when a local datapath > >>> + * is added or deleted. */ > >>> + return false; > >>> + } > >>> + > >>> + struct shash_node *shash_node; > >>> + SHASH_FOR_EACH (shash_node, &tdp->lports) { > >>> + struct tracked_lport *lport = shash_node->data; > >>> + bool removed = > >>> + lport->tracked_type == TRACKED_RESOURCE_REMOVED ? > >>> true: false; > >>> + if (!ovn_port_mirror_handle_lport(lport->pb, removed, > >>> &pm_ctx)) { > >>> + return false; > >>> + } > >>> + } > >>> + } > >>> + > >>> + engine_set_node_state(node, EN_UPDATED); > >>> + return true; > >>> +} > >>> + > >>> +static bool > >>> +port_mirror_port_binding_handler(struct engine_node *node, void *data) > >>> +{ > >>> + struct port_mirror_ctx pm_ctx; > >>> + struct ed_type_port_mirror *port_mirror_data = data; > >>> + struct ed_type_runtime_data *rt_data = > >>> + engine_get_input_data("runtime_data", node); > >>> + > >>> + init_port_mirror_ctx(node, rt_data, port_mirror_data, &pm_ctx); > >>> + > >>> + /* handle port binding updates (i.,e when the mirror column > >>> + * of port_binding is updated) > >>> + */ > >>> + const struct sbrec_port_binding *pb; > >>> + SBREC_PORT_BINDING_TABLE_FOR_EACH_TRACKED (pb, > >>> + pm_ctx.port_binding_table) { > >>> + bool removed = sbrec_port_binding_is_deleted(pb); > >>> + if (!ovn_port_mirror_handle_lport(pb, removed, &pm_ctx)) { > >>> + return false; > >>> + } > >>> + } > >>> + > >>> + engine_set_node_state(node, EN_UPDATED); > >>> + return true; > >>> + > >>> +} > >>> + > >>> +static bool > >>> +port_mirror_sb_mirror_handler(struct engine_node *node, void *data) > >>> +{ > >>> + struct port_mirror_ctx pm_ctx; > >>> + struct ed_type_port_mirror *port_mirror_data = data; > >>> + struct ed_type_runtime_data *rt_data = > >>> + engine_get_input_data("runtime_data", node); > >>> + > >>> + init_port_mirror_ctx(node, rt_data, port_mirror_data, &pm_ctx); > >>> + > >>> + /* handle sb mirror updates > >>> + */ > >>> + if (!ovn_port_mirror_handle_update(&pm_ctx)) { > >>> + return false; > >>> + } > >>> + > >>> + engine_set_node_state(node, EN_UPDATED); > >>> + return true; > >>> + > >>> +} > >>> + > >>> /* Engine node which is used to handle the Non VIF data like > >>> * - OVS patch ports > >>> * - Tunnel ports and the related chassis information. > >>> @@ -3704,6 +3905,7 @@ main(int argc, char *argv[]) > >>> ENGINE_NODE_WITH_CLEAR_TRACK_DATA(port_groups, "port_groups"); > >>> ENGINE_NODE(northd_options, "northd_options"); > >>> ENGINE_NODE(dhcp_options, "dhcp_options"); > >>> + ENGINE_NODE(port_mirror, "port_mirror"); > >>> > >>> #define SB_NODE(NAME, NAME_STR) ENGINE_NODE_SB(NAME, NAME_STR); > >>> SB_NODES > >>> @@ -3862,6 +4064,22 @@ main(int argc, char *argv[]) > >>> engine_add_input(&en_flow_output, &en_pflow_output, > >>> flow_output_pflow_output_handler); > >>> > >>> + engine_add_input(&en_port_mirror, &en_ovs_open_vswitch, NULL); > >>> + engine_add_input(&en_port_mirror, &en_ovs_bridge, NULL); > >>> + engine_add_input(&en_port_mirror, &en_ovs_mirror, NULL); > >>> + engine_add_input(&en_port_mirror, &en_sb_chassis, NULL); > >>> + engine_add_input(&en_port_mirror, &en_ovs_port, > >>> engine_noop_handler); > >>> + engine_add_input(&en_port_mirror, &en_ovs_interface_shadow, > >>> + engine_noop_handler); > >>> + engine_add_input(&en_flow_output, &en_port_mirror, > >>> + engine_noop_handler); > >>> + engine_add_input(&en_port_mirror, &en_runtime_data, > >>> + port_mirror_runtime_data_handler); > >>> + engine_add_input(&en_port_mirror, &en_sb_mirror, > >>> + port_mirror_sb_mirror_handler); > >>> + engine_add_input(&en_port_mirror, &en_sb_port_binding, > >>> + port_mirror_port_binding_handler); > >>> + > >>> struct engine_arg engine_arg = { > >>> .sb_idl = ovnsb_idl_loop.idl, > >>> .ovs_idl = ovs_idl_loop.idl, > >>> @@ -4131,34 +4349,36 @@ main(int argc, char *argv[]) > >>> > >>> stopwatch_start(CONTROLLER_LOOP_STOPWATCH_NAME, > >>> time_msec()); > >>> - if (ovnsb_idl_txn) { > >>> - if (ofctrl_has_backlog()) { > >>> - /* When there are in-flight messages > >>> pending to > >>> - * ovs-vswitchd, we should hold on > >>> recomputing so > >>> - * that the previous flow installations > >>> won't be > >>> - * delayed. However, we still want to > >>> try if > >>> - * recompute is not needed and we can > >>> quickly > >>> - * incrementally process the new > >>> changes, to avoid > >>> - * unnecessarily forced recomputes > >>> later on. This > >>> - * is because the OVSDB change tracker > >>> cannot > >>> - * preserve tracked changes across > >>> iterations. If > >>> - * change tracking is improved, we can > >>> simply skip > >>> - * this round of engine_run and > >>> continue processing > >>> - * acculated changes incrementally > >>> later when > >>> - * ofctrl_has_backlog() returns false. */ > >>> - engine_run(false); > >>> - } else { > >>> - engine_run(true); > >>> - } > >>> - } else { > >>> - /* Even if there's no SB DB transaction > >>> available, > >>> + > >>> + bool allow_engine_recompute = true; > >>> + > >>> + if (!ovnsb_idl_txn || !ovs_idl_txn || > >>> + ofctrl_has_backlog()) { > >>> + /* When there are in-flight messages > >>> pending to > >>> + * ovs-vswitchd, we should hold on > >>> recomputing so > >>> + * that the previous flow installations > >>> won't be > >>> + * delayed. However, we still want to try if > >>> + * recompute is not needed and we can quickly > >>> + * incrementally process the new changes, > >>> to avoid > >>> + * unnecessarily forced recomputes later > >>> on. This > >>> + * is because the OVSDB change tracker cannot > >>> + * preserve tracked changes across > >>> iterations. If > >>> + * change tracking is improved, we can > >>> simply skip > >>> + * this round of engine_run and continue > >>> processing > >>> + * acculated changes incrementally later when > >>> + * ofctrl_has_backlog() returns false. */ > >>> + > >>> + /* Even if there's no SB/OVS DB transaction > >>> available, > >>> * try to run the engine so that we can > >>> handle any > >>> * incremental changes that don't require > >>> a recompute. > >>> * If a recompute is required, the engine > >>> will abort, > >>> * triggerring a full run in the next > >>> iteration. > >>> */ > >>> - engine_run(false); > >>> + allow_engine_recompute = false; > >>> } > >>> + > >>> + engine_run(allow_engine_recompute); > >>> + > >>> stopwatch_stop(CONTROLLER_LOOP_STOPWATCH_NAME, > >>> time_msec()); > >>> if (engine_has_updated()) { > >>> diff --git a/northd/en-northd.c b/northd/en-northd.c > >>> index 7fe83db64..608220b1f 100644 > >>> --- a/northd/en-northd.c > >>> +++ b/northd/en-northd.c > >>> @@ -80,6 +80,8 @@ void en_northd_run(struct engine_node *node, void > >>> *data) > >>> EN_OVSDB_GET(engine_get_input("NB_acl", node)); > >>> input_data.nbrec_static_mac_binding_table = > >>> EN_OVSDB_GET(engine_get_input("NB_static_mac_binding", node)); > >>> + input_data.nbrec_mirror_table = > >>> + EN_OVSDB_GET(engine_get_input("NB_mirror", node)); > >>> > >>> input_data.sbrec_sb_global_table = > >>> EN_OVSDB_GET(engine_get_input("SB_sb_global", node)); > >>> @@ -113,6 +115,8 @@ void en_northd_run(struct engine_node *node, > >>> void *data) > >>> EN_OVSDB_GET(engine_get_input("SB_chassis_private", node)); > >>> input_data.sbrec_static_mac_binding_table = > >>> EN_OVSDB_GET(engine_get_input("SB_static_mac_binding", node)); > >>> + input_data.sbrec_mirror_table = > >>> + EN_OVSDB_GET(engine_get_input("SB_mirror", node)); > >>> > >>> northd_run(&input_data, data, > >>> eng_ctx->ovnnb_idl_txn, > >>> diff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c > >>> index 54e0ad3b0..ac27a730e 100644 > >>> --- a/northd/inc-proc-northd.c > >>> +++ b/northd/inc-proc-northd.c > >>> @@ -50,6 +50,7 @@ VLOG_DEFINE_THIS_MODULE(inc_proc_northd); > >>> NB_NODE(acl, "acl") \ > >>> NB_NODE(logical_router, "logical_router") \ > >>> NB_NODE(qos, "qos") \ > >>> + NB_NODE(mirror, "mirror") \ > >>> NB_NODE(meter, "meter") \ > >>> NB_NODE(meter_band, "meter_band") \ > >>> NB_NODE(logical_router_port, "logical_router_port") \ > >>> @@ -92,6 +93,7 @@ VLOG_DEFINE_THIS_MODULE(inc_proc_northd); > >>> SB_NODE(logical_flow, "logical_flow") \ > >>> SB_NODE(logical_dp_group, "logical_DP_group") \ > >>> SB_NODE(multicast_group, "multicast_group") \ > >>> + SB_NODE(mirror, "mirror") \ > >>> SB_NODE(meter, "meter") \ > >>> SB_NODE(meter_band, "meter_band") \ > >>> SB_NODE(datapath_binding, "datapath_binding") \ > >>> @@ -172,6 +174,7 @@ void inc_proc_northd_init(struct ovsdb_idl_loop > >>> *nb, > >>> engine_add_input(&en_northd, &en_nb_acl, NULL); > >>> engine_add_input(&en_northd, &en_nb_logical_router, NULL); > >>> engine_add_input(&en_northd, &en_nb_qos, NULL); > >>> + engine_add_input(&en_northd, &en_nb_mirror, NULL); > >>> engine_add_input(&en_northd, &en_nb_meter, NULL); > >>> engine_add_input(&en_northd, &en_nb_meter_band, NULL); > >>> engine_add_input(&en_northd, &en_nb_logical_router_port, NULL); > >>> @@ -194,6 +197,7 @@ void inc_proc_northd_init(struct ovsdb_idl_loop > >>> *nb, > >>> engine_add_input(&en_northd, &en_sb_address_set, NULL); > >>> engine_add_input(&en_northd, &en_sb_port_group, NULL); > >>> engine_add_input(&en_northd, &en_sb_logical_dp_group, NULL); > >>> + engine_add_input(&en_northd, &en_sb_mirror, NULL); > >>> engine_add_input(&en_northd, &en_sb_meter, NULL); > >>> engine_add_input(&en_northd, &en_sb_meter_band, NULL); > >>> engine_add_input(&en_northd, &en_sb_datapath_binding, NULL); > >>> diff --git a/northd/northd.c b/northd/northd.c > >>> index b7388afc5..52abdda28 100644 > >>> --- a/northd/northd.c > >>> +++ b/northd/northd.c > >>> @@ -3248,6 +3248,89 @@ ovn_port_update_sbrec_chassis( > >>> free(requested_chassis_sb); > >>> } > >>> > >>> +static void > >>> +do_sb_mirror_addition(struct northd_input *input_data, > >>> + const struct ovn_port *op) > >>> +{ > >>> + for (size_t i = 0; i < op->nbsp->n_mirror_rules; i++) { > >>> + const struct sbrec_mirror *sb_mirror; > >>> + SBREC_MIRROR_TABLE_FOR_EACH (sb_mirror, > >>> + input_data->sbrec_mirror_table) { > >>> + if (!strcmp(sb_mirror->name, > >>> + op->nbsp->mirror_rules[i]->name)) { > >>> + /* Add the value to SB */ > >>> + sbrec_port_binding_update_mirror_rules_addvalue(op->sb, > >>> + sb_mirror); > >>> + } > >>> + } > >>> + } > >>> +} > >>> + > >>> +static void > >>> +sbrec_port_binding_update_mirror_rules(struct northd_input > >>> *input_data, > >>> + const struct ovn_port *op) > >>> +{ > >>> + size_t i = 0; > >>> + if (op->sb->n_mirror_rules > op->nbsp->n_mirror_rules) { > >>> + /* Needs deletion in SB */ > >>> + struct shash nb_mirror_rules = > >>> SHASH_INITIALIZER(&nb_mirror_rules); > >>> + for (i = 0; i < op->nbsp->n_mirror_rules; i++) { > >>> + shash_add(&nb_mirror_rules, > >>> + op->nbsp->mirror_rules[i]->name, > >>> + op->nbsp->mirror_rules[i]); > >>> + } > >>> + > >>> + for (i = 0; i < op->sb->n_mirror_rules; i++) { > >>> + if (!shash_find(&nb_mirror_rules, > >>> + op->sb->mirror_rules[i]->name)) { > >>> + sbrec_port_binding_update_mirror_rules_delvalue(op->sb, > >>> + op->sb->mirror_rules[i]); > >>> + } > >>> + } > >>> + > >>> + struct shash_node *node, *next; > >>> + SHASH_FOR_EACH_SAFE (node, next, &nb_mirror_rules) { > >>> + shash_delete(&nb_mirror_rules, node); > >>> + } > >>> + shash_destroy(&nb_mirror_rules); > >>> + > >>> + } else if (op->sb->n_mirror_rules < op->nbsp->n_mirror_rules) { > >>> + /* Needs addition in SB */ > >>> + do_sb_mirror_addition(input_data, op); > >>> + } else if (op->sb->n_mirror_rules == op->nbsp->n_mirror_rules) { > >>> + /* > >>> + * Check if its the same mirrors on both SB and NB DBs > >>> + * If not update accordingly. > >>> + */ > >>> + bool needs_sb_addition = false; > >>> + struct shash nb_mirror_rules = > >>> SHASH_INITIALIZER(&nb_mirror_rules); > >>> + for (i = 0; i < op->nbsp->n_mirror_rules; i++) { > >>> + shash_add(&nb_mirror_rules, > >>> + op->nbsp->mirror_rules[i]->name, > >>> + op->nbsp->mirror_rules[i]); > >>> + } > >>> + > >>> + for (i = 0; i < op->sb->n_mirror_rules; i++) { > >>> + if (!shash_find(&nb_mirror_rules, > >>> + op->sb->mirror_rules[i]->name)) { > >>> + sbrec_port_binding_update_mirror_rules_delvalue(op->sb, > >>> + op->sb->mirror_rules[i]); > >>> + needs_sb_addition = true; > >>> + } > >>> + } > >>> + > >>> + struct shash_node *node, *next; > >>> + SHASH_FOR_EACH_SAFE (node, next, &nb_mirror_rules) { > >>> + shash_delete(&nb_mirror_rules, node); > >>> + } > >>> + shash_destroy(&nb_mirror_rules); > >>> + > >>> + if (needs_sb_addition) { > >>> + do_sb_mirror_addition(input_data, op); > >>> + } > >>> + } > >>> +} > >>> + > >>> static void > >>> ovn_port_update_sbrec(struct northd_input *input_data, > >>> struct ovsdb_idl_txn *ovnsb_txn, > >>> @@ -3607,6 +3690,15 @@ ovn_port_update_sbrec(struct northd_input > >>> *input_data, > >>> } > >>> sbrec_port_binding_set_external_ids(op->sb, &ids); > >>> smap_destroy(&ids); > >>> + > >>> + if (!op->nbsp->n_mirror_rules) { > >>> + /* Nothing is set. Clear mirror_rules from pb. */ > >>> + sbrec_port_binding_set_mirror_rules(op->sb, NULL, 0); > >>> + } else { > >>> + /* Check if SB DB update needed */ > >>> + sbrec_port_binding_update_mirror_rules(input_data, op); > >>> + } > >>> + > >>> } > >>> if (op->tunnel_key != op->sb->tunnel_key) { > >>> sbrec_port_binding_set_tunnel_key(op->sb, op->tunnel_key); > >>> @@ -15014,6 +15106,85 @@ sync_meters(struct northd_input *input_data, > >>> shash_destroy(&sb_meters); > >>> } > >>> > >>> +static bool > >>> +mirror_needs_update(const struct nbrec_mirror *nb_mirror, > >>> + const struct sbrec_mirror *sb_mirror) > >>> +{ > >>> + > >>> + if (nb_mirror->index != sb_mirror->index) { > >>> + return true; > >>> + } else if (strcmp(nb_mirror->sink, sb_mirror->sink)) { > >>> + return true; > >>> + } else if (strcmp(nb_mirror->type, sb_mirror->type)) { > >>> + return true; > >>> + } else if (strcmp(nb_mirror->filter, sb_mirror->filter)) { > >>> + return true; > >>> + } > >>> + > >>> + return false; > >>> +} > >>> + > >>> +static void > >>> +sync_mirrors_iterate_nb_mirror(struct ovsdb_idl_txn *ovnsb_txn, > >>> + const char *mirror_name, > >>> + const struct nbrec_mirror *nb_mirror, > >>> + struct shash *sb_mirrors, > >>> + struct sset *used_sb_mirrors) > >>> +{ > >>> + const struct sbrec_mirror *sb_mirror; > >>> + bool new_sb_mirror = false; > >>> + > >>> + sb_mirror = shash_find_data(sb_mirrors, mirror_name); > >>> + if (!sb_mirror) { > >>> + sb_mirror = sbrec_mirror_insert(ovnsb_txn); > >>> + sbrec_mirror_set_name(sb_mirror, mirror_name); > >>> + shash_add(sb_mirrors, sb_mirror->name, sb_mirror); > >>> + new_sb_mirror = true; > >>> + } > >>> + sset_add(used_sb_mirrors, mirror_name); > >>> + > >>> + if ((new_sb_mirror) || mirror_needs_update(nb_mirror, > >>> sb_mirror)) { > >>> + sbrec_mirror_set_filter(sb_mirror,nb_mirror->filter); > >>> + sbrec_mirror_set_index(sb_mirror, nb_mirror->index); > >>> + sbrec_mirror_set_sink(sb_mirror, nb_mirror->sink); > >>> + sbrec_mirror_set_type(sb_mirror, nb_mirror->type); > >>> + } > >>> +} > >>> + > >>> +static void > >>> +sync_mirrors(struct northd_input *input_data, > >>> + struct ovsdb_idl_txn *ovnsb_txn) > >>> +{ > >>> + struct shash sb_mirrors = SHASH_INITIALIZER(&sb_mirrors); > >>> + struct sset used_sb_mirrors = SSET_INITIALIZER(&used_sb_mirrors); > >>> + > >>> + const struct sbrec_mirror *sb_mirror; > >>> + SBREC_MIRROR_TABLE_FOR_EACH (sb_mirror, > >>> input_data->sbrec_mirror_table) { > >>> + shash_add(&sb_mirrors, sb_mirror->name, sb_mirror); > >>> + } > >>> + > >>> + const struct nbrec_mirror *nb_mirror; > >>> + NBREC_MIRROR_TABLE_FOR_EACH (nb_mirror, > >>> input_data->nbrec_mirror_table) { > >>> + sync_mirrors_iterate_nb_mirror(ovnsb_txn, nb_mirror->name, > >>> nb_mirror, > >>> + &sb_mirrors, &used_sb_mirrors); > >>> + } > >>> + > >>> + const char *used_mirror; > >>> + const char *used_mirror_next; > >>> + SSET_FOR_EACH_SAFE (used_mirror, used_mirror_next, > >>> &used_sb_mirrors) { > >>> + shash_find_and_delete(&sb_mirrors, used_mirror); > >>> + sset_delete(&used_sb_mirrors, > >>> SSET_NODE_FROM_NAME(used_mirror)); > >>> + } > >>> + sset_destroy(&used_sb_mirrors); > >>> + > >>> + struct shash_node *node, *next; > >>> + SHASH_FOR_EACH_SAFE (node, next, &sb_mirrors) { > >>> + sbrec_mirror_delete(node->data); > >>> + shash_delete(&sb_mirrors, node); > >>> + } > >>> + shash_destroy(&sb_mirrors); > >>> +} > >>> + > >>> /* > >>> * struct 'dns_info' is used to sync the DNS records between OVN > >>> Northbound db > >>> * and Southbound db. > >>> @@ -15644,6 +15815,7 @@ ovnnb_db_run(struct northd_input *input_data, > >>> sync_address_sets(input_data, ovnsb_txn, &data->datapaths); > >>> sync_port_groups(input_data, ovnsb_txn, &data->port_groups); > >>> sync_meters(input_data, ovnsb_txn, &data->meter_groups); > >>> + sync_mirrors(input_data, ovnsb_txn); > >>> sync_dns_entries(input_data, ovnsb_txn, &data->datapaths); > >>> cleanup_stale_fdb_entries(input_data, &data->datapaths); > >>> stopwatch_stop(CLEAR_LFLOWS_CTX_STOPWATCH_NAME, time_msec()); > >>> diff --git a/northd/northd.h b/northd/northd.h > >>> index da90e2815..17a62ea10 100644 > >>> --- a/northd/northd.h > >>> +++ b/northd/northd.h > >>> @@ -36,6 +36,7 @@ struct northd_input { > >>> const struct nbrec_acl_table *nbrec_acl_table; > >>> const struct nbrec_static_mac_binding_table > >>> *nbrec_static_mac_binding_table; > >>> + const struct nbrec_mirror_table *nbrec_mirror_table; > >>> > >>> /* Southbound table references */ > >>> const struct sbrec_sb_global_table *sbrec_sb_global_table; > >>> @@ -55,6 +56,7 @@ struct northd_input { > >>> const struct sbrec_chassis_private_table > >>> *sbrec_chassis_private_table; > >>> const struct sbrec_static_mac_binding_table > >>> *sbrec_static_mac_binding_table; > >>> + const struct sbrec_mirror_table *sbrec_mirror_table; > >>> > >>> /* Indexes */ > >>> struct ovsdb_idl_index *sbrec_chassis_by_name; > >>> diff --git a/ovn-nb.ovsschema b/ovn-nb.ovsschema > >>> index 174364c8b..01de55222 100644 > >>> --- a/ovn-nb.ovsschema > >>> +++ b/ovn-nb.ovsschema > >>> @@ -1,7 +1,7 @@ > >>> { > >>> "name": "OVN_Northbound", > >>> - "version": "6.3.0", > >>> - "cksum": "4042813038 31869", > >>> + "version": "6.4.0", > >>> + "cksum": "589874483 33352", > >>> "tables": { > >>> "NB_Global": { > >>> "columns": { > >>> @@ -132,6 +132,11 @@ > >>> "refType": "weak"}, > >>> "min": 0, > >>> "max": 1}}, > >>> + "mirror_rules": {"type": {"key": {"type": "uuid", > >>> + "refTable": "Mirror", > >>> + "refType": "weak"}, > >>> + "min": 0, > >>> + "max": "unlimited"}}, > >>> "ha_chassis_group": { > >>> "type": {"key": {"type": "uuid", > >>> "refTable": "HA_Chassis_Group", > >>> @@ -301,6 +306,28 @@ > >>> "type": {"key": "string", "value": "string", > >>> "min": 0, "max": "unlimited"}}}, > >>> "isRoot": false}, > >>> + "Mirror": { > >>> + "columns": { > >>> + "name": {"type": "string"}, > >>> + "filter": {"type": {"key": {"type": "string", > >>> + "enum": ["set", > >>> ["from-lport", > >>> + "to-lport", > >>> + "both"]]}}}, > >>> + "sink":{"type": "string"}, > >>> + "type": {"type": {"key": {"type": "string", > >>> + "enum": ["set", ["gre", > >>> + "erspan"]]}}}, > >>> + "index": {"type": "integer"}, > >>> + "src": {"type": {"key": {"type": "uuid", > >>> + "refTable": > >>> "Logical_Switch_Port", > >>> + "refType": "weak"}, > >>> + "min": 0, > >>> + "max": "unlimited"}}, > >>> + "external_ids": { > >>> + "type": {"key": "string", "value": "string", > >>> + "min": 0, "max": "unlimited"}}}, > >>> + "indexes": [["name"]], > >>> + "isRoot": true}, > >>> "Meter": { > >>> "columns": { > >>> "name": {"type": "string"}, > >>> diff --git a/ovn-nb.xml b/ovn-nb.xml > >>> index f41e9d7c0..d8730c8fc 100644 > >>> --- a/ovn-nb.xml > >>> +++ b/ovn-nb.xml > >>> @@ -1554,6 +1554,11 @@ > >>> </column> > >>> </group> > >>> > >>> + <column name="mirror_rules"> > >>> + Mirror rules that apply to logical switch port which is the > >>> source. > >>> + Please see the <ref table="Mirror"/> table. > >>> + </column> > >>> + > >>> <column name="ha_chassis_group"> > >>> References a row in the OVN Northbound database's > >>> <ref table="HA_Chassis_Group" db="OVN_Northbound"/> table. > >>> @@ -2491,6 +2496,64 @@ > >>> </column> > >>> </table> > >>> > >>> + <table name="Mirror" title="Mirror Entry"> > >>> + <p> > >>> + Each row in this table represents one Mirror that can be used > >>> for > >>> + port mirroring. These Mirrors are referenced by the > >>> + <ref column="mirror_rules" table="Logical_Switch_Port"/> > >>> column in > >>> + the <ref table="Logical_Switch_Port"/> table. > >>> + </p> > >>> + > >>> + <column name="name"> > >>> + <p> > >>> + Represents the name of the mirror. > >>> + </p> > >>> + </column> > >>> + > >>> + <column name="filter"> > >>> + <p> > >>> + The value of this field represents selection criteria of > >>> the mirror. > >>> + Supported values for filter to-lport / from-lport / both > >>> + to-lport - to mirror packets coming into logical port > >>> + from-lport - to mirror packets going out of logical port > >>> + both - to mirror packets coming into and going out of > >>> logical port. > >>> + </p> > >>> + </column> > >>> + > >>> + <column name="sink"> > >>> + <p> > >>> + The value of this field represents the destination/sink of > >>> the mirror. > >>> + The value it takes is an IP address of the sink port. > >>> + </p> > >>> + </column> > >>> + > >>> + <column name="type"> > >>> + <p> > >>> + The value of this field represents the type of the tunnel > >>> used for > >>> + sending the mirrored packets. Supported Tunnel types gre > >>> and erspan > >>> + </p> > >>> + </column> > >>> + > >>> + <column name="index"> > >>> + <p> > >>> + The value of this field represents the tunnel ID. Depending > >>> on the > >>> + tunnel type configured, GRE key value if type GRE and > >>> erspan_idx value > >>> + if ERSPAN > >>> + </p> > >>> + </column> > >>> + > >>> + <column name="src"> > >>> + <p> > >>> + The value of this field represents a list of source ports > >>> for the > >>> + mirror. Please see the <ref table="Logical_Switch_Port"/> > >>> table. > >>> + </p> > >>> + </column> > >>> + > >>> + <column name="external_ids"> > >>> + See <em>External IDs</em> at the beginning of this document. > >>> + </column> > >>> + </table> > >>> + > >>> <table name="Meter" title="Meter entry"> > >>> <p> > >>> Each row in this table represents a meter that can be used > >>> for QoS or > >>> diff --git a/ovn-sb.ovsschema b/ovn-sb.ovsschema > >>> index 576ebbdeb..b83134416 100644 > >>> --- a/ovn-sb.ovsschema > >>> +++ b/ovn-sb.ovsschema > >>> @@ -1,7 +1,7 @@ > >>> { > >>> "name": "OVN_Southbound", > >>> - "version": "20.25.0", > >>> - "cksum": "53184112 28845", > >>> + "version": "20.26.0", > >>> + "cksum": "2344151793 30004", > >>> "tables": { > >>> "SB_Global": { > >>> "columns": { > >>> @@ -142,6 +142,23 @@ > >>> "indexes": [["datapath", "tunnel_key"], > >>> ["datapath", "name"]], > >>> "isRoot": true}, > >>> + "Mirror": { > >>> + "columns": { > >>> + "name": {"type": "string"}, > >>> + "filter": {"type": {"key": {"type": "string", > >>> + "enum": ["set", > >>> + ["from-lport", > >>> + "to-lport","both"]]}}}, > >>> + "sink":{"type": "string"}, > >>> + "type": {"type": {"key": {"type": "string", > >>> + "enum": ["set", > >>> + ["gre", > >>> "erspan"]]}}}, > >>> + "index": {"type": "integer"}, > >>> + "external_ids": { > >>> + "type": {"key": "string", "value": "string", > >>> + "min": 0, "max": "unlimited"}}}, > >>> + "indexes": [["name"]], > >>> + "isRoot": true}, > >>> "Meter": { > >>> "columns": { > >>> "name": {"type": "string"}, > >>> @@ -230,6 +247,11 @@ > >>> "refTable": "Encap", > >>> "refType": "weak"}, > >>> "min": 0, "max": "unlimited"}}, > >>> + "mirror_rules": {"type": {"key": {"type": "uuid", > >>> + "refTable": "Mirror", > >>> + "refType": "weak"}, > >>> + "min": 0, > >>> + "max": "unlimited"}}, > >>> "mac": {"type": {"key": "string", > >>> "min": 0, > >>> "max": "unlimited"}}, > >>> diff --git a/ovn-sb.xml b/ovn-sb.xml > >>> index 315d60853..05c0db6b4 100644 > >>> --- a/ovn-sb.xml > >>> +++ b/ovn-sb.xml > >>> @@ -2742,6 +2742,51 @@ tcp.flags = RST; > >>> </column> > >>> </table> > >>> > >>> + <table name="Mirror" title="Mirror Entry"> > >>> + <p> > >>> + Each row in this table represents one Mirror that can be used > >>> for > >>> + port mirroring. These Mirrors are referenced by the > >>> + <ref column="mirror_rules" table="Port_Binding"/> column in > >>> + the <ref table="Port_Binding"/> table. > >>> + </p> > >>> + > >>> + <column name="name"> > >>> + <p> > >>> + Represents the name of the mirror. > >>> + </p> > >>> + </column> > >>> + > >>> + <column name="filter"> > >>> + <p> > >>> + The value of this field represents selection criteria of > >>> the mirror. > >>> + </p> > >>> + </column> > >>> + > >>> + <column name="sink"> > >>> + <p> > >>> + The value of this field represents the destination/sink of > >>> the mirror. > >>> + </p> > >>> + </column> > >>> + > >>> + <column name="type"> > >>> + <p> > >>> + The value of this field represents the type of the tunnel > >>> used for > >>> + sending the mirrored packets > >>> + </p> > >>> + </column> > >>> + > >>> + <column name="index"> > >>> + <p> > >>> + The value of this field represents the key/idx depending on > >>> the > >>> + tunnel type configured > >>> + </p> > >>> + </column> > >>> + > >>> + <column name="external_ids"> > >>> + See <em>External IDs</em> at the beginning of this document. > >>> + </column> > >>> + </table> > >>> + > >>> <table name="Meter" title="Meter entry"> > >>> <p> > >>> Each row in this table represents a meter that can be used > >>> for QoS or > >>> @@ -3244,6 +3289,11 @@ tcp.flags = RST; > >>> </column> > >>> </group> > >>> > >>> + <column name="mirror_rules"> > >>> + Mirror rules that apply to the port binding. > >>> + Please see the <ref table="Mirror"/> table. > >>> + </column> > >>> + > >>> <group title="Patch Options"> > >>> <p> > >>> These options apply to logical ports with <ref > >>> column="type"/> of > >>> diff --git a/tests/ovn-nbctl.at b/tests/ovn-nbctl.at > >>> index 4d480e357..d79f9d929 100644 > >>> --- a/tests/ovn-nbctl.at > >>> +++ b/tests/ovn-nbctl.at > >>> @@ -435,6 +435,126 @@ AT_CHECK([ovn-nbctl meter-list], [0], [dnl > >>> > >>> dnl > >>> --------------------------------------------------------------------- > >>> > >>> +OVN_NBCTL_TEST([ovn_nbctl_mirrors], [mirrors], [ > >>> +AT_CHECK([ovn-nbctl mirror-add mirror1 gre 0 from-lport 10.10.10.1]) > >>> +AT_CHECK([ovn-nbctl mirror-add mirror2 erspan 1 both 10.10.10.2]) > >>> +AT_CHECK([ovn-nbctl mirror-add mirror3 gre 2 to-lport 10.10.10.3]) > >>> +AT_CHECK([ovn-nbctl ls-add sw0]) > >>> +AT_CHECK([ovn-nbctl lsp-add sw0 sw0-port1]) > >>> +AT_CHECK([ovn-nbctl lsp-add sw0 sw0-port2]) > >>> +AT_CHECK([ovn-nbctl lsp-add sw0 sw0-port3]) > >>> + > >>> +dnl Add duplicate mirror name > >>> +AT_CHECK([ovn-nbctl mirror-add mirror1 gre 0 from-lport > >>> 10.10.10.5], [1], [], [stderr]) > >>> +AT_CHECK([grep 'already exists' stderr], [0], [ignore]) > >>> + > >>> +dnl Attach invalid source port to mirror > >>> +AT_CHECK([ovn-nbctl lsp-attach-mirror sw0-port4 mirror3], [1], [], > >>> [stderr]) > >>> +AT_CHECK([grep 'port name not found' stderr], [0], [ignore]) > >>> + > >>> +dnl Attach source port to invalid mirror > >>> +AT_CHECK([ovn-nbctl lsp-attach-mirror sw0-port3 mirror4], [1], [], > >>> [stderr]) > >>> +AT_CHECK([grep 'mirror name not found' stderr], [0], [ignore]) > >>> + > >>> +dnl Attach source port to mirror > >>> +AT_CHECK([ovn-nbctl lsp-attach-mirror sw0-port1 mirror3]) > >>> + > >>> +dnl Attach one more source port to mirror > >>> +AT_CHECK([ovn-nbctl lsp-attach-mirror sw0-port3 mirror3]) > >>> + > >>> +dnl Verify if multiple ports are attached to the same mirror properly > >>> +AT_CHECK([ovn-nbctl mirror-list], [0], [dnl > >>> +mirror1: > >>> + Type : gre > >>> + Sink : 10.10.10.1 > >>> + Filter : from-lport > >>> + Index/Key: 0 > >>> + Sources : None attached > >>> +mirror2: > >>> + Type : erspan > >>> + Sink : 10.10.10.2 > >>> + Filter : both > >>> + Index/Key: 1 > >>> + Sources : None attached > >>> +mirror3: > >>> + Type : gre > >>> + Sink : 10.10.10.3 > >>> + Filter : to-lport > >>> + Index/Key: 2 > >>> + Sources : sw0-port1 sw0-port3 > >>> +]) > >>> + > >>> +dnl Detach one source port from mirror > >>> +AT_CHECK([ovn-nbctl lsp-detach-mirror sw0-port3 mirror3]) > >>> + > >>> +dnl Verify if detach source port from mirror happens properly > >>> +AT_CHECK([ovn-nbctl mirror-list], [0], [dnl > >>> +mirror1: > >>> + Type : gre > >>> + Sink : 10.10.10.1 > >>> + Filter : from-lport > >>> + Index/Key: 0 > >>> + Sources : None attached > >>> +mirror2: > >>> + Type : erspan > >>> + Sink : 10.10.10.2 > >>> + Filter : both > >>> + Index/Key: 1 > >>> + Sources : None attached > >>> +mirror3: > >>> + Type : gre > >>> + Sink : 10.10.10.3 > >>> + Filter : to-lport > >>> + Index/Key: 2 > >>> + Sources : sw0-port1 > >>> +]) > >>> + > >>> +dnl Delete a single mirror which has source attached. > >>> +AT_CHECK([ovn-nbctl mirror-del mirror3]) > >>> + > >>> +dnl Check if the detach happened from source properly > >>> +AT_CHECK([ovn-nbctl get Logical_Switch_Port sw0-port1 mirror_rules > >>> | cut -b 3], [0], [dnl > >>> + > >>> +]) > >>> + > >>> +dnl Check if the mirror deleted properly > >>> +AT_CHECK([ovn-nbctl mirror-list], [0], [dnl > >>> +mirror1: > >>> + Type : gre > >>> + Sink : 10.10.10.1 > >>> + Filter : from-lport > >>> + Index/Key: 0 > >>> + Sources : None attached > >>> +mirror2: > >>> + Type : erspan > >>> + Sink : 10.10.10.2 > >>> + Filter : both > >>> + Index/Key: 1 > >>> + Sources : None attached > >>> +]) > >>> + > >>> +dnl Delete another mirror > >>> +AT_CHECK([ovn-nbctl mirror-del mirror2]) > >>> + > >>> +dnl Update the Sink address > >>> +AT_CHECK([ovn-nbctl set mirror . sink=192.168.1.13]) > >>> + > >>> +AT_CHECK([ovn-nbctl mirror-list], [0], [dnl > >>> +mirror1: > >>> + Type : gre > >>> + Sink : 192.168.1.13 > >>> + Filter : from-lport > >>> + Index/Key: 0 > >>> + Sources : None attached > >>> +]) > >>> + > >>> +dnl Delete all mirrors > >>> +AT_CHECK([ovn-nbctl mirror-del]) > >>> +AT_CHECK([ovn-nbctl mirror-list], [0], [dnl > >>> +])]) > >>> + > >>> +dnl > >>> --------------------------------------------------------------------- > >>> + > >>> OVN_NBCTL_TEST([ovn_nbctl_nats], [NATs], [ > >>> AT_CHECK([ovn-nbctl lr-add lr0]) > >>> AT_CHECK([ovn-nbctl lr-nat-add lr0 snatt 30.0.0.2 192.168.1.2], > >>> [1], [], > >>> diff --git a/tests/ovn-northd.at b/tests/ovn-northd.at > >>> index 4f399eccb..4e6c268e4 100644 > >>> --- a/tests/ovn-northd.at > >>> +++ b/tests/ovn-northd.at > >>> @@ -2319,6 +2319,108 @@ check_meter_by_name NOT meter_me__${acl1} > >>> meter_me__${acl2} > >>> AT_CLEANUP > >>> ]) > >>> > >>> +OVN_FOR_EACH_NORTHD_NO_HV([ > >>> +AT_SETUP([Check NB-SB mirrors sync]) > >>> +AT_KEYWORDS([mirrors]) > >>> +ovn_start > >>> + > >>> +check ovn-nbctl --wait=sb mirror-add mirror1 erspan 0 both 10.10.10.2 > >>> + > >>> +AT_CHECK([ovn-sbctl get Mirror . Sink], [0], [dnl > >>> +"10.10.10.2" > >>> +]) > >>> + > >>> +AT_CHECK([ovn-sbctl get Mirror . type], [0], [dnl > >>> +erspan > >>> +]) > >>> + > >>> +AT_CHECK([ovn-sbctl get Mirror . index], [0], [dnl > >>> +0 > >>> +]) > >>> + > >>> +AT_CHECK([ovn-sbctl get Mirror . filter], [0], [dnl > >>> +both > >>> +]) > >>> + > >>> +check ovn-nbctl --wait=sb \ > >>> + -- set mirror . sink=192.168.1.13 > >>> + > >>> +AT_CHECK([ovn-sbctl get Mirror . Sink], [0], [dnl > >>> +"192.168.1.13" > >>> +]) > >>> + > >>> +AT_CHECK([ovn-sbctl get Mirror . type], [0], [dnl > >>> +erspan > >>> +]) > >>> + > >>> +AT_CHECK([ovn-sbctl get Mirror . index], [0], [dnl > >>> +0 > >>> +]) > >>> + > >>> +AT_CHECK([ovn-sbctl get Mirror . filter], [0], [dnl > >>> +both > >>> +]) > >>> + > >>> +check ovn-nbctl --wait=sb \ > >>> + -- set mirror . type=gre > >>> + > >>> +AT_CHECK([ovn-sbctl get Mirror . type], [0], [dnl > >>> +gre > >>> +]) > >>> + > >>> +AT_CHECK([ovn-sbctl get Mirror . Sink], [0], [dnl > >>> +"192.168.1.13" > >>> +]) > >>> + > >>> +AT_CHECK([ovn-sbctl get Mirror . index], [0], [dnl > >>> +0 > >>> +]) > >>> + > >>> +AT_CHECK([ovn-sbctl get Mirror . filter], [0], [dnl > >>> +both > >>> +]) > >>> + > >>> +check ovn-nbctl --wait=sb \ > >>> + -- set mirror . index=12 > >>> + > >>> +AT_CHECK([ovn-sbctl get Mirror . index], [0], [dnl > >>> +12 > >>> +]) > >>> + > >>> +AT_CHECK([ovn-sbctl get Mirror . type], [0], [dnl > >>> +gre > >>> +]) > >>> + > >>> +AT_CHECK([ovn-sbctl get Mirror . Sink], [0], [dnl > >>> +"192.168.1.13" > >>> +]) > >>> + > >>> +AT_CHECK([ovn-sbctl get Mirror . filter], [0], [dnl > >>> +both > >>> +]) > >>> + > >>> +check ovn-nbctl --wait=sb \ > >>> + -- set mirror . filter=to-lport > >>> + > >>> +AT_CHECK([ovn-sbctl get Mirror . filter], [0], [dnl > >>> +to-lport > >>> +]) > >>> + > >>> +AT_CHECK([ovn-sbctl get Mirror . index], [0], [dnl > >>> +12 > >>> +]) > >>> + > >>> +AT_CHECK([ovn-sbctl get Mirror . type], [0], [dnl > >>> +gre > >>> +]) > >>> + > >>> +AT_CHECK([ovn-sbctl get Mirror . Sink], [0], [dnl > >>> +"192.168.1.13" > >>> +]) > >>> + > >>> +AT_CLEANUP > >>> +]) > >>> + > >>> OVN_FOR_EACH_NORTHD_NO_HV([ > >>> AT_SETUP([ACL skip hints for stateless config]) > >>> AT_KEYWORDS([acl]) > >>> diff --git a/tests/ovn.at b/tests/ovn.at > >>> index f8b8db4df..cd5527ea1 100644 > >>> --- a/tests/ovn.at > >>> +++ b/tests/ovn.at > >>> @@ -16121,6 +16121,784 @@ OVN_CLEANUP([hv1], [hv2]) > >>> AT_CLEANUP > >>> ]) > >>> > >>> +OVN_FOR_EACH_NORTHD([ > >>> +AT_SETUP([Mirror]) > >>> +AT_KEYWORDS([Mirror]) > >>> +ovn_start > >>> + > >>> +# Logical network: > >>> +# One LR - R1 has switch ls1 (191.168.1.0/24) connected to it, > >>> +# and has switch ls2 (172.16.1.0/24) connected to it. > >>> + > >>> +ovn-nbctl lr-add R1 > >>> + > >>> +ovn-nbctl ls-add ls1 > >>> +ovn-nbctl ls-add ls2 > >>> + > >>> +# Connect ls1 to R1 > >>> +ovn-nbctl lrp-add R1 ls1 00:00:00:01:02:f1 192.168.1.1/24 > >>> +ovn-nbctl lsp-add ls1 rp-ls1 -- set Logical_Switch_Port rp-ls1 \ > >>> + type=router options:router-port=ls1 > >>> addresses=\"00:00:00:01:02:f1\" > >>> + > >>> +# Connect ls2 to R1 > >>> +ovn-nbctl lrp-add R1 ls2 00:00:00:01:02:f2 172.16.1.1/24 > >>> +ovn-nbctl lsp-add ls2 rp-ls2 -- set Logical_Switch_Port rp-ls2 \ > >>> + type=router options:router-port=ls2 > >>> addresses=\"00:00:00:01:02:f2\" > >>> + > >>> +# Create logical port ls1-lp1 in ls1 > >>> +ovn-nbctl lsp-add ls1 ls1-lp1 \ > >>> +-- lsp-set-addresses ls1-lp1 "00:00:00:01:02:03 192.168.1.2" > >>> + > >>> +# Create logical port ls2-lp1 in ls2 > >>> +ovn-nbctl lsp-add ls2 ls2-lp1 \ > >>> +-- lsp-set-addresses ls2-lp1 "00:00:00:01:02:04 172.16.1.2" > >>> + > >>> +ovn-nbctl lsp-add ls1 ln-public > >>> +ovn-nbctl lsp-set-type ln-public localnet > >>> +ovn-nbctl lsp-set-addresses ln-public unknown > >>> +ovn-nbctl lsp-set-options ln-public network_name=public > >>> + > >>> +# Create one hypervisor and create OVS ports corresponding to > >>> logical ports. > >>> +net_add n1 > >>> + > >>> +sim_add hv1 > >>> +as hv1 > >>> +ovs-vsctl add-br br-phys -- set bridge br-phys > >>> other-config:hwaddr=\"00:00:00:01:02:00\" > >>> +ovn_attach n1 br-phys 192.168.1.11 > >>> + > >>> +ovs-vsctl -- add-port br-int vif1 -- \ > >>> + set interface vif1 external-ids:iface-id=ls1-lp1 \ > >>> + options:tx_pcap=hv1/vif1-tx.pcap \ > >>> + options:rxq_pcap=hv1/vif1-rx.pcap \ > >>> + ofport-request=1 > >>> + > >>> +ovs-vsctl -- add-port br-int vif2 -- \ > >>> + set interface vif2 external-ids:iface-id=ls2-lp1 \ > >>> + options:tx_pcap=hv1/vif2-tx.pcap \ > >>> + options:rxq_pcap=hv1/vif2-rx.pcap \ > >>> + ofport-request=1 > >>> + > >>> +ovs-vsctl set open . external-ids:ovn-bridge-mappings=public:br-phys > >>> + > >>> +# Allow some time for ovn-northd and ovn-controller to catch up. > >>> +wait_for_ports_up > >>> +check ovn-nbctl --wait=hv sync > >>> +ovn-nbctl dump-flows > sbflows > >>> +AT_CAPTURE_FILE([sbflows]) > >>> + > >>> +for i in 1 2; do > >>> + : > vif$i.expected > >>> +done > >>> + > >>> +net_add n2 > >>> + > >>> +sim_add hv2 > >>> +as hv2 > >>> +ovs-vsctl add-br br-phys -- set bridge br-phys > >>> other-config:hwaddr=\"00:00:00:02:02:00\" > >>> +ovn_attach n2 br-phys 192.168.1.12 > >>> + > >>> +OVN_POPULATE_ARP > >>> + > >>> +as hv1 > >>> + > >>> +# test_ipv4_icmp_request INPORT ETH_SRC ETH_DST IPV4_SRC IPV4_DST > >>> IP_CHKSUM ICMP_CHKSUM [EXP_IP_CHKSUM EXP_ICMP_CHKSUM] ENCAP_TYPE FILTER > >>> +# > >>> +# Causes a packet to be received on INPORT. The packet is an ICMPv4 > >>> +# request with ETH_SRC, ETH_DST, IPV4_SRC, IPV4_DST, IP_CHSUM and > >>> +# ICMP_CHKSUM as specified. If EXP_IP_CHKSUM and EXP_ICMP_CHKSUM are > >>> +# provided, then it should be the ip and icmp checksums of the packet > >>> +# responded; otherwise, no reply is expected. > >>> +# In the absence of an ip checksum calculation helpers, this relies > >>> +# on the caller to provide the checksums for the ip and icmp headers. > >>> +# XXX This should be more systematic. > >>> +# > >>> +# INPORT is an lport number, e.g. 11 for vif11. > >>> +# ETH_SRC and ETH_DST are each 12 hex digits. > >>> +# IPV4_SRC and IPV4_DST are each 8 hex digits. > >>> +# IP_CHSUM and ICMP_CHKSUM are each 4 hex digits. > >>> +# EXP_IP_CHSUM and EXP_ICMP_CHKSUM are each 4 hex digits. > >>> +# ENCAP_TYPE - gre or erspan > >>> +# FILTER - Mirror Filter - to-lport / from-lport > >>> +test_ipv4_icmp_request() { > >>> + local inport=$1 eth_src=$2 eth_dst=$3 ipv4_src=$4 ipv4_dst=$5 > >>> ip_chksum=$6 icmp_chksum=$7 > >>> + local exp_ip_chksum=$8 exp_icmp_chksum=$9 > >>> mirror_encap_type=${10} mirror_filter=${11} > >>> + shift; shift; shift; shift; shift; shift; shift > >>> + shift; shift; shift; shift; > >>> + > >>> + # Use ttl to exercise section 4.2.2.9 of RFC1812 > >>> + local ip_ttl=02 > >>> + local icmp_id=5fbf > >>> + local icmp_seq=0001 > >>> + local icmp_data=$(seq 1 56 | xargs printf "%02x") > >>> + local icmp_type_code_request=0800 > >>> + local > >>> > icmp_payload=${icmp_type_code_request}${icmp_chksum}${icmp_id}${icmp_seq}${icmp_data} > >>> + local > >>> > packet=${eth_dst}${eth_src}08004500005400004000${ip_ttl}01${ip_chksum}${ipv4_src}${ipv4_dst}${icmp_payload} > >>> + > >>> + as hv1 ovs-appctl netdev-dummy/receive vif$inport $packet > >>> + > >>> + # Expect to receive the reply, if any. In same port where > >>> packet was sent. > >>> + # Note: src and dst fields are expected to be reversed. > >>> + local icmp_type_code_response=0000 > >>> + local reply_icmp_ttl=fe > >>> + local > >>> > reply_icmp_payload=${icmp_type_code_response}${exp_icmp_chksum}${icmp_id}${icmp_seq}${icmp_data} > >>> + local > >>> > reply=${eth_src}${eth_dst}08004500005400004000${reply_icmp_ttl}01${exp_ip_chksum}${ipv4_dst}${ipv4_src}${reply_icmp_payload} > >>> + echo $reply >> vif$inport.expected > >>> + local remote_mac=000000020200 > >>> + local local_mac=000000010200 > >>> + if test ${mirror_encap_type} = "gre" ; then > >>> + local > >>> ipv4_gre=4500007e00004000402fb6e9c0a8010bc0a8010c2000655800000000 > >>> + if test ${mirror_filter} = "to-lport" ; then > >>> + local > >>> mirror=${remote_mac}${local_mac}0800${ipv4_gre}${reply} > >>> + elif test ${mirror_filter} = "from-lport" ; then > >>> + local > >>> mirror=${remote_mac}${local_mac}0800${ipv4_gre}${packet} > >>> + fi > >>> + elif test ${mirror_encap_type} = "erspan" ; then > >>> + local ipv4_erspan=4500008600004000402fb6e1c0a8010bc0a8010c > >>> + local erspan_seq0=100088be000000001000000000000000 > >>> + local erspan_seq1=100088be000000011000000000000000 > >>> + if test ${mirror_filter} = "to-lport" ; then > >>> + local > >>> > mirror=${remote_mac}${local_mac}0800${ipv4_erspan}${erspan_seq0}${reply} > >>> > >>> + elif test ${mirror_filter} = "from-lport" ; then > >>> + local > >>> > mirror=${remote_mac}${local_mac}0800${ipv4_erspan}${erspan_seq1}${packet} > >>> + fi > >>> + fi > >>> + echo $mirror >> br-phys_n1.expected > >>> + > >>> +} > >>> + > >>> +# Set IPs > >>> +rtr_l2_ip=$(ip_to_hex 172 16 1 1) > >>> +l1_ip=$(ip_to_hex 192 168 1 2) > >>> + > >>> +check ovn-nbctl mirror-add mirror0 gre 0 to-lport 192.168.1.12 > >>> +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror0 > >>> + > >>> +# Send ping packet and check for mirrored packet of the reply > >>> +test_ipv4_icmp_request 1 000000010203 0000000102f1 $l1_ip > >>> $rtr_l2_ip 0000 8510 03ff 8d10 "gre" "to-lport" > >>> + > >>> +OVN_CHECK_PACKETS_CONTAIN([hv1/br-phys_n1-tx.pcap], > >>> [br-phys_n1.expected]) > >>> +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected]) > >>> + > >>> +as hv1 reset_pcap_file vif1 hv1/vif1 > >>> +as hv1 reset_pcap_file br-phys_n1 hv1/br-phys_n1 > >>> +rm -f br-phys_n1.expected > >>> +rm -f vif1.expected > >>> + > >>> +check ovn-nbctl set mirror . type=erspan > >>> + > >>> +# Send ping packet and check for mirrored packet of the reply > >>> +test_ipv4_icmp_request 1 000000010203 0000000102f1 $l1_ip > >>> $rtr_l2_ip 0000 8510 03ff 8d10 "erspan" "to-lport" > >>> + > >>> +OVN_CHECK_PACKETS_CONTAIN([hv1/br-phys_n1-tx.pcap], > >>> [br-phys_n1.expected]) > >>> +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected]) > >>> + > >>> +as hv1 reset_pcap_file vif1 hv1/vif1 > >>> +as hv1 reset_pcap_file br-phys_n1 hv1/br-phys_n1 > >>> +rm -f br-phys_n1.expected > >>> +rm -f vif1.expected > >>> + > >>> +check ovn-nbctl set mirror . filter=from-lport > >>> + > >>> +# Send ping packet and check for mirrored packet of the request > >>> +test_ipv4_icmp_request 1 000000010203 0000000102f1 $l1_ip > >>> $rtr_l2_ip 0000 8510 03ff 8d10 "erspan" "from-lport" > >>> + > >>> +OVN_CHECK_PACKETS_CONTAIN([hv1/br-phys_n1-tx.pcap], > >>> [br-phys_n1.expected]) > >>> +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected]) > >>> + > >>> +as hv1 reset_pcap_file vif1 hv1/vif1 > >>> +as hv1 reset_pcap_file br-phys_n1 hv1/br-phys_n1 > >>> +rm -f br-phys_n1.expected > >>> +rm -f vif1.expected > >>> + > >>> +check ovn-nbctl set mirror . type=gre > >>> + > >>> +# Send ping packet and check for mirrored packet of the request > >>> +test_ipv4_icmp_request 1 000000010203 0000000102f1 $l1_ip > >>> $rtr_l2_ip 0000 8510 03ff 8d10 "gre" "from-lport" > >>> + > >>> +OVN_CHECK_PACKETS_CONTAIN([hv1/br-phys_n1-tx.pcap], > >>> [br-phys_n1.expected]) > >>> +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected]) > >>> + > >>> +echo "---------OVN NB Mirror-----" > >>> +ovn-nbctl mirror-list > >>> + > >>> +echo "---------OVS Mirror----" > >>> +ovs-vsctl list Mirror > >>> + > >>> +echo "-----------------------" > >>> + > >>> +echo "Verifying Mirror deletion in OVS" > >>> +# Set vif1 iface-id such that OVN releases port binding > >>> +check ovs-vsctl set interface vif1 external_ids:iface-id=foo > >>> +check ovn-nbctl --wait=hv sync > >>> + > >>> +AT_CHECK([as hv1 ovs-vsctl list Mirror], [0], [dnl > >>> +]) > >>> + > >>> +# Set vif1 iface-id back to ls1-lp1 > >>> +check ovs-vsctl set interface vif1 external_ids:iface-id=ls1-lp1 > >>> +check ovn-nbctl --wait=hv sync > >>> + > >>> +OVS_WAIT_UNTIL([test $(as hv1 ovs-vsctl get Mirror mirror0 name) = > >>> "mirror0"]) > >>> + > >>> +# Delete vif1 so that OVN releases port binding > >>> +check ovs-vsctl del-port br-int vif1 > >>> +check ovn-nbctl --wait=hv sync > >>> + > >>> +OVS_WAIT_UNTIL([test 0 = $(as hv1 ovs-vsctl list Mirror | wc -l)]) > >>> + > >>> +OVN_CLEANUP([hv1], [hv2]) > >>> +AT_CLEANUP > >>> +]) > >>> + > >>> +OVN_FOR_EACH_NORTHD([ > >>> +AT_SETUP([Mirror test bulk swap attachments]) > >>> +AT_KEYWORDS([Mirror test bulk swap attachments]) > >>> +ovn_start > >>> + > >>> +# Logical network: > >>> +# One LR - R1 has switch ls1 (191.168.1.0/24) connected to it, > >>> +# and has switch ls2 (172.16.1.0/24) connected to it. > >>> + > >>> +ovn-nbctl lr-add R1 > >>> + > >>> +ovn-nbctl ls-add ls1 > >>> +ovn-nbctl ls-add ls2 > >>> + > >>> +# Connect ls1 to R1 > >>> +ovn-nbctl lrp-add R1 ls1 00:00:00:01:02:f1 192.168.1.1/24 > >>> +ovn-nbctl lsp-add ls1 rp-ls1 -- set Logical_Switch_Port rp-ls1 \ > >>> + type=router options:router-port=ls1 > >>> addresses=\"00:00:00:01:02:f1\" > >>> + > >>> +# Connect ls2 to R1 > >>> +ovn-nbctl lrp-add R1 ls2 00:00:00:01:02:f2 172.16.1.1/24 > >>> +ovn-nbctl lsp-add ls2 rp-ls2 -- set Logical_Switch_Port rp-ls2 \ > >>> + type=router options:router-port=ls2 > >>> addresses=\"00:00:00:01:02:f2\" > >>> + > >>> +# Create logical port ls1-lp1 in ls1 > >>> +ovn-nbctl lsp-add ls1 ls1-lp1 \ > >>> +-- lsp-set-addresses ls1-lp1 "00:00:00:01:02:03 192.168.1.2" > >>> + > >>> +# Create logical port ls1-lp2 in ls1 > >>> +ovn-nbctl lsp-add ls1 ls1-lp2 \ > >>> +-- lsp-set-addresses ls1-lp2 "00:00:00:01:03:03 192.168.1.3" > >>> + > >>> +# Create logical port ls2-lp1 in ls2 > >>> +ovn-nbctl lsp-add ls2 ls2-lp1 \ > >>> +-- lsp-set-addresses ls2-lp1 "00:00:00:01:02:04 172.16.1.2" > >>> + > >>> +# Create logical port ls2-lp2 in ls2 > >>> +ovn-nbctl lsp-add ls2 ls2-lp2 \ > >>> +-- lsp-set-addresses ls2-lp2 "00:00:00:01:03:04 172.16.1.3" > >>> + > >>> +ovn-nbctl lsp-add ls1 ln-public > >>> +ovn-nbctl lsp-set-type ln-public localnet > >>> +ovn-nbctl lsp-set-addresses ln-public unknown > >>> +ovn-nbctl lsp-set-options ln-public network_name=public > >>> + > >>> +# Create one hypervisor and create OVS ports corresponding to > >>> logical ports. > >>> +net_add n1 > >>> + > >>> +sim_add hv1 > >>> +as hv1 > >>> +ovs-vsctl add-br br-phys -- set bridge br-phys > >>> other-config:hwaddr=\"00:00:00:01:02:00\" > >>> +ovn_attach n1 br-phys 192.168.1.11 > >>> + > >>> +ovs-vsctl -- add-port br-int vif1 -- \ > >>> + set interface vif1 external-ids:iface-id=ls1-lp1 > >>> + > >>> +ovs-vsctl -- add-port br-int vif2 -- \ > >>> + set interface vif2 external-ids:iface-id=ls2-lp1 > >>> + > >>> +ovs-vsctl -- add-port br-int vif3 -- \ > >>> + set interface vif3 external-ids:iface-id=ls1-lp2 > >>> + > >>> +ovs-vsctl -- add-port br-int vif4 -- \ > >>> + set interface vif4 external-ids:iface-id=ls2-lp2 > >>> + > >>> +ovs-vsctl set open . external-ids:ovn-bridge-mappings=public:br-phys > >>> + > >>> +# Allow some time for ovn-northd and ovn-controller to catch up. > >>> +wait_for_ports_up > >>> +check ovn-nbctl --wait=hv sync > >>> + > >>> +check ovn-nbctl mirror-add mirror0 gre 0 to-lport 192.168.1.12 > >>> +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror0 > >>> +check ovn-nbctl lsp-attach-mirror ls2-lp1 mirror0 > >>> + > >>> +check ovn-nbctl mirror-add mirror1 gre 1 to-lport 192.168.1.12 > >>> +check ovn-nbctl lsp-attach-mirror ls1-lp2 mirror1 > >>> +check ovn-nbctl lsp-attach-mirror ls2-lp2 mirror1 > >>> + > >>> +check ovn-nbctl --wait=hv sync > >>> + > >>> +as hv1 > >>> +orig1=$(ovs-vsctl get Mirror mirror0 select_dst_port) > >>> +orig2=$(ovs-vsctl get Mirror mirror1 select_dst_port) > >>> + > >>> +# Equal detaches and attaches > >>> +check as hv1 ovn-appctl -t ovn-controller debug/pause > >>> +check ovn-nbctl lsp-detach-mirror ls1-lp1 mirror0 > >>> +check ovn-nbctl lsp-detach-mirror ls2-lp1 mirror0 > >>> +check ovn-nbctl lsp-attach-mirror ls1-lp2 mirror0 > >>> +check ovn-nbctl lsp-attach-mirror ls2-lp2 mirror0 > >>> + > >>> +check ovn-nbctl lsp-detach-mirror ls1-lp2 mirror1 > >>> +check ovn-nbctl lsp-detach-mirror ls2-lp2 mirror1 > >>> +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror1 > >>> +check ovn-nbctl lsp-attach-mirror ls2-lp1 mirror1 > >>> +check as hv1 ovn-appctl -t ovn-controller debug/resume > >>> + > >>> +check ovn-nbctl --wait=hv sync > >>> + > >>> +as hv1 > >>> +new1=$(ovs-vsctl get Mirror mirror0 select_dst_port) > >>> +new2=$(ovs-vsctl get Mirror mirror1 select_dst_port) > >>> + > >>> +AT_CHECK([test "$orig1" = "$new2"], [0], []) > >>> + > >>> +AT_CHECK([test "$orig2" = "$new1"], [0], []) > >>> + > >>> +OVN_CLEANUP([hv1]) > >>> +AT_CLEANUP > >>> +]) > >>> + > >>> +OVN_FOR_EACH_NORTHD([ > >>> +AT_SETUP([Mirror test bulk attach multiple]) > >>> +AT_KEYWORDS([Mirror test bulk attach multiple]) > >>> +ovn_start > >>> + > >>> +# Logical network: > >>> +# One LR - R1 has switch ls1 (191.168.1.0/24) connected to it, > >>> +# and has switch ls2 (172.16.1.0/24) connected to it. > >>> + > >>> +ovn-nbctl lr-add R1 > >>> + > >>> +ovn-nbctl ls-add ls1 > >>> +ovn-nbctl ls-add ls2 > >>> + > >>> +# Connect ls1 to R1 > >>> +ovn-nbctl lrp-add R1 ls1 00:00:00:01:02:f1 192.168.1.1/24 > >>> +ovn-nbctl lsp-add ls1 rp-ls1 -- set Logical_Switch_Port rp-ls1 \ > >>> + type=router options:router-port=ls1 > >>> addresses=\"00:00:00:01:02:f1\" > >>> + > >>> +# Connect ls2 to R1 > >>> +ovn-nbctl lrp-add R1 ls2 00:00:00:01:02:f2 172.16.1.1/24 > >>> +ovn-nbctl lsp-add ls2 rp-ls2 -- set Logical_Switch_Port rp-ls2 \ > >>> + type=router options:router-port=ls2 > >>> addresses=\"00:00:00:01:02:f2\" > >>> + > >>> +# Create logical port ls1-lp1 in ls1 > >>> +ovn-nbctl lsp-add ls1 ls1-lp1 \ > >>> +-- lsp-set-addresses ls1-lp1 "00:00:00:01:02:03 192.168.1.2" > >>> + > >>> +# Create logical port ls1-lp2 in ls1 > >>> +ovn-nbctl lsp-add ls1 ls1-lp2 \ > >>> +-- lsp-set-addresses ls1-lp2 "00:00:00:01:03:03 192.168.1.3" > >>> + > >>> +# Create logical port ls2-lp1 in ls2 > >>> +ovn-nbctl lsp-add ls2 ls2-lp1 \ > >>> +-- lsp-set-addresses ls2-lp1 "00:00:00:01:02:04 172.16.1.2" > >>> + > >>> +# Create logical port ls2-lp2 in ls2 > >>> +ovn-nbctl lsp-add ls2 ls2-lp2 \ > >>> +-- lsp-set-addresses ls2-lp2 "00:00:00:01:03:04 172.16.1.3" > >>> + > >>> +ovn-nbctl lsp-add ls1 ln-public > >>> +ovn-nbctl lsp-set-type ln-public localnet > >>> +ovn-nbctl lsp-set-addresses ln-public unknown > >>> +ovn-nbctl lsp-set-options ln-public network_name=public > >>> + > >>> +# Create one hypervisor and create OVS ports corresponding to > >>> logical ports. > >>> +net_add n1 > >>> + > >>> +sim_add hv1 > >>> +as hv1 > >>> +ovs-vsctl add-br br-phys -- set bridge br-phys > >>> other-config:hwaddr=\"00:00:00:01:02:00\" > >>> +ovn_attach n1 br-phys 192.168.1.11 > >>> + > >>> +ovs-vsctl -- add-port br-int vif1 -- \ > >>> + set interface vif1 external-ids:iface-id=ls1-lp1 > >>> + > >>> +ovs-vsctl -- add-port br-int vif2 -- \ > >>> + set interface vif2 external-ids:iface-id=ls2-lp1 > >>> + > >>> +ovs-vsctl -- add-port br-int vif3 -- \ > >>> + set interface vif3 external-ids:iface-id=ls1-lp2 > >>> + > >>> +ovs-vsctl -- add-port br-int vif4 -- \ > >>> + set interface vif4 external-ids:iface-id=ls2-lp2 > >>> + > >>> +ovs-vsctl set open . external-ids:ovn-bridge-mappings=public:br-phys > >>> + > >>> +# Allow some time for ovn-northd and ovn-controller to catch up. > >>> +wait_for_ports_up > >>> +check ovn-nbctl --wait=hv sync > >>> + > >>> +check ovn-nbctl mirror-add mirror0 gre 0 to-lport 192.168.1.12 > >>> +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror0 > >>> +check ovn-nbctl lsp-attach-mirror ls2-lp1 mirror0 > >>> + > >>> +check ovn-nbctl mirror-add mirror1 gre 1 to-lport 192.168.1.12 > >>> +check ovn-nbctl lsp-attach-mirror ls1-lp2 mirror1 > >>> +check ovn-nbctl lsp-attach-mirror ls2-lp2 mirror1 > >>> + > >>> +check ovn-nbctl --wait=hv sync > >>> + > >>> +as hv1 > >>> +orig1=$(ovs-vsctl get Mirror mirror0 select_dst_port) > >>> +orig2=$(ovs-vsctl get Mirror mirror1 select_dst_port) > >>> + > >>> +check ovn-nbctl mirror-del > >>> +check ovn-nbctl --wait=hv sync > >>> + > >>> +check ovn-nbctl mirror-add mirror0 gre 0 to-lport 192.168.1.12 > >>> +check ovn-nbctl mirror-add mirror1 gre 1 to-lport 192.168.1.12 > >>> + > >>> +# Attaches multiple mirrors > >>> +check as hv1 ovn-appctl -t ovn-controller debug/pause > >>> +check ovn-nbctl lsp-attach-mirror ls2-lp1 mirror0 > >>> +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror0 > >>> +check ovn-nbctl lsp-attach-mirror ls2-lp2 mirror1 > >>> +check ovn-nbctl lsp-attach-mirror ls1-lp2 mirror1 > >>> +check as hv1 ovn-appctl -t ovn-controller debug/resume > >>> + > >>> +check ovn-nbctl --wait=hv sync > >>> + > >>> +as hv1 > >>> +new1=$(ovs-vsctl get Mirror mirror0 select_dst_port) > >>> +new2=$(ovs-vsctl get Mirror mirror1 select_dst_port) > >>> + > >>> +AT_CHECK([test "$orig1" = "$new1"], [0], []) > >>> + > >>> +AT_CHECK([test "$orig2" = "$new2"], [0], []) > >>> + > >>> +OVN_CLEANUP([hv1]) > >>> +AT_CLEANUP > >>> +]) > >>> + > >>> +OVN_FOR_EACH_NORTHD([ > >>> +AT_SETUP([Mirror test bulk more detach and less attach]) > >>> +AT_KEYWORDS([Mirror test bulk more detach and less attach]) > >>> +ovn_start > >>> + > >>> +# Logical network: > >>> +# One LR - R1 has switch ls1 (191.168.1.0/24) connected to it, > >>> +# and has switch ls2 (172.16.1.0/24) connected to it. > >>> + > >>> +ovn-nbctl lr-add R1 > >>> + > >>> +ovn-nbctl ls-add ls1 > >>> +ovn-nbctl ls-add ls2 > >>> + > >>> +# Connect ls1 to R1 > >>> +ovn-nbctl lrp-add R1 ls1 00:00:00:01:02:f1 192.168.1.1/24 > >>> +ovn-nbctl lsp-add ls1 rp-ls1 -- set Logical_Switch_Port rp-ls1 \ > >>> + type=router options:router-port=ls1 > >>> addresses=\"00:00:00:01:02:f1\" > >>> + > >>> +# Connect ls2 to R1 > >>> +ovn-nbctl lrp-add R1 ls2 00:00:00:01:02:f2 172.16.1.1/24 > >>> +ovn-nbctl lsp-add ls2 rp-ls2 -- set Logical_Switch_Port rp-ls2 \ > >>> + type=router options:router-port=ls2 > >>> addresses=\"00:00:00:01:02:f2\" > >>> + > >>> +# Create logical port ls1-lp1 in ls1 > >>> +ovn-nbctl lsp-add ls1 ls1-lp1 \ > >>> +-- lsp-set-addresses ls1-lp1 "00:00:00:01:02:03 192.168.1.2" > >>> + > >>> +# Create logical port ls1-lp2 in ls1 > >>> +ovn-nbctl lsp-add ls1 ls1-lp2 \ > >>> +-- lsp-set-addresses ls1-lp2 "00:00:00:01:03:03 192.168.1.3" > >>> + > >>> +# Create logical port ls2-lp1 in ls2 > >>> +ovn-nbctl lsp-add ls2 ls2-lp1 \ > >>> +-- lsp-set-addresses ls2-lp1 "00:00:00:01:02:04 172.16.1.2" > >>> + > >>> +# Create logical port ls2-lp2 in ls2 > >>> +ovn-nbctl lsp-add ls2 ls2-lp2 \ > >>> +-- lsp-set-addresses ls2-lp2 "00:00:00:01:03:04 172.16.1.3" > >>> + > >>> +ovn-nbctl lsp-add ls1 ln-public > >>> +ovn-nbctl lsp-set-type ln-public localnet > >>> +ovn-nbctl lsp-set-addresses ln-public unknown > >>> +ovn-nbctl lsp-set-options ln-public network_name=public > >>> + > >>> +# Create one hypervisor and create OVS ports corresponding to > >>> logical ports. > >>> +net_add n1 > >>> + > >>> +sim_add hv1 > >>> +as hv1 > >>> +ovs-vsctl add-br br-phys -- set bridge br-phys > >>> other-config:hwaddr=\"00:00:00:01:02:00\" > >>> +ovn_attach n1 br-phys 192.168.1.11 > >>> + > >>> +ovs-vsctl -- add-port br-int vif1 -- \ > >>> + set interface vif1 external-ids:iface-id=ls1-lp1 > >>> + > >>> +ovs-vsctl -- add-port br-int vif2 -- \ > >>> + set interface vif2 external-ids:iface-id=ls2-lp1 > >>> + > >>> +ovs-vsctl -- add-port br-int vif3 -- \ > >>> + set interface vif3 external-ids:iface-id=ls1-lp2 > >>> + > >>> +ovs-vsctl -- add-port br-int vif4 -- \ > >>> + set interface vif4 external-ids:iface-id=ls2-lp2 > >>> + > >>> +ovs-vsctl set open . external-ids:ovn-bridge-mappings=public:br-phys > >>> + > >>> +# Allow some time for ovn-northd and ovn-controller to catch up. > >>> +wait_for_ports_up > >>> +check ovn-nbctl --wait=hv sync > >>> + > >>> +check ovn-nbctl mirror-add mirror0 gre 0 to-lport 192.168.1.12 > >>> +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror0 > >>> +check ovn-nbctl --wait=hv sync > >>> +origA=$(ovs-vsctl get Mirror mirror0 select_dst_port) > >>> + > >>> +check ovn-nbctl mirror-add mirror1 gre 1 to-lport 192.168.1.12 > >>> +check ovn-nbctl lsp-attach-mirror ls1-lp2 mirror1 > >>> +check ovn-nbctl --wait=hv sync > >>> +origB=$(ovs-vsctl get Mirror mirror1 select_dst_port) > >>> + > >>> +check ovn-nbctl mirror-del > >>> +check ovn-nbctl --wait=hv sync > >>> + > >>> +check ovn-nbctl mirror-add mirror0 gre 0 to-lport 192.168.1.12 > >>> +check ovn-nbctl mirror-add mirror1 gre 1 to-lport 192.168.1.12 > >>> + > >>> +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror1 > >>> +check ovn-nbctl lsp-attach-mirror ls2-lp2 mirror0 > >>> +check ovn-nbctl lsp-attach-mirror ls1-lp2 mirror0 > >>> + > >>> +check ovn-nbctl --wait=hv sync > >>> + > >>> +# Detaches more than attaches > >>> +check as hv1 ovn-appctl -t ovn-controller debug/pause > >>> +check ovn-nbctl lsp-detach-mirror ls1-lp1 mirror1 > >>> +check ovn-nbctl lsp-detach-mirror ls1-lp2 mirror0 > >>> +check ovn-nbctl lsp-detach-mirror ls2-lp2 mirror0 > >>> +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror0 > >>> +check ovn-nbctl lsp-attach-mirror ls1-lp2 mirror1 > >>> +check as hv1 ovn-appctl -t ovn-controller debug/resume > >>> + > >>> +check ovn-nbctl --wait=hv sync > >>> + > >>> +as hv1 > >>> +new1=$(ovs-vsctl get Mirror mirror0 select_dst_port) > >>> +new2=$(ovs-vsctl get Mirror mirror1 select_dst_port) > >>> + > >>> +AT_CHECK([test "$origA" = "$new1"], [0], []) > >>> + > >>> +AT_CHECK([test "$origB" = "$new2"], [0], []) > >>> + > >>> +OVN_CLEANUP([hv1]) > >>> +AT_CLEANUP > >>> +]) > >>> + > >>> +OVN_FOR_EACH_NORTHD([ > >>> +AT_SETUP([Mirror test bulk attach more than detach]) > >>> +AT_KEYWORDS([Mirror test bulk attach more than detach]) > >>> +ovn_start > >>> + > >>> +# Logical network: > >>> +# One LR - R1 has switch ls1 (191.168.1.0/24) connected to it, > >>> +# and has switch ls2 (172.16.1.0/24) connected to it. > >>> + > >>> +ovn-nbctl lr-add R1 > >>> + > >>> +ovn-nbctl ls-add ls1 > >>> +ovn-nbctl ls-add ls2 > >>> + > >>> +# Connect ls1 to R1 > >>> +ovn-nbctl lrp-add R1 ls1 00:00:00:01:02:f1 192.168.1.1/24 > >>> +ovn-nbctl lsp-add ls1 rp-ls1 -- set Logical_Switch_Port rp-ls1 \ > >>> + type=router options:router-port=ls1 > >>> addresses=\"00:00:00:01:02:f1\" > >>> + > >>> +# Connect ls2 to R1 > >>> +ovn-nbctl lrp-add R1 ls2 00:00:00:01:02:f2 172.16.1.1/24 > >>> +ovn-nbctl lsp-add ls2 rp-ls2 -- set Logical_Switch_Port rp-ls2 \ > >>> + type=router options:router-port=ls2 > >>> addresses=\"00:00:00:01:02:f2\" > >>> + > >>> +# Create logical port ls1-lp1 in ls1 > >>> +ovn-nbctl lsp-add ls1 ls1-lp1 \ > >>> +-- lsp-set-addresses ls1-lp1 "00:00:00:01:02:03 192.168.1.2" > >>> + > >>> +# Create logical port ls1-lp2 in ls1 > >>> +ovn-nbctl lsp-add ls1 ls1-lp2 \ > >>> +-- lsp-set-addresses ls1-lp2 "00:00:00:01:03:03 192.168.1.3" > >>> + > >>> +# Create logical port ls2-lp1 in ls2 > >>> +ovn-nbctl lsp-add ls2 ls2-lp1 \ > >>> +-- lsp-set-addresses ls2-lp1 "00:00:00:01:02:04 172.16.1.2" > >>> + > >>> +# Create logical port ls2-lp2 in ls2 > >>> +ovn-nbctl lsp-add ls2 ls2-lp2 \ > >>> +-- lsp-set-addresses ls2-lp2 "00:00:00:01:03:04 172.16.1.3" > >>> + > >>> +ovn-nbctl lsp-add ls1 ln-public > >>> +ovn-nbctl lsp-set-type ln-public localnet > >>> +ovn-nbctl lsp-set-addresses ln-public unknown > >>> +ovn-nbctl lsp-set-options ln-public network_name=public > >>> + > >>> +# Create one hypervisor and create OVS ports corresponding to > >>> logical ports. > >>> +net_add n1 > >>> + > >>> +sim_add hv1 > >>> +as hv1 > >>> +ovs-vsctl add-br br-phys -- set bridge br-phys > >>> other-config:hwaddr=\"00:00:00:01:02:00\" > >>> +ovn_attach n1 br-phys 192.168.1.11 > >>> + > >>> +ovs-vsctl -- add-port br-int vif1 -- \ > >>> + set interface vif1 external-ids:iface-id=ls1-lp1 > >>> + > >>> +ovs-vsctl -- add-port br-int vif2 -- \ > >>> + set interface vif2 external-ids:iface-id=ls2-lp1 > >>> + > >>> +ovs-vsctl -- add-port br-int vif3 -- \ > >>> + set interface vif3 external-ids:iface-id=ls1-lp2 > >>> + > >>> +ovs-vsctl -- add-port br-int vif4 -- \ > >>> + set interface vif4 external-ids:iface-id=ls2-lp2 > >>> + > >>> +ovs-vsctl set open . external-ids:ovn-bridge-mappings=public:br-phys > >>> + > >>> +# Allow some time for ovn-northd and ovn-controller to catch up. > >>> +wait_for_ports_up > >>> +check ovn-nbctl --wait=hv sync > >>> + > >>> +check ovn-nbctl mirror-add mirror0 gre 0 to-lport 192.168.1.12 > >>> +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror0 > >>> +check ovn-nbctl lsp-attach-mirror ls2-lp1 mirror0 > >>> + > >>> +check ovn-nbctl mirror-add mirror1 gre 1 to-lport 192.168.1.12 > >>> +check ovn-nbctl lsp-attach-mirror ls1-lp2 mirror1 > >>> +check ovn-nbctl lsp-attach-mirror ls2-lp2 mirror1 > >>> + > >>> +check ovn-nbctl --wait=hv sync > >>> + > >>> +as hv1 > >>> +orig1=$(ovs-vsctl get Mirror mirror0 select_dst_port) > >>> +orig2=$(ovs-vsctl get Mirror mirror1 select_dst_port) > >>> + > >>> +check ovn-nbctl lsp-detach-mirror ls2-lp1 mirror0 > >>> +check ovn-nbctl lsp-detach-mirror ls2-lp2 mirror1 > >>> +check ovn-nbctl --wait=hv sync > >>> + > >>> +# Attaches more than detaches > >>> +check as hv1 ovn-appctl -t ovn-controller debug/pause > >>> +check ovn-nbctl lsp-detach-mirror ls1-lp1 mirror0 > >>> +check ovn-nbctl lsp-detach-mirror ls1-lp2 mirror1 > >>> +check ovn-nbctl lsp-attach-mirror ls1-lp2 mirror0 > >>> +check ovn-nbctl lsp-attach-mirror ls2-lp2 mirror0 > >>> +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror1 > >>> +check ovn-nbctl lsp-attach-mirror ls2-lp1 mirror1 > >>> +check as hv1 ovn-appctl -t ovn-controller debug/resume > >>> + > >>> +check ovn-nbctl --wait=hv sync > >>> + > >>> +as hv1 > >>> +new1=$(ovs-vsctl get Mirror mirror0 select_dst_port) > >>> +new2=$(ovs-vsctl get Mirror mirror1 select_dst_port) > >>> + > >>> +AT_CHECK([test "$orig1" = "$new2"], [0], []) > >>> + > >>> +AT_CHECK([test "$orig2" = "$new1"], [0], []) > >>> + > >>> +OVN_CLEANUP([hv1]) > >>> +AT_CLEANUP > >>> +]) > >>> + > >>> +OVN_FOR_EACH_NORTHD([ > >>> +AT_SETUP([Mirror test bulk detach multiple]) > >>> +AT_KEYWORDS([Mirror test bulk detach multiple]) > >>> +ovn_start > >>> + > >>> +# Logical network: > >>> +# One LR - R1 has switch ls1 (191.168.1.0/24) connected to it, > >>> +# and has switch ls2 (172.16.1.0/24) connected to it. > >>> + > >>> +ovn-nbctl lr-add R1 > >>> + > >>> +ovn-nbctl ls-add ls1 > >>> +ovn-nbctl ls-add ls2 > >>> + > >>> +# Connect ls1 to R1 > >>> +ovn-nbctl lrp-add R1 ls1 00:00:00:01:02:f1 192.168.1.1/24 > >>> +ovn-nbctl lsp-add ls1 rp-ls1 -- set Logical_Switch_Port rp-ls1 \ > >>> + type=router options:router-port=ls1 > >>> addresses=\"00:00:00:01:02:f1\" > >>> + > >>> +# Connect ls2 to R1 > >>> +ovn-nbctl lrp-add R1 ls2 00:00:00:01:02:f2 172.16.1.1/24 > >>> +ovn-nbctl lsp-add ls2 rp-ls2 -- set Logical_Switch_Port rp-ls2 \ > >>> + type=router options:router-port=ls2 > >>> addresses=\"00:00:00:01:02:f2\" > >>> + > >>> +# Create logical port ls1-lp1 in ls1 > >>> +ovn-nbctl lsp-add ls1 ls1-lp1 \ > >>> +-- lsp-set-addresses ls1-lp1 "00:00:00:01:02:03 192.168.1.2" > >>> + > >>> +# Create logical port ls1-lp2 in ls1 > >>> +ovn-nbctl lsp-add ls1 ls1-lp2 \ > >>> +-- lsp-set-addresses ls1-lp2 "00:00:00:01:03:03 192.168.1.3" > >>> + > >>> +# Create logical port ls2-lp1 in ls2 > >>> +ovn-nbctl lsp-add ls2 ls2-lp1 \ > >>> +-- lsp-set-addresses ls2-lp1 "00:00:00:01:02:04 172.16.1.2" > >>> + > >>> +# Create logical port ls2-lp2 in ls2 > >>> +ovn-nbctl lsp-add ls2 ls2-lp2 \ > >>> +-- lsp-set-addresses ls2-lp2 "00:00:00:01:03:04 172.16.1.3" > >>> + > >>> +ovn-nbctl lsp-add ls1 ln-public > >>> +ovn-nbctl lsp-set-type ln-public localnet > >>> +ovn-nbctl lsp-set-addresses ln-public unknown > >>> +ovn-nbctl lsp-set-options ln-public network_name=public > >>> + > >>> +# Create one hypervisor and create OVS ports corresponding to > >>> logical ports. > >>> +net_add n1 > >>> + > >>> +sim_add hv1 > >>> +as hv1 > >>> +ovs-vsctl add-br br-phys -- set bridge br-phys > >>> other-config:hwaddr=\"00:00:00:01:02:00\" > >>> +ovn_attach n1 br-phys 192.168.1.11 > >>> + > >>> +ovs-vsctl -- add-port br-int vif1 -- \ > >>> + set interface vif1 external-ids:iface-id=ls1-lp1 > >>> + > >>> +ovs-vsctl -- add-port br-int vif2 -- \ > >>> + set interface vif2 external-ids:iface-id=ls2-lp1 > >>> + > >>> +ovs-vsctl -- add-port br-int vif3 -- \ > >>> + set interface vif3 external-ids:iface-id=ls1-lp2 > >>> + > >>> +ovs-vsctl -- add-port br-int vif4 -- \ > >>> + set interface vif4 external-ids:iface-id=ls2-lp2 > >>> + > >>> +ovs-vsctl set open . external-ids:ovn-bridge-mappings=public:br-phys > >>> + > >>> +# Allow some time for ovn-northd and ovn-controller to catch up. > >>> +wait_for_ports_up > >>> +check ovn-nbctl --wait=hv sync > >>> + > >>> +check ovn-nbctl mirror-add mirror0 gre 0 to-lport 192.168.1.12 > >>> +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror0 > >>> +check ovn-nbctl lsp-attach-mirror ls2-lp1 mirror0 > >>> + > >>> +check ovn-nbctl mirror-add mirror1 gre 1 to-lport 192.168.1.12 > >>> +check ovn-nbctl lsp-attach-mirror ls1-lp2 mirror1 > >>> +check ovn-nbctl lsp-attach-mirror ls2-lp2 mirror1 > >>> + > >>> +check ovn-nbctl --wait=hv sync > >>> + > >>> +# Detaches all > >>> +check as hv1 ovn-appctl -t ovn-controller debug/pause > >>> +check ovn-nbctl lsp-detach-mirror ls1-lp1 mirror0 > >>> +check ovn-nbctl lsp-detach-mirror ls2-lp1 mirror0 > >>> +check ovn-nbctl lsp-detach-mirror ls1-lp2 mirror1 > >>> +check ovn-nbctl lsp-detach-mirror ls2-lp2 mirror1 > >>> +check as hv1 ovn-appctl -t ovn-controller debug/resume > >>> +check ovn-nbctl --wait=hv sync > >>> + > >>> +OVS_WAIT_UNTIL([test 0 = $(as hv1 ovs-vsctl list Mirror | wc -l)]) > >>> + > >>> +OVN_CLEANUP([hv1]) > >>> +AT_CLEANUP > >>> +]) > >>> > >>> OVN_FOR_EACH_NORTHD([ > >>> AT_SETUP([Port Groups]) > >>> diff --git a/utilities/ovn-nbctl.c b/utilities/ovn-nbctl.c > >>> index 811468dc6..af2e61435 100644 > >>> --- a/utilities/ovn-nbctl.c > >>> +++ b/utilities/ovn-nbctl.c > >>> @@ -271,6 +271,19 @@ QoS commands:\n\ > >>> remove QoS rules from SWITCH\n\ > >>> qos-list SWITCH print QoS rules for SWITCH\n\ > >>> \n\ > >>> +Mirror commands:\n\ > >>> + mirror-add NAME TYPE INDEX FILTER IP\n\ > >>> + add a mirror with given name\n\ > >>> + specify TYPE 'gre' or 'erspan'\n\ > >>> + specify the tunnel INDEX value\n\ > >>> + (indicates key if GRE\n\ > >>> + erpsan_idx if ERSPAN)\n\ > >>> + specify FILTER for mirroring selection\n\ > >>> + 'to-lport' / 'from-lport' / 'both'\n\ > >>> + specify Sink / Destination i.e. Remote > >>> IP\n\ > >>> + mirror-del [NAME] remove mirrors\n\ > >>> + mirror-list print mirrors\n\ > >>> +\n\ > >>> Meter commands:\n\ > >>> [--fair]\n\ > >>> meter-add NAME ACTION RATE UNIT [BURST]\n\ > >>> @@ -311,6 +324,8 @@ Logical switch port commands:\n\ > >>> set dhcpv6 options for PORT\n\ > >>> lsp-get-dhcpv6-options PORT get the dhcpv6 options for PORT\n\ > >>> lsp-get-ls PORT get the logical switch which the port > >>> belongs to\n\ > >>> + lsp-attach-mirror PORT MIRROR attach source PORT to MIRROR\n\ > >>> + lsp-detach-mirror PORT MIRROR detach source PORT from MIRROR\n\ > >>> \n\ > >>> Forwarding group commands:\n\ > >>> [--liveness]\n\ > >>> @@ -1685,6 +1700,130 @@ nbctl_pre_lsp_type(struct ctl_context *ctx) > >>> ovsdb_idl_add_column(ctx->idl, > >>> &nbrec_logical_switch_port_col_type); > >>> } > >>> > >>> +static void > >>> +nbctl_pre_lsp_mirror(struct ctl_context *ctx) > >>> +{ > >>> + ovsdb_idl_add_column(ctx->idl, > >>> &nbrec_logical_switch_port_col_name); > >>> + ovsdb_idl_add_column(ctx->idl, > >>> + &nbrec_logical_switch_port_col_mirror_rules); > >>> + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_name); > >>> + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_src); > >>> +} > >>> + > >>> +static int > >>> +mirror_cmp(const void *mirror1_, const void *mirror2_) > >>> +{ > >>> + const struct nbrec_mirror *const *mirror_1 = mirror1_; > >>> + const struct nbrec_mirror *const *mirror_2 = mirror2_; > >>> + > >>> + const struct nbrec_mirror *mirror1 = *mirror_1; > >>> + const struct nbrec_mirror *mirror2 = *mirror_2; > >>> + > >>> + return strcmp(mirror1->name,mirror2->name); > >>> +} > >>> + > >>> +static char * OVS_WARN_UNUSED_RESULT > >>> +mirror_by_name_or_uuid(struct ctl_context *ctx, const char *id, > >>> + bool must_exist, > >>> + const struct nbrec_mirror **mirror_p) > >>> +{ > >>> + const struct nbrec_mirror *mirror = NULL; > >>> + *mirror_p = NULL; > >>> + > >>> + struct uuid mirror_uuid; > >>> + bool is_uuid = uuid_from_string(&mirror_uuid, id); > >>> + if (is_uuid) { > >>> + mirror = nbrec_mirror_get_for_uuid(ctx->idl, &mirror_uuid); > >>> + } > >>> + > >>> + if (!mirror) { > >>> + NBREC_MIRROR_FOR_EACH (mirror, ctx->idl) { > >>> + if (!strcmp(mirror->name, id)) { > >>> + break; > >>> + } > >>> + } > >>> + } > >>> + > >>> + if (!mirror && must_exist) { > >>> + return xasprintf("%s: mirror %s not found", > >>> + id, is_uuid ? "UUID" : "name"); > >>> + } > >>> + > >>> + *mirror_p = mirror; > >>> + return NULL; > >>> +} > >>> + > >>> +static void > >>> +nbctl_lsp_attach_mirror(struct ctl_context *ctx) > >>> +{ > >>> + const char *port = ctx->argv[1]; > >>> + const char *mirror_name = ctx->argv[2]; > >>> + const struct nbrec_logical_switch_port *lsp = NULL; > >>> + const struct nbrec_mirror *mirror; > >>> + > >>> + char *error; > >>> + > >>> + error = lsp_by_name_or_uuid(ctx, port, true, &lsp); > >>> + if (error) { > >>> + ctx->error = error; > >>> + return; > >>> + } > >>> + > >>> + > >>> + /*check if a mirror rule actually exists on that name or not*/ > >>> + error = mirror_by_name_or_uuid(ctx, mirror_name, true, &mirror); > >>> + if (error) { > >>> + ctx->error = error; > >>> + return; > >>> + } > >>> + > >>> + /* Check if same mirror rule already exists for the lsp */ > >>> + for (size_t i = 0; i < lsp->n_mirror_rules; i++) { > >>> + if (!mirror_cmp(&lsp->mirror_rules[i], &mirror)) { > >>> + bool may_exist = shash_find(&ctx->options, > >>> "--may-exist") != NULL; > >>> + if (!may_exist) { > >>> + ctl_error(ctx, "Same mirror already existed on the > >>> lsp %s.", > >>> + ctx->argv[1]); > >>> + return; > >>> + } > >>> + return; > >>> + } > >>> + } > >>> + > >>> + nbrec_logical_switch_port_update_mirror_rules_addvalue(lsp, mirror); > >>> + nbrec_mirror_update_src_addvalue(mirror,lsp); > >>> + > >>> +} > >>> + > >>> +static void > >>> +nbctl_lsp_detach_mirror(struct ctl_context *ctx) > >>> +{ > >>> + const char *port = ctx->argv[1]; > >>> + const char *mirror_name = ctx->argv[2]; > >>> + const struct nbrec_logical_switch_port *lsp = NULL; > >>> + const struct nbrec_mirror *mirror; > >>> + > >>> + char *error; > >>> + > >>> + error = lsp_by_name_or_uuid(ctx, port, true, &lsp); > >>> + if (error) { > >>> + ctx->error = error; > >>> + return; > >>> + } > >>> + > >>> + > >>> + /*check if a mirror rule actually exists on that name or not*/ > >>> + error = mirror_by_name_or_uuid(ctx, mirror_name, true, &mirror); > >>> + if (error) { > >>> + ctx->error = error; > >>> + return; > >>> + } > >>> + > >>> + nbrec_logical_switch_port_update_mirror_rules_delvalue(lsp, mirror); > >>> + nbrec_mirror_update_src_delvalue(mirror,lsp); > >>> + > >>> +} > >>> + > >>> static void > >>> nbctl_lsp_set_type(struct ctl_context *ctx) > >>> { > >>> @@ -7241,6 +7380,211 @@ cmd_ha_ch_grp_set_chassis_prio(struct > >>> ctl_context *ctx) > >>> nbrec_ha_chassis_set_priority(ha_chassis, priority); > >>> } > >>> > >>> +static char * OVS_WARN_UNUSED_RESULT > >>> +parse_filter(const char *arg, const char **selection_p) > >>> +{ > >>> + /* Validate selection. Only require the first letter. */ > >>> + if (arg[0] == 't') { > >>> + *selection_p = "to-lport"; > >>> + } else if (arg[0] == 'f') { > >>> + *selection_p = "from-lport"; > >>> + } else if (arg[0] == 'b') { > >>> + *selection_p = "both"; > >>> + } else { > >>> + *selection_p = NULL; > >>> + return xasprintf("%s: selection must be \"to-lport\" or " > >>> + "\"from-lport\" or \"both\" ", arg); > >>> + } > >>> + return NULL; > >>> +} > >>> + > >>> +static char * OVS_WARN_UNUSED_RESULT > >>> +parse_type(const char *arg, const char **type_p) > >>> +{ > >>> + /* Validate type. Only require the first letter. */ > >>> + if (arg[0] == 'g') { > >>> + *type_p = "gre"; > >>> + } else if (arg[0] == 'e') { > >>> + *type_p = "erspan"; > >>> + } else { > >>> + *type_p = NULL; > >>> + return xasprintf("%s: type must be \"gre\" or " > >>> + "\"erspan\"", arg); > >>> + } > >>> + return NULL; > >>> +} > >>> + > >>> +static void > >>> +nbctl_pre_mirror_add(struct ctl_context *ctx) > >>> +{ > >>> + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_name); > >>> + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_filter); > >>> + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_index); > >>> + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_sink); > >>> + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_type); > >>> +} > >>> + > >>> +static void > >>> +nbctl_mirror_add(struct ctl_context *ctx) > >>> +{ > >>> + const char *filter = NULL; > >>> + const char *sink_ip = NULL; > >>> + const char *type = NULL; > >>> + const char *name = NULL; > >>> + char *new_sink_ip = NULL; > >>> + int64_t index; > >>> + char *error = NULL; > >>> + const struct nbrec_mirror *mirror_check = NULL; > >>> + > >>> + /* Mirror Name */ > >>> + name = ctx->argv[1]; > >>> + NBREC_MIRROR_FOR_EACH (mirror_check, ctx->idl) { > >>> + if (!strcmp(mirror_check->name, name)) { > >>> + ctl_error(ctx, "Mirror with %s name already exists.", > >>> + name); > >>> + return; > >>> + } > >>> + } > >>> + > >>> + /* Tunnel Type - GRE/ERSPAN */ > >>> + error = parse_type(ctx->argv[2], &type); > >>> + if (error) { > >>> + ctx->error = error; > >>> + return; > >>> + } > >>> + > >>> + /* tunnel index / GRE key / ERSPAN idx */ > >>> + index = atoi(ctx->argv[3]); > >> Shouldn't we validate the input is an actual number? > >> > >>> + > >>> + /* Filter for mirroring */ > >>> + error = parse_filter(ctx->argv[4], &filter); > >>> + if (error) { > >>> + ctx->error = error; > >>> + return; > >>> + } > >>> + > >>> + /* Destination / Sink details */ > >>> + sink_ip = ctx->argv[5]; > >>> + > >>> + /* check if it is a valid ip */ > >>> + new_sink_ip = normalize_ipv4_addr_str(sink_ip); > >>> + if (!new_sink_ip) { > >>> + new_sink_ip = normalize_ipv6_addr_str(sink_ip); > >>> + } > >>> + > >>> + if (new_sink_ip) { > >>> + free(new_sink_ip); > >>> + } else { > >>> + ctl_error(ctx, "Invalid sink ip: %s", sink_ip); > >>> + return; > >>> + } > >>> + > >>> + /* Create the mirror. */ > >>> + struct nbrec_mirror *mirror = nbrec_mirror_insert(ctx->txn); > >>> + nbrec_mirror_set_name(mirror, name); > >>> + nbrec_mirror_set_index(mirror, index); > >>> + nbrec_mirror_set_filter(mirror, filter); > >>> + nbrec_mirror_set_type(mirror, type); > >>> + nbrec_mirror_set_sink(mirror, sink_ip); > >>> + > >>> +} > >>> + > >>> +static void > >>> +nbctl_pre_mirror_del(struct ctl_context *ctx) > >>> +{ > >>> + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_name); > >>> + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_src); > >>> +} > >>> + > >>> +static void > >>> +nbctl_mirror_del(struct ctl_context *ctx) > >>> +{ > >>> + const struct nbrec_mirror *mirror, *next; > >>> + > >>> + /* If a name is not specified, delete all mirrors. */ > >>> + if (ctx->argc == 1) { > >>> + NBREC_MIRROR_FOR_EACH_SAFE (mirror, next, ctx->idl) { > >>> + nbrec_mirror_delete(mirror); > >>> + } > >>> + return; > >>> + } > >>> + > >>> + /* Remove the matching mirror. */ > >>> + NBREC_MIRROR_FOR_EACH (mirror, ctx->idl) { > >>> + if (strcmp(ctx->argv[1], mirror->name)) { > >>> + continue; > >>> + } > >>> + nbrec_mirror_delete(mirror); > >>> + return; > >>> + } > >>> +} > >>> + > >>> +static void > >>> +nbctl_pre_mirror_list(struct ctl_context *ctx) > >>> +{ > >>> + ovsdb_idl_add_column(ctx->idl, > >>> &nbrec_logical_switch_port_col_name); > >>> + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_name); > >>> + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_filter); > >>> + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_index); > >>> + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_sink); > >>> + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_type); > >>> + ovsdb_idl_add_column(ctx->idl, &nbrec_mirror_col_src); > >>> +} > >>> + > >>> +static void > >>> +nbctl_mirror_list(struct ctl_context *ctx) > >>> +{ > >>> + > >>> + const struct nbrec_mirror **mirrors = NULL; > >>> + const struct nbrec_mirror *mirror; > >>> + size_t n_capacity = 0; > >>> + size_t n_mirrors = 0; > >>> + > >>> + NBREC_MIRROR_FOR_EACH (mirror, ctx->idl) { > >>> + if (n_mirrors == n_capacity) { > >>> + mirrors = x2nrealloc(mirrors, &n_capacity, sizeof > >>> *mirrors); > >>> + } > >>> + > >>> + mirrors[n_mirrors] = mirror; > >>> + n_mirrors++; > >>> + } > >>> + > >>> + if (n_mirrors) { > >>> + qsort(mirrors, n_mirrors, sizeof *mirrors, mirror_cmp); > >>> + } > >>> + > >>> + for (size_t i = 0; i < n_mirrors; i++) { > >>> + mirror = mirrors[i]; > >>> + ds_put_format(&ctx->output, "%s:\n", mirror->name); > >>> + /* print all the values */ > >>> + ds_put_format(&ctx->output, " Type : %s\n", > >>> mirror->type); > >>> + ds_put_format(&ctx->output, " Sink : %s\n", > >>> mirror->sink); > >>> + ds_put_format(&ctx->output, " Filter : %s\n", > >>> mirror->filter); > >>> + ds_put_format(&ctx->output, " Index/Key: %ld\n", > >>> + (long int) > >>> mirror->index); > >> You don't ned to cast if you pass %d formatter instead of %ld. The > >> same applies to other places in the patch where you cast to long int. > >> In general, casting is not needed and should be avoided. > >> > >>> + ds_put_cstr(&ctx->output, " Sources :"); > >>> + if (mirror->n_src > 0) { > >>> + struct svec srcs; > >>> + const char *src; > >>> + size_t j; > >>> + svec_init(&srcs); > >>> + for (j = 0; j < mirror->n_src; j++) { > >>> + svec_add(&srcs, mirror->src[j]->name); > >>> + } > >>> + svec_sort(&srcs); > >>> + SVEC_FOR_EACH (j, src, &srcs) { > >>> + ds_put_format(&ctx->output, " %s", src); > >>> + } > >>> + svec_destroy(&srcs); > >>> + } else { > >>> + ds_put_cstr(&ctx->output, " None attached"); > >>> + } > >>> + ds_put_cstr(&ctx->output, "\n"); > >>> + } > >>> + > >>> + free(mirrors); > >>> +} > >>> + > >>> static const struct ctl_table_class tables[NBREC_N_TABLES] = { > >>> [NBREC_TABLE_DHCP_OPTIONS].row_ids > >>> = {{&nbrec_logical_switch_port_col_name, NULL, > >>> @@ -7334,6 +7678,15 @@ static const struct ctl_command_syntax > >>> nbctl_commands[] = { > >>> { "qos-list", 1, 1, "SWITCH", nbctl_pre_qos_list, nbctl_qos_list, > >>> NULL, "", RO }, > >>> > >>> + /* mirror commands. */ > >>> + { "mirror-add", 5, 5, > >>> + "NAME TYPE INDEX FILTER IP", > >>> + nbctl_pre_mirror_add, nbctl_mirror_add, NULL, "--may-exist", > >>> RW }, > >>> + { "mirror-del", 0, 1, "[NAME]", > >>> + nbctl_pre_mirror_del, nbctl_mirror_del, NULL, "", RW }, > >>> + { "mirror-list", 0, 0, "", nbctl_pre_mirror_list, > >>> nbctl_mirror_list, > >>> + NULL, "", RO }, > >>> + > >>> /* meter commands. */ > >>> { "meter-add", 4, 5, "NAME ACTION RATE UNIT [BURST]", > >>> nbctl_pre_meter_add, > >>> nbctl_meter_add, NULL, "--fair,--may-exist", RW }, > >>> @@ -7388,6 +7741,10 @@ static const struct ctl_command_syntax > >>> nbctl_commands[] = { > >>> nbctl_lsp_get_dhcpv6_options, NULL, "", RO }, > >>> { "lsp-get-ls", 1, 1, "PORT", nbctl_pre_lsp_get_ls, > >>> nbctl_lsp_get_ls, > >>> NULL, "", RO }, > >>> + { "lsp-attach-mirror", 2, 2, "PORT MIRROR", nbctl_pre_lsp_mirror, > >>> + nbctl_lsp_attach_mirror, NULL, "", RW }, > >>> + { "lsp-detach-mirror", 2, 2, "PORT MIRROR", nbctl_pre_lsp_mirror, > >>> + nbctl_lsp_detach_mirror, NULL, "", RW }, > >>> > >>> /* forwarding group commands. */ > >>> { "fwd-group-add", 4, INT_MAX, "SWITCH GROUP VIP VMAC PORT...", > >>> diff --git a/utilities/ovn-sbctl.c b/utilities/ovn-sbctl.c > >>> index f60dde1b6..3d73e9e25 100644 > >>> --- a/utilities/ovn-sbctl.c > >>> +++ b/utilities/ovn-sbctl.c > >>> @@ -307,6 +307,7 @@ pre_get_info(struct ctl_context *ctx) > >>> ovsdb_idl_add_column(ctx->idl, &sbrec_port_binding_col_chassis); > >>> ovsdb_idl_add_column(ctx->idl, &sbrec_port_binding_col_datapath); > >>> ovsdb_idl_add_column(ctx->idl, &sbrec_port_binding_col_up); > >>> + ovsdb_idl_add_column(ctx->idl, > >>> &sbrec_port_binding_col_mirror_rules); > >>> > >>> ovsdb_idl_add_column(ctx->idl, > >>> &sbrec_logical_flow_col_logical_datapath); > >>> ovsdb_idl_add_column(ctx->idl, > >>> &sbrec_logical_flow_col_logical_dp_group); > >>> @@ -1431,6 +1432,9 @@ static const struct ctl_table_class > >>> tables[SBREC_N_TABLES] = { > >>> [SBREC_TABLE_HA_CHASSIS_GROUP].row_ids[0] > >>> = {&sbrec_ha_chassis_group_col_name, NULL, NULL}, > >>> > >>> + [SBREC_TABLE_MIRROR].row_ids[0] > >>> + = {&sbrec_mirror_col_name, NULL, NULL}, > >>> + > >>> [SBREC_TABLE_METER].row_ids[0] > >>> = {&sbrec_meter_col_name, NULL, NULL}, > >>> > >>> -- > >>> 2.31.1 > >>> > > _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev