diff mbox

[ovs-dev,v2] ovn-tutorial: Add a section on ACLs.

Message ID 1446652412-13609-1-git-send-email-rbryant@redhat.com
State Accepted
Headers show

Commit Message

Russell Bryant Nov. 4, 2015, 3:53 p.m. UTC
Add a section that gives a quick introduction to applying ACLs.  It
discusses how the ACLs are translated into OVN logical flows. It doesn't
get down to the OpenFlow level because that's not supported in
ovs-sandbox yet.  Instead, it provides a reference to an OpenStack
related blog post that talks about how OVN ACLs are used there and gives
examples of the resulting OpenFlow flows.

In theory, once we have a userspace conntrack implementation available,
we'll be able to provide better suppot for it in ovs-sandbox.

Signed-off-by: Russell Bryant <rbryant@redhat.com>
---
 tutorial/OVN-Tutorial.md      | 84 +++++++++++++++++++++++++++++++++++++++++++
 tutorial/automake.mk          |  4 ++-
 tutorial/ovn/env6/add-acls.sh | 21 +++++++++++
 tutorial/ovn/env6/setup.sh    | 46 ++++++++++++++++++++++++
 4 files changed, 154 insertions(+), 1 deletion(-)
 create mode 100755 tutorial/ovn/env6/add-acls.sh
 create mode 100755 tutorial/ovn/env6/setup.sh

Comments

Kyle Mestery Nov. 4, 2015, 3:58 p.m. UTC | #1
Thanks for writing this up Russell! I found super pedantic (possible) nit,
but otherwise, this reads fine to me and was helpful in understanding how
ACLs work. Thanks!

Acked-by: Kyle Mestery <mestery@mestery.com>

On Wed, Nov 4, 2015 at 9:53 AM, Russell Bryant <rbryant@redhat.com> wrote:

> Add a section that gives a quick introduction to applying ACLs.  It
> discusses how the ACLs are translated into OVN logical flows. It doesn't
> get down to the OpenFlow level because that's not supported in
> ovs-sandbox yet.  Instead, it provides a reference to an OpenStack
> related blog post that talks about how OVN ACLs are used there and gives
> examples of the resulting OpenFlow flows.
>
> In theory, once we have a userspace conntrack implementation available,
> we'll be able to provide better suppot for it in ovs-sandbox.
>
> Signed-off-by: Russell Bryant <rbryant@redhat.com>
> ---
>  tutorial/OVN-Tutorial.md      | 84
> +++++++++++++++++++++++++++++++++++++++++++
>  tutorial/automake.mk          |  4 ++-
>  tutorial/ovn/env6/add-acls.sh | 21 +++++++++++
>  tutorial/ovn/env6/setup.sh    | 46 ++++++++++++++++++++++++
>  4 files changed, 154 insertions(+), 1 deletion(-)
>  create mode 100755 tutorial/ovn/env6/add-acls.sh
>  create mode 100755 tutorial/ovn/env6/setup.sh
>
> diff --git a/tutorial/OVN-Tutorial.md b/tutorial/OVN-Tutorial.md
> index 4fc06eb..667b76b 100644
> --- a/tutorial/OVN-Tutorial.md
> +++ b/tutorial/OVN-Tutorial.md
> @@ -628,6 +628,87 @@ see it output to OpenFlow ports 5 and 6 only.
>      $ ovn/env5/packet2.sh
>
>
> +6) Stateful ACLs
> +----------------
> +
> +ACLs provide a way to do distributed packet filtering for OVN networks.
> One
> +example use of ACLs is that OpenStack Neutron uses them to implement
> security
> +groups.  ACLs are implemented using conntrack integration with OVS.
> +
> +Start with a simple logical switch with 2 logical ports.
> +
> +[View ovn/env6/setup.sh][env6setup].
> +
> +    $ ovn/env6/setup.sh
> +
> +A common use case would be the following policy applied for `sw0-port1`:
> +
> +* Allow outbound IP traffic  and associated return traffic.
>

To my eyes, looks like an extra space after "traffic" above, but it's super
pedantic, so only re-spin if you need to for some other reason. :)


> +* Allow incoming ICMP requests and associated return traffic.
> +* Allow incoming SSH connections and associated return traffic.
> +* Drop other incoming IP traffic.
> +
> +The following script applies this policy to our environment.
> +
> +[View ovn/env6/add-acls.sh][env6acls].
> +
> +    $ ovn/env6/add-acls.sh
> +
> +We can view the configured ACLs on this network using the `ovn-nbctl`
> command.
> +
> +    $ ovn-nbctl acl-list sw0
> +    from-lport  1002 (inport == “sw0-port1” && ip) allow-related
> +      to-lport  1002 (outport == “sw0-port1” && ip && icmp) allow-related
> +      to-lport  1002 (outport == “sw0-port1” && ip && tcp && tcp.dst ==
> 22) allow-related
> +      to-lport  1001 (outport == “sw0-port1” && ip) drop
> +
> +Now that we have ACLs configured, there are new entries in the logical
> flow
> +table in the stages `switch_in_pre_acl`, switch_in_acl`,
> `switch_out_pre_acl`,
> +and `switch_out_acl`.
> +
> +    $ ovn-sbctl lflow-list
> +
> +Let’s look more closely at `switch_out_pre_acl` and `switch_out_acl`.
> +
> +In `switch_out_pre_acl`, we match IP traffic and put it through the
> connection
> +tracker.  This populates the connection state fields so that we can apply
> policy
> +as appropriate.
> +
> +    table=0(switch_out_pre_acl), priority=  100, match=(ip),
> action=(ct_next;)
> +    table=1(switch_in_pre_acl), priority=    0, match=(1), action=(next;)
> +
> +In `switch_out_acl`, we allow packets associated with existing
> connections.  We
> +drop packets that are deemed to be invalid (such as non-SYN TCP packet not
> +associated with an existing connection).
> +
> +    table=1(switch_out_acl), priority=65535, match=(!ct.est && ct.rel &&
> !ct.new && !ct.inv), action=(next;)
> +    table=1(switch_out_acl), priority=65535, match=(ct.est && !ct.rel &&
> !ct.new && !ct.inv), action=(next;)
> +    table=1(switch_out_acl), priority=65535, match=(ct.inv),
> action=(drop;)
> +
> +For new connections, we apply our configured ACL policy to decide whether
> to
> +allow the connection or not.  In this case, we’ll allow ICMP or SSH.
> Otherwise,
> +we’ll drop the packet.
> +
> +    table=1(switch_out_acl), priority= 2002, match=(ct.new && (outport ==
> “sw0-port1” && ip && icmp)), action=(ct_commit; next;)
> +    table=1(switch_out_acl), priority= 2002, match=(ct.new && (outport ==
> “sw0-port1” && ip && tcp && tcp.dst == 22)), action=(ct_commit; next;)
> +    table=1(switch_out_acl), priority= 2001, match=(outport ==
> “sw0-port1” && ip), action=(drop;)
> +
> +When using ACLs, the default policy is to allow and track IP
> connections.  Based
> +on our above policy, IP traffic directed at `sw0-port1` will never hit
> this flow
> +at priority 1.
> +
> +    table=1(switch_out_acl), priority=    1, match=(ip),
> action=(ct_commit; next;)
> +    table=1(switch_out_acl), priority=    0, match=(1), action=(next;)
> +
> +Note that conntrack integration is not yet supported in ovs-sandbox, so
> the
> +OpenFlow flows will not represent what you’d see in a real environment.
> The
> +logical flows described above give a very good idea of what the flows
> look like,
> +though.
> +
> +[This blog post][openstack-ovn-acl-blog] discusses OVN ACLs from an
> OpenStack
> +perspective and also provides an example of what the resulting OpenFlow
> flows
> +look like.
> +
>  [ovn-architecture(7)]:
> http://openvswitch.org/support/dist-docs/ovn-architecture.7.html
>  [Tutorial.md]:./Tutorial.md
>  [ovn-nb(5)]:http://openvswitch.org/support/dist-docs/ovn-nb.5.html
> @@ -659,3 +740,6 @@ see it output to OpenFlow ports 5 and 6 only.
>  [env5setup]:./ovn/env5/setup.sh
>  [env5packet1]:./ovn/env5/packet1.sh
>  [env5packet2]:./ovn/env5/packet2.sh
> +[env6setup]:./ovn/env6/setup.sh
> +[env6acls]:./ovn/env6/add-acls.sh
> +[openstack-ovn-acl-blog]:
> http://blog.russellbryant.net/2015/10/22/openstack-security-groups-using-ovn-acls/
> diff --git a/tutorial/automake.mk b/tutorial/automake.mk
> index e86ef74..f41c406 100644
> --- a/tutorial/automake.mk
> +++ b/tutorial/automake.mk
> @@ -28,7 +28,9 @@ EXTRA_DIST += \
>         tutorial/ovn/env4/packet5.sh \
>         tutorial/ovn/env5/setup.sh \
>         tutorial/ovn/env5/packet1.sh \
> -       tutorial/ovn/env5/packet2.sh
> +       tutorial/ovn/env5/packet2.sh \
> +       tutorial/ovn/env6/setup.sh \
> +       tutorial/ovn/env6/add-acls.sh
>
>  sandbox: all
>         cd $(srcdir)/tutorial && MAKE=$(MAKE) ./ovs-sandbox -b
> $(abs_builddir) $(SANDBOXFLAGS)
> diff --git a/tutorial/ovn/env6/add-acls.sh b/tutorial/ovn/env6/add-acls.sh
> new file mode 100755
> index 0000000..74cf17b
> --- /dev/null
> +++ b/tutorial/ovn/env6/add-acls.sh
> @@ -0,0 +1,21 @@
> +#!/bin/bash
> +#
> +# 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.
> +#
> +
> +set -o xtrace
> +
> +ovn-nbctl acl-add sw0 from-lport 1002 "inport == \"sw0-port1\" && ip"
> allow-related
> +ovn-nbctl acl-add sw0 to-lport 1002 "outport == \"sw0-port1\" && ip &&
> icmp" allow-related
> +ovn-nbctl acl-add sw0 to-lport 1002 "outport == \"sw0-port1\" && ip &&
> tcp && tcp.dst == 22" allow-related
> +ovn-nbctl acl-add sw0 to-lport 1001 "outport == \"sw0-port1\" && ip" drop
> diff --git a/tutorial/ovn/env6/setup.sh b/tutorial/ovn/env6/setup.sh
> new file mode 100755
> index 0000000..78657e9
> --- /dev/null
> +++ b/tutorial/ovn/env6/setup.sh
> @@ -0,0 +1,46 @@
> +#!/bin/bash
> +#
> +# 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.
> +#
> +
> +#
> +# See "Simple two-port setup" in tutorial/OVN-Tutorial.md.
> +#
> +
> +set -o xtrace
> +
> +# Create a logical switch named "sw0"
> +ovn-nbctl lswitch-add sw0
> +
> +# Create two logical ports on "sw0".
> +ovn-nbctl lport-add sw0 sw0-port1
> +ovn-nbctl lport-add sw0 sw0-port2
> +
> +# Set a MAC address for each of the two logical ports.
> +ovn-nbctl lport-set-addresses sw0-port1 00:00:00:00:00:01
> +ovn-nbctl lport-set-addresses sw0-port2 00:00:00:00:00:02
> +
> +# Set up port security for the two logical ports.  This ensures that
> +# the logical port mac address we have configured is the only allowed
> +# source and destination mac address for these ports.
> +ovn-nbctl lport-set-port-security sw0-port1 00:00:00:00:00:01
> +ovn-nbctl lport-set-port-security sw0-port2 00:00:00:00:00:02
> +
> +# Create ports on the local OVS bridge, br-int.  When ovn-controller
> +# sees these ports show up with an "iface-id" that matches the OVN
> +# logical port names, it associates these local ports with the OVN
> +# logical ports.  ovn-controller will then set up the flows necessary
> +# for these ports to be able to communicate each other as defined by
> +# the OVN logical topology.
> +ovs-vsctl add-port br-int lport1 -- set Interface lport1
> external_ids:iface-id=sw0-port1
> +ovs-vsctl add-port br-int lport2 -- set Interface lport2
> external_ids:iface-id=sw0-port2
> --
> 2.5.0
>
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> http://openvswitch.org/mailman/listinfo/dev
>
Russell Bryant Nov. 4, 2015, 4:04 p.m. UTC | #2
On 11/04/2015 10:58 AM, Kyle Mestery wrote:
> Thanks for writing this up Russell! I found super pedantic (possible)
> nit, but otherwise, this reads fine to me and was helpful in
> understanding how ACLs work. Thanks!
> 
> Acked-by: Kyle Mestery <mestery@mestery.com <mailto:mestery@mestery.com>>

Thanks for the review!  I fixed the typo you pointed out and pushed this
to master.

> 
> On Wed, Nov 4, 2015 at 9:53 AM, Russell Bryant <rbryant@redhat.com
> <mailto:rbryant@redhat.com>> wrote:
> 
>     Add a section that gives a quick introduction to applying ACLs.  It
>     discusses how the ACLs are translated into OVN logical flows. It doesn't
>     get down to the OpenFlow level because that's not supported in
>     ovs-sandbox yet.  Instead, it provides a reference to an OpenStack
>     related blog post that talks about how OVN ACLs are used there and gives
>     examples of the resulting OpenFlow flows.
> 
>     In theory, once we have a userspace conntrack implementation available,
>     we'll be able to provide better suppot for it in ovs-sandbox.
> 
>     Signed-off-by: Russell Bryant <rbryant@redhat.com
>     <mailto:rbryant@redhat.com>>
>     ---
>      tutorial/OVN-Tutorial.md      | 84
>     +++++++++++++++++++++++++++++++++++++++++++
>      tutorial/automake.mk <http://automake.mk>          |  4 ++-
>      tutorial/ovn/env6/add-acls.sh | 21 +++++++++++
>      tutorial/ovn/env6/setup.sh    | 46 ++++++++++++++++++++++++
>      4 files changed, 154 insertions(+), 1 deletion(-)
>      create mode 100755 tutorial/ovn/env6/add-acls.sh
>      create mode 100755 tutorial/ovn/env6/setup.sh
> 
>     diff --git a/tutorial/OVN-Tutorial.md b/tutorial/OVN-Tutorial.md
>     index 4fc06eb..667b76b 100644
>     --- a/tutorial/OVN-Tutorial.md
>     +++ b/tutorial/OVN-Tutorial.md
>     @@ -628,6 +628,87 @@ see it output to OpenFlow ports 5 and 6 only.
>          $ ovn/env5/packet2.sh
> 
> 
>     +6) Stateful ACLs
>     +----------------
>     +
>     +ACLs provide a way to do distributed packet filtering for OVN
>     networks.  One
>     +example use of ACLs is that OpenStack Neutron uses them to
>     implement security
>     +groups.  ACLs are implemented using conntrack integration with OVS.
>     +
>     +Start with a simple logical switch with 2 logical ports.
>     +
>     +[View ovn/env6/setup.sh][env6setup].
>     +
>     +    $ ovn/env6/setup.sh
>     +
>     +A common use case would be the following policy applied for
>     `sw0-port1`:
>     +
>     +* Allow outbound IP traffic  and associated return traffic.
> 
> 
> To my eyes, looks like an extra space after "traffic" above, but it's
> super pedantic, so only re-spin if you need to for some other reason. :)

There is indeed an extra space there.  I removed it.
diff mbox

Patch

diff --git a/tutorial/OVN-Tutorial.md b/tutorial/OVN-Tutorial.md
index 4fc06eb..667b76b 100644
--- a/tutorial/OVN-Tutorial.md
+++ b/tutorial/OVN-Tutorial.md
@@ -628,6 +628,87 @@  see it output to OpenFlow ports 5 and 6 only.
     $ ovn/env5/packet2.sh
 
 
+6) Stateful ACLs
+----------------
+
+ACLs provide a way to do distributed packet filtering for OVN networks.  One
+example use of ACLs is that OpenStack Neutron uses them to implement security
+groups.  ACLs are implemented using conntrack integration with OVS.
+
+Start with a simple logical switch with 2 logical ports.
+
+[View ovn/env6/setup.sh][env6setup].
+
+    $ ovn/env6/setup.sh
+
+A common use case would be the following policy applied for `sw0-port1`:
+
+* Allow outbound IP traffic  and associated return traffic.
+* Allow incoming ICMP requests and associated return traffic.
+* Allow incoming SSH connections and associated return traffic.
+* Drop other incoming IP traffic.
+
+The following script applies this policy to our environment.
+
+[View ovn/env6/add-acls.sh][env6acls].
+
+    $ ovn/env6/add-acls.sh
+
+We can view the configured ACLs on this network using the `ovn-nbctl` command.
+
+    $ ovn-nbctl acl-list sw0
+    from-lport  1002 (inport == “sw0-port1” && ip) allow-related
+      to-lport  1002 (outport == “sw0-port1” && ip && icmp) allow-related
+      to-lport  1002 (outport == “sw0-port1” && ip && tcp && tcp.dst == 22) allow-related
+      to-lport  1001 (outport == “sw0-port1” && ip) drop
+
+Now that we have ACLs configured, there are new entries in the logical flow
+table in the stages `switch_in_pre_acl`, switch_in_acl`, `switch_out_pre_acl`,
+and `switch_out_acl`.
+
+    $ ovn-sbctl lflow-list
+
+Let’s look more closely at `switch_out_pre_acl` and `switch_out_acl`.
+
+In `switch_out_pre_acl`, we match IP traffic and put it through the connection
+tracker.  This populates the connection state fields so that we can apply policy
+as appropriate.
+
+    table=0(switch_out_pre_acl), priority=  100, match=(ip), action=(ct_next;)
+    table=1(switch_in_pre_acl), priority=    0, match=(1), action=(next;)
+
+In `switch_out_acl`, we allow packets associated with existing connections.  We
+drop packets that are deemed to be invalid (such as non-SYN TCP packet not
+associated with an existing connection).
+
+    table=1(switch_out_acl), priority=65535, match=(!ct.est && ct.rel && !ct.new && !ct.inv), action=(next;)
+    table=1(switch_out_acl), priority=65535, match=(ct.est && !ct.rel && !ct.new && !ct.inv), action=(next;)
+    table=1(switch_out_acl), priority=65535, match=(ct.inv), action=(drop;)
+
+For new connections, we apply our configured ACL policy to decide whether to
+allow the connection or not.  In this case, we’ll allow ICMP or SSH.  Otherwise,
+we’ll drop the packet.
+
+    table=1(switch_out_acl), priority= 2002, match=(ct.new && (outport == “sw0-port1” && ip && icmp)), action=(ct_commit; next;)
+    table=1(switch_out_acl), priority= 2002, match=(ct.new && (outport == “sw0-port1” && ip && tcp && tcp.dst == 22)), action=(ct_commit; next;)
+    table=1(switch_out_acl), priority= 2001, match=(outport == “sw0-port1” && ip), action=(drop;)
+
+When using ACLs, the default policy is to allow and track IP connections.  Based
+on our above policy, IP traffic directed at `sw0-port1` will never hit this flow
+at priority 1.
+
+    table=1(switch_out_acl), priority=    1, match=(ip), action=(ct_commit; next;)
+    table=1(switch_out_acl), priority=    0, match=(1), action=(next;)
+
+Note that conntrack integration is not yet supported in ovs-sandbox, so the
+OpenFlow flows will not represent what you’d see in a real environment.  The
+logical flows described above give a very good idea of what the flows look like,
+though.
+
+[This blog post][openstack-ovn-acl-blog] discusses OVN ACLs from an OpenStack
+perspective and also provides an example of what the resulting OpenFlow flows
+look like.
+
 [ovn-architecture(7)]:http://openvswitch.org/support/dist-docs/ovn-architecture.7.html
 [Tutorial.md]:./Tutorial.md
 [ovn-nb(5)]:http://openvswitch.org/support/dist-docs/ovn-nb.5.html
@@ -659,3 +740,6 @@  see it output to OpenFlow ports 5 and 6 only.
 [env5setup]:./ovn/env5/setup.sh
 [env5packet1]:./ovn/env5/packet1.sh
 [env5packet2]:./ovn/env5/packet2.sh
+[env6setup]:./ovn/env6/setup.sh
+[env6acls]:./ovn/env6/add-acls.sh
+[openstack-ovn-acl-blog]:http://blog.russellbryant.net/2015/10/22/openstack-security-groups-using-ovn-acls/
diff --git a/tutorial/automake.mk b/tutorial/automake.mk
index e86ef74..f41c406 100644
--- a/tutorial/automake.mk
+++ b/tutorial/automake.mk
@@ -28,7 +28,9 @@  EXTRA_DIST += \
 	tutorial/ovn/env4/packet5.sh \
 	tutorial/ovn/env5/setup.sh \
 	tutorial/ovn/env5/packet1.sh \
-	tutorial/ovn/env5/packet2.sh
+	tutorial/ovn/env5/packet2.sh \
+	tutorial/ovn/env6/setup.sh \
+	tutorial/ovn/env6/add-acls.sh
 
 sandbox: all
 	cd $(srcdir)/tutorial && MAKE=$(MAKE) ./ovs-sandbox -b $(abs_builddir) $(SANDBOXFLAGS)
diff --git a/tutorial/ovn/env6/add-acls.sh b/tutorial/ovn/env6/add-acls.sh
new file mode 100755
index 0000000..74cf17b
--- /dev/null
+++ b/tutorial/ovn/env6/add-acls.sh
@@ -0,0 +1,21 @@ 
+#!/bin/bash
+#
+# 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.
+#
+
+set -o xtrace
+
+ovn-nbctl acl-add sw0 from-lport 1002 "inport == \"sw0-port1\" && ip" allow-related
+ovn-nbctl acl-add sw0 to-lport 1002 "outport == \"sw0-port1\" && ip && icmp" allow-related
+ovn-nbctl acl-add sw0 to-lport 1002 "outport == \"sw0-port1\" && ip && tcp && tcp.dst == 22" allow-related
+ovn-nbctl acl-add sw0 to-lport 1001 "outport == \"sw0-port1\" && ip" drop
diff --git a/tutorial/ovn/env6/setup.sh b/tutorial/ovn/env6/setup.sh
new file mode 100755
index 0000000..78657e9
--- /dev/null
+++ b/tutorial/ovn/env6/setup.sh
@@ -0,0 +1,46 @@ 
+#!/bin/bash
+#
+# 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.
+#
+
+#
+# See "Simple two-port setup" in tutorial/OVN-Tutorial.md.
+#
+
+set -o xtrace
+
+# Create a logical switch named "sw0"
+ovn-nbctl lswitch-add sw0
+
+# Create two logical ports on "sw0".
+ovn-nbctl lport-add sw0 sw0-port1
+ovn-nbctl lport-add sw0 sw0-port2
+
+# Set a MAC address for each of the two logical ports.
+ovn-nbctl lport-set-addresses sw0-port1 00:00:00:00:00:01
+ovn-nbctl lport-set-addresses sw0-port2 00:00:00:00:00:02
+
+# Set up port security for the two logical ports.  This ensures that
+# the logical port mac address we have configured is the only allowed
+# source and destination mac address for these ports.
+ovn-nbctl lport-set-port-security sw0-port1 00:00:00:00:00:01
+ovn-nbctl lport-set-port-security sw0-port2 00:00:00:00:00:02
+
+# Create ports on the local OVS bridge, br-int.  When ovn-controller
+# sees these ports show up with an "iface-id" that matches the OVN
+# logical port names, it associates these local ports with the OVN
+# logical ports.  ovn-controller will then set up the flows necessary
+# for these ports to be able to communicate each other as defined by
+# the OVN logical topology.
+ovs-vsctl add-port br-int lport1 -- set Interface lport1 external_ids:iface-id=sw0-port1
+ovs-vsctl add-port br-int lport2 -- set Interface lport2 external_ids:iface-id=sw0-port2