ovn: Add datapath column to the MAC_Binding table
[cascardo/ovs.git] / ovn / ovn-sb.xml
index 8102eb3..3d26e65 100644 (file)
     The OVN Southbound database sits at the center of the OVN
     architecture.  It is the one component that speaks both southbound
     directly to all the hypervisors and gateways, via
-    <code>ovn-controller</code>, and northbound to the Cloud Management
-    System, via <code>ovn-northd</code>:
+    <code>ovn-controller</code>/<code>ovn-controller-vtep</code>, and
+    northbound to the Cloud Management System, via <code>ovn-northd</code>:
   </p>
 
   <h2>Database Structure</h2>
 
   <p>
-    The OVN Southbound database contains three classes of data with
+    The OVN Southbound database contains classes of data with
     different properties, as described in the sections below.
   </p>
 
     data.
   </p>
 
-  <h3>Bindings data</h3>
+  <h3>Logical-physical bindings</h3>
 
   <p>
-    Bindings data link logical and physical components.  They show the current
+    These tables link logical and physical components.  They show the current
     placement of logical components (such as VMs and VIFs) onto chassis, and
     map logical entities to the values that represent them in tunnel
     encapsulations.
   </p>
 
   <p>
-    Bindings change frequently, at least every time a VM powers up or down
+    These tables change frequently, at least every time a VM powers up or down
     or migrates, and especially quickly in a container environment.  The
     amount of data per VM (or VIF) is small.
   </p>
     contain binding data.
   </p>
 
+  <h3>MAC bindings</h3>
+
+  <p>
+    The <ref table="MAC_Binding"/> table tracks the bindings from IP addresses
+    to Ethernet addresses that are dynamically discovered using ARP (for IPv4)
+    and neighbor discovery (for IPv6).  Usually, IP-to-MAC bindings for virtual
+    machines are statically populated into the <ref table="Port_Binding"/>
+    table, so <ref table="MAC_Binding"/> is primarily used to discover bindings
+    on physical networks.
+  </p>
+
   <h2>Common Columns</h2>
 
   <p>
     <dt><code>external_ids</code>: map of string-string pairs</dt>
     <dd>
       Key-value pairs for use by the software that manages the OVN Southbound
-      database rather than by <code>ovn-controller</code>.  In particular,
-      <code>ovn-northd</code> can use key-value pairs in this column to relate
-      entities in the southbound database to higher-level entities (such as
-      entities in the OVN Northbound database).  Individual key-value pairs in
-      this column may be documented in some cases to aid in understanding and
-      troubleshooting, but the reader should not mistake such documentation as
-      comprehensive.
+      database rather than by
+      <code>ovn-controller</code>/<code>ovn-controller-vtep</code>.  In
+      particular, <code>ovn-northd</code> can use key-value pairs in this
+      column to relate entities in the southbound database to higher-level
+      entities (such as entities in the OVN Northbound database).  Individual
+      key-value pairs in this column may be documented in some cases to aid
+      in understanding and troubleshooting, but the reader should not mistake
+      such documentation as comprehensive.
     </dd>
   </dl>
 
     <p>
       Each row in this table represents a hypervisor or gateway (a chassis) in
       the physical network (PN).  Each chassis, via
-      <code>ovn-controller</code>, adds and updates its own row, and keeps a
-      copy of the remaining rows to determine how to reach other hypervisors.
+      <code>ovn-controller</code>/<code>ovn-controller-vtep</code>, adds
+      and updates its own row, and keeps a copy of the remaining rows to
+      determine how to reach other hypervisors.
     </p>
 
     <p>
     </p>
 
     <column name="name">
-      A chassis name, taken from <ref key="system-id" table="Open_vSwitch"
-      column="external_ids" db="Open_vSwitch"/> in the Open_vSwitch
-      database's <ref table="Open_vSwitch" db="Open_vSwitch"/> table.  OVN does
-      not prescribe a particular format for chassis names.
+      OVN does not prescribe a particular format for chassis names.
+      ovn-controller populates this column using <ref key="system-id"
+      table="Open_vSwitch" column="external_ids" db="Open_vSwitch"/>
+      in the Open_vSwitch database's <ref table="Open_vSwitch"
+      db="Open_vSwitch"/> table.  ovn-controller-vtep populates this
+      column with <ref table="Physical_Switch" column="name"
+      db="hardware_vtep"/> in the hardware_vtep database's
+      <ref table="Physical_Switch" db="hardware_vtep"/> table.
     </column>
 
+    <column name="hostname">
+      The hostname of the chassis, if applicable.  ovn-controller will populate
+      this column with the hostname of the host it is running on.
+      ovn-controller-vtep will leave this column empty.
+    </column>
+
+    <column name="external_ids" key="ovn-bridge-mappings">
+      <code>ovn-controller</code> populates this key with the set of bridge
+      mappings it has been configured to use.  Other applications should treat
+      this key as read-only.  See <code>ovn-controller</code>(8) for more
+      information.
+    </column>
+
+    <group title="Common Columns">
+      The overall purpose of these columns is described under <code>Common
+      Columns</code> at the beginning of this document.
+
+      <column name="external_ids"/>
+    </group>
+
     <group title="Encapsulation Configuration">
       <p>
         OVN uses encapsulation to transmit logical dataplane packets
         A <dfn>gateway</dfn> is a chassis that forwards traffic between the
         OVN-managed part of a logical network and a physical VLAN, extending a
         tunnel-based logical network into a physical network.  Gateways are
-        typically dedicated nodes that do not host VMs.
+        typically dedicated nodes that do not host VMs and will be controlled
+        by <code>ovn-controller-vtep</code>.
       </p>
 
       <column name="vtep_logical_switches">
-        Stores all vtep logical switch names connected by this gateway
-        chassis.
+        Stores all VTEP logical switch names connected by this gateway
+        chassis.  The <ref table="Port_Binding"/> table entry with
+        <ref column="options" table="Port_Binding"/>:<code>vtep-physical-switch</code>
+        equal <ref table="Chassis"/> <ref column="name" table="Chassis"/>, and
+        <ref column="options" table="Port_Binding"/>:<code>vtep-logical-switch</code>
+        value in <ref table="Chassis"/>
+        <ref column="vtep_logical_switches" table="Chassis"/>, will be
+        associated with this <ref table="Chassis"/>.
       </column>
      </group>
   </table>
       The <ref column="encaps" table="Chassis"/> column in the <ref
       table="Chassis"/> table refers to rows in this table to identify
       how OVN may transmit logical dataplane packets to this chassis.
-      Each chassis, via <code>ovn-controller</code>(8), adds and updates
-      its own rows and keeps a copy of the remaining rows to determine
-      how to reach other chassis.
+      Each chassis, via <code>ovn-controller</code>(8) or
+      <code>ovn-controller-vtep</code>(8), adds and updates its own rows
+      and keeps a copy of the remaining rows to determine how to reach
+      other chassis.
     </p>
 
     <column name="type">
     </column>
   </table>
 
+  <table name="Address_Set" title="Address Sets">
+    <p>
+      See the documentation for the <ref table="Address_Set"
+      db="OVN_Northbound"/> table in the <ref db="OVN_Northbound"/> database
+      for details.
+    </p>
+
+    <column name="name"/>
+    <column name="addresses"/>
+  </table>
+
   <table name="Logical_Flow" title="Logical Network Flows">
     <p>
       Each row in this table represents one logical flow.
       The default action when no flow matches is to drop packets.
     </p>
 
-    <p><em>Logical Life Cycle of a Packet</em></p>
+    <p><em>Architectural Logical Life Cycle of a Packet</em></p>
 
     <p>
       This following description focuses on the life cycle of a packet through
       a logical datapath, ignoring physical details of the implementation.
-      Please refer to <em>Life Cycle of a Packet</em> in
+      Please refer to <em>Architectural Physical Life Cycle of a Packet</em> in
       <code>ovn-architecture</code>(7) for the physical information.
     </p>
 
       <code>inport</code> to <code>outport</code>; if they are equal, it treats
       the <code>output</code> as a no-op.  In the common case, where they are
       different, the packet enters the egress pipeline.  This transition to the
-      egress pipeline discards register data, e.g. <code>reg0</code>
-      ... <code>reg5</code>, to achieve uniform behavior regardless of whether
-      the egress pipeline is on a different hypervisor (because registers
-      aren't preserve across tunnel encapsulation).
+      egress pipeline discards register data, e.g. <code>reg0</code> ...
+      <code>reg9</code> and connection tracking state, to achieve
+      uniform behavior regardless of whether the egress pipeline is on a
+      different hypervisor (because registers aren't preserve across
+      tunnel encapsulation).
     </p>
 
     <p>
         </code>...<code></code>''.
       </p>
 
+      <p>
+        You may refer to a set of IPv4, IPv6, or MAC addresses stored in the
+        <ref table="Address_Set"/> table by its <ref column="name"
+        table="Address_Set"/>.  An <ref table="Address_Set"/> with a name
+        of <code>set1</code> can be referred to as
+        <code>$set1</code>.
+      </p>
+
       <p><em>Miscellaneous</em></p>
 
       <p>
         symbols, only names within the flow's logical datapath may be used.
       </p>
 
+      <p>
+        The <code>reg</code><var>X</var> symbols are 32-bit integers.
+        The <code>xxreg</code><var>X</var> symbols are 128-bit integers,
+        which overlay four of the 32-bit registers: <code>xxreg0</code>
+        overlays <code>reg0</code> through <code>reg3</code>, with
+        <code>reg0</code> supplying the most-significant bits of
+        <code>xxreg0</code> and <code>reg3</code> the least-signficant.
+        <code>xxreg1</code> similarly overlays <code>reg4</code> through
+        <code>reg7</code>.
+      </p>
+
       <ul>
-        <li><code>reg0</code>...<code>reg5</code></li>
+        <li><code>reg0</code>...<code>reg9</code></li>
+        <li><code>xxreg0</code> <code>xxreg1</code></li>
         <li><code>inport</code> <code>outport</code></li>
         <li><code>eth.src</code> <code>eth.dst</code> <code>eth.type</code></li>
         <li><code>vlan.tci</code> <code>vlan.vid</code> <code>vlan.pcp</code> <code>vlan.present</code></li>
         <li><code>icmp4.type</code> <code>icmp4.code</code></li>
         <li><code>icmp6.type</code> <code>icmp6.code</code></li>
         <li><code>nd.target</code> <code>nd.sll</code> <code>nd.tll</code></li>
+        <li><code>ct_mark</code> <code>ct_label</code></li>
+        <li>
+          <p>
+            <code>ct_state</code>, which has the following Boolean subfields:
+          </p>
+          <ul>
+            <li><code>ct.new</code>: True for a new flow</li>
+            <li><code>ct.est</code>: True for an established flow</li>
+            <li><code>ct.rel</code>: True for a related flow</li>
+            <li><code>ct.rpl</code>: True for a reply flow</li>
+            <li><code>ct.inv</code>: True for a connection entry in a bad state</li>
+          </ul>
+          <p>
+            <code>ct_state</code> and its subfields are initialized by the
+            <code>ct_next</code> action, described below.
+          </p>
+        </li>
       </ul>
 
+      <p>
+        The following predicates are supported:
+      </p>
+
+      <ul>
+        <li><code>eth.bcast</code> expands to <code>eth.dst == ff:ff:ff:ff:ff:ff</code></li>
+        <li><code>eth.mcast</code> expands to <code>eth.dst[40]</code></li>
+        <li><code>vlan.present</code> expands to <code>vlan.tci[12]</code></li>
+        <li><code>ip4</code> expands to <code>eth.type == 0x800</code></li>
+        <li><code>ip4.mcast</code> expands to <code>ip4.dst[28..31] == 0xe</code></li>
+        <li><code>ip6</code> expands to <code>eth.type == 0x86dd</code></li>
+        <li><code>ip</code> expands to <code>ip4 || ip6</code></li>
+        <li><code>icmp4</code> expands to <code>ip4 &amp;&amp; ip.proto == 1</code></li>
+        <li><code>icmp6</code> expands to <code>ip6 &amp;&amp; ip.proto == 58</code></li>
+        <li><code>icmp</code> expands to <code>icmp4 || icmp6</code></li>
+        <li><code>ip.is_frag</code> expands to <code>ip.frag[0]</code></li>
+        <li><code>ip.later_frag</code> expands to <code>ip.frag[1]</code></li>
+        <li><code>ip.first_frag</code> expands to <code>ip.is_frag &amp;&amp; !ip.later_frag</code></li>
+        <li><code>arp</code> expands to <code>eth.type == 0x806</code></li>
+        <li><code>nd</code> expands to <code>icmp6.type == {135, 136} &amp;&amp; icmp6.code == 0</code></li>
+        <li><code>tcp</code> expands to <code>ip.proto == 6</code></li>
+        <li><code>udp</code> expands to <code>ip.proto == 17</code></li>
+        <li><code>sctp</code> expands to <code>ip.proto == 132</code></li>
+      </ul>
     </column>
 
     <column name="actions">
       </p>
 
       <p>
-       The following actions are defined:
+        The following actions are defined:
       </p>
 
       <dl>
         <dt><code>output;</code></dt>
         <dd>
           <p>
-           In the ingress pipeline, this action executes the
-           <code>egress</code> pipeline as a subroutine.  If
-           <code>outport</code> names a logical port, the egress pipeline
-           executes once; if it is a multicast group, the egress pipeline runs
-           once for each logical port in the group.
+            In the ingress pipeline, this action executes the
+            <code>egress</code> pipeline as a subroutine.  If
+            <code>outport</code> names a logical port, the egress pipeline
+            executes once; if it is a multicast group, the egress pipeline runs
+            once for each logical port in the group.
           </p>
 
           <p>
           <p>
             Output to the input port is implicitly dropped, that is,
             <code>output</code> becomes a no-op if <code>outport</code> ==
-            <code>inport</code>.
+            <code>inport</code>.  Occasionally it may be useful to override
+            this behavior, e.g. to send an ARP reply to an ARP request; to do
+            so, use <code>inport = "";</code> to set the logical input port to
+            an empty string (which should not be used as the name of any
+            logical port).
           </p>
-       </dd>
+        </dd>
 
         <dt><code>next;</code></dt>
+        <dt><code>next(<var>table</var>);</code></dt>
         <dd>
-          Executes the next logical datapath table as a subroutine.
+          Executes another logical datapath table as a subroutine.  By default,
+          the table after the current one is executed.  Specify
+          <var>table</var> to jump to a specific table in the same pipeline.
         </dd>
 
         <dt><code><var>field</var> = <var>constant</var>;</code></dt>
         <dd>
           <p>
-           Sets data or metadata field <var>field</var> to constant value
-           <var>constant</var>, e.g. <code>outport = "vif0";</code> to set the
-           logical output port.  To set only a subset of bits in a field,
-           specify a subfield for <var>field</var> or a masked
-           <var>constant</var>, e.g. one may use <code>vlan.pcp[2] = 1;</code>
-           or <code>vlan.pcp = 4/4;</code> to set the most sigificant bit of
-           the VLAN PCP.
+            Sets data or metadata field <var>field</var> to constant value
+            <var>constant</var>, e.g. <code>outport = "vif0";</code> to set the
+            logical output port.  To set only a subset of bits in a field,
+            specify a subfield for <var>field</var> or a masked
+            <var>constant</var>, e.g. one may use <code>vlan.pcp[2] = 1;</code>
+            or <code>vlan.pcp = 4/4;</code> to set the most sigificant bit of
+            the VLAN PCP.
           </p>
 
           <p>
             <code>ingress</code> pipeline but not in the <code>egress</code>
             pipeline.
           </p>
-       </dd>
+        </dd>
+
+        <dt><code><var>field1</var> = <var>field2</var>;</code></dt>
+        <dd>
+          <p>
+            Sets data or metadata field <var>field1</var> to the value of data
+            or metadata field <var>field2</var>, e.g. <code>reg0 =
+            ip4.src;</code> copies <code>ip4.src</code> into <code>reg0</code>.
+            To modify only a subset of a field's bits, specify a subfield for
+            <var>field1</var> or <var>field2</var> or both, e.g. <code>vlan.pcp
+            = reg0[0..2];</code> copies the least-significant bits of
+            <code>reg0</code> into the VLAN PCP.
+          </p>
+
+          <p>
+            <var>field1</var> and <var>field2</var> must be the same type,
+            either both string or both integer fields.  If they are both
+            integer fields, they must have the same width.
+          </p>
+
+          <p>
+            If <var>field1</var> or <var>field2</var> has prerequisites, they
+            are added implicitly to <ref column="match"/>.  It is possible to
+            write an assignment with contradictory prerequisites, such as
+            <code>ip4.src = ip6.src[0..31];</code>, but the contradiction means
+            that a logical flow with such an assignment will never be matched.
+          </p>
+        </dd>
+
+        <dt><code><var>field1</var> &lt;-&gt; <var>field2</var>;</code></dt>
+        <dd>
+          <p>
+            Similar to <code><var>field1</var> = <var>field2</var>;</code>
+            except that the two values are exchanged instead of copied.  Both
+            <var>field1</var> and <var>field2</var> must modifiable.
+          </p>
+        </dd>
+
+        <dt><code>ip.ttl--;</code></dt>
+        <dd>
+          <p>
+            Decrements the IPv4 or IPv6 TTL.  If this would make the TTL zero
+            or negative, then processing of the packet halts; no further
+            actions are processed.  (To properly handle such cases, a
+            higher-priority flow should match on
+            <code>ip.ttl == {0, 1};</code>.)
+          </p>
+
+          <p><b>Prerequisite:</b> <code>ip</code></p>
+        </dd>
+
+        <dt><code>ct_next;</code></dt>
+        <dd>
+          <p>
+            Apply connection tracking to the flow, initializing
+            <code>ct_state</code> for matching in later tables.
+            Automatically moves on to the next table, as if followed by
+            <code>next</code>.
+          </p>
+
+          <p>
+            As a side effect, IP fragments will be reassembled for matching.
+            If a fragmented packet is output, then it will be sent with any
+            overlapping fragments squashed.  The connection tracking state is
+            scoped by the logical port, so overlapping addresses may be used.
+            To allow traffic related to the matched flow, execute
+            <code>ct_commit</code>.
+          </p>
+
+          <p>
+            It is possible to have actions follow <code>ct_next</code>,
+            but they will not have access to any of its side-effects and
+            is not generally useful.
+          </p>
+        </dd>
+
+        <dt><code>ct_commit;</code></dt>
+        <dt><code>ct_commit(ct_mark=<var>value[/mask]</var>);</code></dt>
+        <dt><code>ct_commit(ct_label=<var>value[/mask]</var>);</code></dt>
+        <dt><code>ct_commit(ct_mark=<var>value[/mask]</var>, ct_label=<var>value[/mask]</var>);</code></dt>
+        <dd>
+          <p>
+            Commit the flow to the connection tracking entry associated with it
+            by a previous call to <code>ct_next</code>.  When
+            <code>ct_mark=<var>value[/mask]</var></code> and/or
+            <code>ct_label=<var>value[/mask]</var></code> are supplied,
+            <code>ct_mark</code> and/or <code>ct_label</code> will be set to the
+            values indicated by <var>value[/mask]</var> on the connection
+            tracking entry. <code>ct_mark</code> is a 32-bit field.
+            <code>ct_label</code> is a 128-bit field. The <var>value[/mask]</var>
+            should be specified in hex string if more than 64bits are to be used.
+          </p>
+
+          <p>
+            Note that if you want processing to continue in the next table,
+            you must execute the <code>next</code> action after
+            <code>ct_commit</code>.  You may also leave out <code>next</code>
+            which will commit connection tracking state, and then drop the
+            packet.  This could be useful for setting <code>ct_mark</code>
+            on a connection tracking entry before dropping a packet,
+            for example.
+          </p>
+        </dd>
+
+        <dt><code>ct_dnat;</code></dt>
+        <dt><code>ct_dnat(<var>IP</var>);</code></dt>
+        <dd>
+          <p>
+            <code>ct_dnat</code> sends the packet through the DNAT zone in
+            connection tracking table to unDNAT any packet that was DNATed in
+            the opposite direction.  The packet is then automatically sent to
+            to the next tables as if followed by <code>next;</code> action.
+            The next tables will see the changes in the packet caused by
+            the connection tracker.
+          </p>
+          <p>
+            <code>ct_dnat(<var>IP</var>)</code> sends the packet through the
+            DNAT zone to change the destination IP address of the packet to
+            the one provided inside the parentheses and commits the connection.
+            The packet is then automatically sent to the next tables as if
+            followed by <code>next;</code> action.  The next tables will see
+            the changes in the packet caused by the connection tracker.
+          </p>
+        </dd>
+
+        <dt><code>ct_snat;</code></dt>
+        <dt><code>ct_snat(<var>IP</var>);</code></dt>
+        <dd>
+          <p>
+            <code>ct_snat</code> sends the packet through the SNAT zone to
+            unSNAT any packet that was SNATed in the opposite direction.  If
+            the packet needs to be sent to the next tables, then it should be
+            followed by a <code>next;</code> action.  The next tables will not
+            see the changes in the packet caused by the connection tracker.
+          </p>
+          <p>
+            <code>ct_snat(<var>IP</var>)</code> sends the packet through the
+            SNAT zone to change the source IP address of the packet to
+            the one provided inside the parenthesis and commits the connection.
+            The packet is then automatically sent to the next tables as if
+            followed by <code>next;</code> action.  The next tables will see the
+            changes in the packet caused by the connection tracker.
+          </p>
+        </dd>
+
+        <dt><code>arp { <var>action</var>; </code>...<code> };</code></dt>
+        <dd>
+          <p>
+            Temporarily replaces the IPv4 packet being processed by an ARP
+            packet and executes each nested <var>action</var> on the ARP
+            packet.  Actions following the <var>arp</var> action, if any, apply
+            to the original, unmodified packet.
+          </p>
+
+          <p>
+            The ARP packet that this action operates on is initialized based on
+            the IPv4 packet being processed, as follows.  These are default
+            values that the nested actions will probably want to change:
+          </p>
+
+          <ul>
+            <li><code>eth.src</code> unchanged</li>
+            <li><code>eth.dst</code> unchanged</li>
+            <li><code>eth.type = 0x0806</code></li>
+            <li><code>arp.op = 1</code> (ARP request)</li>
+            <li><code>arp.sha</code> copied from <code>eth.src</code></li>
+            <li><code>arp.spa</code> copied from <code>ip4.src</code></li>
+            <li><code>arp.tha = 00:00:00:00:00:00</code></li>
+            <li><code>arp.tpa</code> copied from <code>ip4.dst</code></li>
+          </ul>
+
+          <p>
+            The ARP packet has the same VLAN header, if any, as the IP packet
+            it replaces.
+          </p>
+
+          <p><b>Prerequisite:</b> <code>ip4</code></p>
+        </dd>
+
+        <dt>
+          <code>na { <var>action</var>; </code>...<code> };</code>
+        </dt>
+
+        <dd>
+          <p>
+            Temporarily replaces the IPv6 packet being processed by an IPv6
+            neighbor advertisement (NA) packet and executes each nested
+            <var>action</var> on the NA packet.  Actions following the
+            <var>na</var> action, if any, apply to the original, unmodified
+            packet.
+          </p>
+
+          <p>
+            The NA packet that this action operates on is initialized based on
+            the IPv6 packet being processed, as follows. These are default
+            values that the nested actions will probably want to change:
+          </p>
+
+          <ul>
+            <li><code>eth.dst</code> exchanged with <code>eth.src</code></li>
+            <li><code>eth.type = 0x86dd</code></li>
+            <li><code>ip6.dst</code> copied from <code>ip6.src</code></li>
+            <li><code>ip6.src</code> copied from <code>nd.target</code></li>
+            <li><code>icmp6.type = 136</code> (Neighbor Advertisement)</li>
+            <li><code>nd.target</code> unchanged</li>
+            <li><code>nd.sll = 00:00:00:00:00:00</code></li>
+            <li><code>nd.tll</code> copied from <code>eth.dst</code></li>
+          </ul>
+
+          <p>
+            The ND packet has the same VLAN header, if any, as the IPv6 packet
+            it replaces.
+          </p>
+
+          <p>
+            <b>Prerequisite:</b> <code>nd</code>
+          </p>
+        </dd>
+
+        <dt><code>get_arp(<var>P</var>, <var>A</var>);</code></dt>
+
+        <dd>
+          <p>
+            <b>Parameters</b>: logical port string field <var>P</var>, 32-bit
+            IP address field <var>A</var>.
+          </p>
+
+          <p>
+            Looks up <var>A</var> in <var>P</var>'s ARP table.  If an entry is
+            found, stores its Ethernet address in <code>eth.dst</code>,
+            otherwise stores <code>00:00:00:00:00:00</code> in
+            <code>eth.dst</code>.
+          </p>
+
+          <p><b>Example:</b> <code>get_arp(outport, ip4.dst);</code></p>
+        </dd>
+
+        <dt>
+          <code>put_arp(<var>P</var>, <var>A</var>, <var>E</var>);</code>
+        </dt>
+
+        <dd>
+          <p>
+            <b>Parameters</b>: logical port string field <var>P</var>, 32-bit
+            IP address field <var>A</var>, 48-bit Ethernet address field
+            <var>E</var>.
+          </p>
+
+          <p>
+            Adds or updates the entry for IP address <var>A</var> in logical
+            port <var>P</var>'s ARP table, setting its Ethernet address to
+            <var>E</var>.
+          </p>
+
+          <p><b>Example:</b> <code>put_arp(inport, arp.spa, arp.sha);</code></p>
+        </dd>
+
+        <dt>
+          <code><var>R</var> = put_dhcp_opts(<code>offerip</code> = <var>IP</var>, <var>D1</var> = <var>V1</var>, <var>D2</var> = <var>V2</var>, ..., <var>Dn</var> = <var>Vn</var>);</code>
+        </dt>
+
+        <dd>
+          <p>
+            <b>Parameters</b>: one or more DHCP option/value pairs, the first
+            of which must set a value for the offered IP, <code>offerip</code>.
+          </p>
+
+          <p>
+            <b>Result</b>: stored to a 1-bit subfield <var>R</var>.
+          </p>
+
+          <p>
+            Valid only in the ingress pipeline.
+          </p>
+
+          <p>
+            When this action is applied to a DHCP request packet (DHCPDISCOVER
+            or DHCPREQUEST), it changes the packet into a DHCP reply (DHCPOFFER
+            or DHCPACK, respectively), replaces the options by those specified
+            as parameters, and stores 1 in <var>R</var>.
+          </p>
+
+          <p>
+            When this action is applied to a non-DHCP packet or a DHCP packet
+            that is not DHCPDISCOVER or DHCPREQUEST, it leaves the packet
+            unchanged and stores 0 in <var>R</var>.
+          </p>
+
+          <p>
+            The contents of the <ref table="DHCP_Option"/> table control the
+            DHCP option names and values that this action supports.
+          </p>
+
+          <p>
+            <b>Example:</b>
+            <code>
+              reg0[0] = put_dhcp_opts(offerip = 10.0.0.2, router = 10.0.0.1,
+              netmask = 255.255.255.0, dns_server = {8.8.8.8, 7.7.7.7});
+            </code>
+          </p>
+        </dd>
+
+        <dt><code>ct_lb;</code></dt>
+        <dt><code>ct_lb(</code><var>ip</var>[<code>:</code><var>port</var>]...<code>);</code></dt>
+        <dd>
+          <p>
+            With one or more arguments, <code>ct_lb</code> commits the packet
+            to the connection tracking table and DNATs the packet's destination
+            IP address (and port) to the IP address or addresses (and optional
+            ports) specified in the string.  If multiple comma-separated IP
+            addresses are specified, each is given equal weight for picking the
+            DNAT address.  Processing automatically moves on to the next table,
+            as if <code>next;</code> were specified, and later tables act on
+            the packet as modified by the connection tracker.  Connection
+            tracking state is scoped by the logical port, so overlapping
+            addresses may be used.
+          </p>
+          <p>
+            Without arguments, <code>ct_lb</code> sends the packet to the
+            connection tracking table to NAT the packets.  If the packet is
+            part of an established connection that was previously committed to
+            the connection tracker via <code>ct_lb(</code>...<code>)</code>, it
+            will automatically get DNATed to the same IP address as the first
+            packet in that connection.
+          </p>
+        </dd>
       </dl>
 
       <p>
       </p>
 
       <dl>
-        <dt><code><var>field1</var> = <var>field2</var>;</code></dt>
+        <dt><code>icmp4 { <var>action</var>; </code>...<code> };</code></dt>
         <dd>
-          Extends the assignment action to allow copying between fields.
-        </dd>
+          <p>
+            Temporarily replaces the IPv4 packet being processed by an ICMPv4
+            packet and executes each nested <var>action</var> on the ICMPv4
+            packet.  Actions following the <var>icmp4</var> action, if any,
+            apply to the original, unmodified packet.
+          </p>
 
-        <dt><code>learn</code></dt>
+          <p>
+            The ICMPv4 packet that this action operates on is initialized based
+            on the IPv4 packet being processed, as follows.  These are default
+            values that the nested actions will probably want to change.
+            Ethernet and IPv4 fields not listed here are not changed:
+          </p>
 
-        <dt><code>conntrack</code></dt>
+          <ul>
+            <li><code>ip.proto = 1</code> (ICMPv4)</li>
+            <li><code>ip.frag = 0</code> (not a fragment)</li>
+            <li><code>icmp4.type = 3</code> (destination unreachable)</li>
+            <li><code>icmp4.code = 1</code> (host unreachable)</li>
+          </ul>
 
-        <dt><code>dec_ttl { <var>action</var>, </code>...<code> } { <var>action</var>; </code>...<code>};</code></dt>
-        <dd>
-          decrement TTL; execute first set of actions if
-          successful, second set if TTL decrement fails
+          <p>
+            Details TBD.
+          </p>
+
+          <p><b>Prerequisite:</b> <code>ip4</code></p>
         </dd>
 
-        <dt><code>icmp_reply { <var>action</var>, </code>...<code> };</code></dt>
-        <dd>generate ICMP reply from packet, execute <var>action</var>s</dd>
+        <dt><code>tcp_reset;</code></dt>
+        <dd>
+          <p>
+            This action transforms the current TCP packet according to the
+            following pseudocode:
+          </p>
 
-        <dt><code>arp { <var>action</var>, </code>...<code> }</code></dt>
-        <dd>generate ARP from packet, execute <var>action</var>s</dd>
+          <pre>
+if (tcp.ack) {
+        tcp.seq = tcp.ack;
+} else {
+        tcp.ack = tcp.seq + length(tcp.payload);
+        tcp.seq = 0;
+}
+tcp.flags = RST;
+</pre>
+
+          <p>
+            Then, the action drops all TCP options and payload data, and
+            updates the TCP checksum.
+          </p>
+
+          <p>
+            Details TBD.
+          </p>
+
+          <p><b>Prerequisite:</b> <code>tcp</code></p>
+        </dd>
       </dl>
     </column>
 
       constructed for each supported encapsulation.
     </column>
 
-    <column name="external_ids" key="logical-switch" type='{"type": "uuid"}'>
-      Each row in <ref table="Datapath_Binding"/> is associated with some
-      logical datapath.  <code>ovn-northd</code> uses this key to store the
-      UUID of the logical datapath <ref table="Logical_Switch"
-      db="OVN_Northbound"/> row in the <ref db="OVN_Northbound"/> database.
-    </column>
+    <group title="OVN_Northbound Relationship">
+      <p>
+        Each row in <ref table="Datapath_Binding"/> is associated with some
+        logical datapath.  <code>ovn-northd</code> uses these keys to track the
+        association of a logical datapath with concepts in the <ref
+        db="OVN_Northbound"/> database.
+      </p>
+
+      <column name="external_ids" key="logical-switch" type='{"type": "uuid"}'>
+        For a logical datapath that represents a logical switch,
+        <code>ovn-northd</code> stores in this key the UUID of the
+        corresponding <ref table="Logical_Switch" db="OVN_Northbound"/> row in
+        the <ref db="OVN_Northbound"/> database.
+      </column>
+
+      <column name="external_ids" key="logical-router" type='{"type": "uuid"}'>
+        For a logical datapath that represents a logical router,
+        <code>ovn-northd</code> stores in this key the UUID of the
+        corresponding <ref table="Logical_Router" db="OVN_Northbound"/> row in
+        the <ref db="OVN_Northbound"/> database.
+      </column>
+    </group>
 
     <group title="Common Columns">
       The overall purpose of these columns is described under <code>Common
 
   <table name="Port_Binding" title="Physical-Logical Port Bindings">
     <p>
-      Each row in this table identifies the physical location of a logical
-      port.
+      Most rows in this table identify the physical location of a logical port.
+      (The exceptions are logical patch ports, which do not have any physical
+      location.)
     </p>
 
     <p>
-      For every <code>Logical_Port</code> record in <code>OVN_Northbound</code>
-      database, <code>ovn-northd</code> creates a record in this table.
-      <code>ovn-northd</code> populates and maintains every column except
-      the <code>chassis</code> column, which it leaves empty in new records.
+      For every <code>Logical_Switch_Port</code> record in
+      <code>OVN_Northbound</code> database, <code>ovn-northd</code>
+      creates a record in this table.  <code>ovn-northd</code> populates
+      and maintains every column except the <code>chassis</code> column,
+      which it leaves empty in new records.
     </p>
 
     <p>
-      <code>ovn-controller</code> populates the <code>chassis</code> column
-      for the records that identify the logical ports that are located on its
-      hypervisor, which <code>ovn-controller</code> in turn finds out by
-      monitoring the local hypervisor's Open_vSwitch database, which
-      identifies logical ports via the conventions described in
-      <code>IntegrationGuide.md</code>.
+      <code>ovn-controller</code>/<code>ovn-controller-vtep</code>
+      populates the <code>chassis</code> column for the records that
+      identify the logical ports that are located on its hypervisor/gateway,
+      which <code>ovn-controller</code>/<code>ovn-controller-vtep</code> in
+      turn finds out by monitoring the local hypervisor's Open_vSwitch
+      database, which identifies logical ports via the conventions described
+      in <code>IntegrationGuide.md</code>.  (The exceptions are for
+      <code>Port_Binding</code> records with <code>type</code> of
+      <code>gateway</code>, whose locations are identified by
+      <code>ovn-northd</code> via the <code>options:gateway-chassis</code>
+      column in this table.  <code>ovn-controller</code> is still responsible
+      to populate the <code>chassis</code> column.)
     </p>
 
     <p>
       (This is not critical because resources hosted on the chassis are equally
       unreachable regardless of whether their rows are present.)  To handle the
       case where a VM is shut down abruptly on one chassis, then brought up
-      again on a different one, <code>ovn-controller</code> must overwrite the
-      <code>chassis</code> column with new information.
+      again on a different one,
+      <code>ovn-controller</code>/<code>ovn-controller-vtep</code> must
+      overwrite the <code>chassis</code> column with new information.
     </p>
 
-    <column name="datapath">
-      The logical datapath to which the logical port belongs.
-    </column>
+    <group title="Core Features">
+      <column name="datapath">
+        The logical datapath to which the logical port belongs.
+      </column>
 
-    <column name="logical_port">
-      A logical port, taken from <ref table="Logical_Port" column="name"
-      db="OVN_Northbound"/> in the OVN_Northbound database's
-      <ref table="Logical_Port" db="OVN_Northbound"/> table.  OVN does not
-      prescribe a particular format for the logical port ID.
-    </column>
+      <column name="logical_port">
+        A logical port, taken from <ref table="Logical_Switch_Port"
+        column="name" db="OVN_Northbound"/> in the OVN_Northbound
+        database's <ref table="Logical_Switch_Port" db="OVN_Northbound"/>
+        table.  OVN does not prescribe a particular format for the
+        logical port ID.
+      </column>
 
-    <column name="type">
+      <column name="chassis">
+        The meaning of this column depends on the value of the <ref column="type"/>
+        column.  This is the meaning for each <ref column="type"/>
+
+        <dl>
+          <dt>(empty string)</dt>
+          <dd>
+            The physical location of the logical port.  To successfully identify a
+            chassis, this column must be a <ref table="Chassis"/> record.  This is
+            populated by <code>ovn-controller</code>.
+          </dd>
+
+          <dt>vtep</dt>
+          <dd>
+            The physical location of the hardware_vtep gateway.  To successfully
+            identify a chassis, this column must be a <ref table="Chassis"/> record.
+            This is populated by <code>ovn-controller-vtep</code>.
+          </dd>
+
+          <dt>localnet</dt>
+          <dd>
+            Always empty.  A localnet port is realized on every chassis that has
+            connectivity to the corresponding physical network.
+          </dd>
+
+          <dt>gateway</dt>
+          <dd>
+            The physical location of the L3 gateway.  To successfully identify a
+            chassis, this column must be a <ref table="Chassis"/> record.  This is
+            populated by <code>ovn-controller</code> based on the value of
+            the <code>options:gateway-chassis</code> column in this table.
+          </dd>
+
+          <dt>l2gateway</dt>
+          <dd>
+            The physical location of this L2 gateway.  To successfully identify a
+            chassis, this column must be a <ref table="Chassis"/> record.
+            This is populated by <code>ovn-controller</code> based on the value
+            of the <code>options:l2gateway-chassis</code> column in this table.
+          </dd>
+        </dl>
+
+      </column>
+
+      <column name="tunnel_key">
+        <p>
+          A number that represents the logical port in the key (e.g. STT key or
+          Geneve TLV) field carried within tunnel protocol packets.
+        </p>
+
+        <p>
+          The tunnel ID must be unique within the scope of a logical datapath.
+        </p>
+      </column>
+
+      <column name="mac">
+        <p>
+          The Ethernet address or addresses used as a source address on the
+          logical port, each in the form
+          <var>xx</var>:<var>xx</var>:<var>xx</var>:<var>xx</var>:<var>xx</var>:<var>xx</var>.
+          The string <code>unknown</code> is also allowed to indicate that the
+          logical port has an unknown set of (additional) source addresses.
+        </p>
+
+        <p>
+          A VM interface would ordinarily have a single Ethernet address.  A
+          gateway port might initially only have <code>unknown</code>, and then
+          add MAC addresses to the set as it learns new source addresses.
+        </p>
+      </column>
+
+      <column name="type">
+        <p>
+          A type for this logical port.  Logical ports can be used to model other
+          types of connectivity into an OVN logical switch.  The following types
+          are defined:
+        </p>
+
+        <dl>
+          <dt>(empty string)</dt>
+          <dd>VM (or VIF) interface.</dd>
+
+          <dt><code>patch</code></dt>
+          <dd>
+            One of a pair of logical ports that act as if connected by a patch
+            cable.  Useful for connecting two logical datapaths, e.g. to connect
+            a logical router to a logical switch or to another logical router.
+          </dd>
+
+          <dt><code>gateway</code></dt>
+          <dd>
+            One of a pair of logical ports that act as if connected by a patch
+            cable across multiple chassis.  Useful for connecting a logical
+            switch with a Gateway router (which is only resident on a
+            particular chassis).
+          </dd>
+
+          <dt><code>localnet</code></dt>
+          <dd>
+            A connection to a locally accessible network from each
+            <code>ovn-controller</code> instance.  A logical switch can only
+            have a single <code>localnet</code> port attached.  This is used
+            to model direct connectivity to an existing network.
+          </dd>
+
+          <dt><code>l2gateway</code></dt>
+          <dd>
+            An L2 connection to a physical network.  The chassis this
+            <ref table="Port_Binding"/> is bound to will serve as
+            an L2 gateway to the network named by
+            <ref column="options" table="Port_Binding"/>:<code>network_name</code>.
+          </dd>
+
+          <dt><code>vtep</code></dt>
+          <dd>
+            A port to a logical switch on a VTEP gateway chassis.  In order to
+            get this port correctly recognized by the OVN controller, the <ref
+            column="options"
+            table="Port_Binding"/>:<code>vtep-physical-switch</code> and <ref
+            column="options"
+            table="Port_Binding"/>:<code>vtep-logical-switch</code> must also
+            be defined.
+          </dd>
+        </dl>
+      </column>
+    </group>
+
+    <group title="Patch Options">
       <p>
-      A type for this logical port.  Logical ports can be used to model
-      other types of connectivity into an OVN logical switch.  Leaving this column
-      blank maintains the default logical port behavior.
+        These options apply to logical ports with <ref column="type"/> of
+        <code>patch</code>.
       </p>
 
+      <column name="options" key="peer">
+        The <ref column="logical_port"/> in the <ref table="Port_Binding"/>
+        record for the other side of the patch.  The named <ref
+        column="logical_port"/> must specify this <ref column="logical_port"/>
+        in its own <code>peer</code> option.  That is, the two patch logical
+        ports must have reversed <ref column="logical_port"/> and
+        <code>peer</code> values.
+      </column>
+    </group>
+
+    <group title="L3 Gateway Options">
       <p>
-      There are no other logical port types implemented yet.
+        These options apply to logical ports with <ref column="type"/> of
+        <code>gateway</code>.
       </p>
-    </column>
 
-    <column name="options">
-        This column provides key/value settings specific to the logical port
-        <ref column="type"/>.
-    </column>
+      <column name="options" key="peer">
+        The <ref column="logical_port"/> in the <ref table="Port_Binding"/>
+        record for the other side of the 'gateway' port.  The named <ref
+        column="logical_port"/> must specify this <ref column="logical_port"/>
+        in its own <code>peer</code> option.  That is, the two 'gateway'
+        logical ports must have reversed <ref column="logical_port"/> and
+        <code>peer</code> values.
+      </column>
 
-    <column name="tunnel_key">
+      <column name="options" key="gateway-chassis">
+        The <code>chassis</code> in which the port resides.
+      </column>
+    </group>
+
+    <group title="Localnet Options">
+      <p>
+        These options apply to logical ports with <ref column="type"/> of
+        <code>localnet</code>.
+      </p>
+
+      <column name="options" key="network_name">
+        Required.  <code>ovn-controller</code> uses the configuration entry
+        <code>ovn-bridge-mappings</code> to determine how to connect to this
+        network.  <code>ovn-bridge-mappings</code> is a list of network names
+        mapped to a local OVS bridge that provides access to that network.  An
+        example of configuring <code>ovn-bridge-mappings</code> would be:
+
+        <pre>$ ovs-vsctl set open . external-ids:ovn-bridge-mappings=physnet1:br-eth0,physnet2:br-eth1</pre>
+
+        <p>
+          When a logical switch has a <code>localnet</code> port attached,
+          every chassis that may have a local vif attached to that logical
+          switch must have a bridge mapping configured to reach that
+          <code>localnet</code>.  Traffic that arrives on a
+          <code>localnet</code> port is never forwarded over a tunnel to
+          another chassis.
+        </p>
+      </column>
+
+      <column name="tag">
+        If set, indicates that the port represents a connection to a specific
+        VLAN on a locally accessible network. The VLAN ID is used to match
+        incoming traffic and is also added to outgoing traffic.
+      </column>
+    </group>
+
+    <group title="L2 Gateway Options">
+      <p>
+        These options apply to logical ports with <ref column="type"/> of
+        <code>l2gateway</code>.
+      </p>
+
+      <column name="options" key="network_name">
+        Required.  <code>ovn-controller</code> uses the configuration entry
+        <code>ovn-bridge-mappings</code> to determine how to connect to this
+        network.  <code>ovn-bridge-mappings</code> is a list of network names
+        mapped to a local OVS bridge that provides access to that network.  An
+        example of configuring <code>ovn-bridge-mappings</code> would be:
+
+        <pre>$ ovs-vsctl set open . external-ids:ovn-bridge-mappings=physnet1:br-eth0,physnet2:br-eth1</pre>
+
+        <p>
+          When a logical switch has a <code>l2gateway</code> port attached,
+          the chassis that the <code>l2gateway</code> port is bound to
+          must have a bridge mapping configured to reach the network
+          identified by <code>network_name</code>.
+        </p>
+      </column>
+
+      <column name="options" key="l2gateway-chassis">
+        Required. The <code>chassis</code> in which the port resides.
+      </column>
+
+      <column name="tag">
+        If set, indicates that the gateway is connected to a specific
+        VLAN on the physical network. The VLAN ID is used to match
+        incoming traffic and is also added to outgoing traffic.
+      </column>
+    </group>
+
+    <group title="VTEP Options">
+      <p>
+        These options apply to logical ports with <ref column="type"/> of
+        <code>vtep</code>.
+      </p>
+
+      <column name="options" key="vtep-physical-switch">
+        Required. The name of the VTEP gateway.
+      </column>
+
+      <column name="options" key="vtep-logical-switch">
+        Required.  A logical switch name connected by the VTEP gateway.  Must
+        be set when <ref column="type"/> is <code>vtep</code>.
+      </column>
+    </group>
+
+    <group title="VMI (or VIF) Options">
       <p>
-        A number that represents the logical port in the key (e.g. STT key or
-        Geneve TLV) field carried within tunnel protocol packets.
+        These options apply to logical ports with <ref column="type"/> having
+        (empty string)
       </p>
 
+      <column name="options" key="policing_rate">
+        If set, indicates the maximum rate for data sent from this interface,
+        in kbps. Data exceeding this rate is dropped.
+      </column>
+
+      <column name="options" key="policing_burst">
+        If set, indicates the maximum burst size for data sent from this
+        interface, in kb.
+      </column>
+    </group>
+
+    <group title="Nested Containers">
       <p>
-        The tunnel ID must be unique within the scope of a logical datapath.
+        These columns support containers nested within a VM.  Specifically,
+        they are used when <ref column="type"/> is empty and <ref
+        column="logical_port"/> identifies the interface of a container spawned
+        inside a VM.  They are empty for containers or VMs that run directly on
+        a hypervisor.
       </p>
+
+      <column name="parent_port">
+        This is taken from
+        <ref table="Logical_Switch_Port" column="parent_name"
+        db="OVN_Northbound"/> in the OVN_Northbound database's
+        <ref table="Logical_Switch_Port" db="OVN_Northbound"/> table.
+      </column>
+
+      <column name="tag">
+        <p>
+          Identifies the VLAN tag in the network traffic associated with that
+          container's network interface.
+        </p>
+
+        <p>
+          This column is used for a different purpose when <ref column="type"/>
+          is <code>localnet</code> (see <code>Localnet Options</code>, above)
+          or <code>l2gateway</code> (see <code>L2 Gateway Options</code>, above).
+        </p>
+      </column>
+    </group>
+  </table>
+
+  <table name="MAC_Binding" title="IP to MAC bindings">
+    <p>
+      Each row in this table specifies a binding from an IP address to an
+      Ethernet address that has been discovered through ARP (for IPv4) or
+      neighbor discovery (for IPv6).  This table is primarily used to discover
+      bindings on physical networks, because IP-to-MAC bindings for virtual
+      machines are usually populated statically into the <ref
+      table="Port_Binding"/> table.
+    </p>
+
+    <p>
+      This table expresses a functional relationship: <ref
+      table="MAC_Binding"/>(<ref column="logical_port"/>, <ref column="ip"/>) =
+      <ref column="mac"/>.
+    </p>
+
+    <p>
+      In outline, the lifetime of a logical router's MAC binding looks like
+      this:
+    </p>
+
+    <ol>
+      <li>
+        On hypervisor 1, a logical router determines that a packet should be
+        forwarded to IP address <var>A</var> on one of its router ports.  It
+        uses its logical flow table to determine that <var>A</var> lacks a
+        static IP-to-MAC binding and the <code>get_arp</code> action to
+        determine that it lacks a dynamic IP-to-MAC binding.
+      </li>
+
+      <li>
+        Using an OVN logical <code>arp</code> action, the logical router
+        generates and sends a broadcast ARP request to the router port.  It
+        drops the IP packet.
+      </li>
+
+      <li>
+        The logical switch attached to the router port delivers the ARP request
+        to all of its ports.  (It might make sense to deliver it only to ports
+        that have no static IP-to-MAC bindings, but this could also be
+        surprising behavior.)
+      </li>
+
+      <li>
+        A host or VM on hypervisor 2 (which might be the same as hypervisor 1)
+        attached to the logical switch owns the IP address in question.  It
+        composes an ARP reply and unicasts it to the logical router port's
+        Ethernet address.
+      </li>
+
+      <li>
+        The logical switch delivers the ARP reply to the logical router port.
+      </li>
+
+      <li>
+        The logical router flow table executes a <code>put_arp</code> action.
+        To record the IP-to-MAC binding, <code>ovn-controller</code> adds a row
+        to the <ref table="MAC_Binding"/> table.
+      </li>
+
+      <li>
+        On hypervisor 1, <code>ovn-controller</code> receives the updated <ref
+        table="MAC_Binding"/> table from the OVN southbound database.  The next
+        packet destined to <var>A</var> through the logical router is sent
+        directly to the bound Ethernet address.
+      </li>
+    </ol>
+
+    <column name="logical_port">
+      The logical port on which the binding was discovered.
     </column>
 
-    <column name="parent_port">
-      For containers created inside a VM, this is taken from
-      <ref table="Logical_Port" column="parent_name" db="OVN_Northbound"/>
-      in the OVN_Northbound database's <ref table="Logical_Port"
-      db="OVN_Northbound"/> table.  It is left empty if
-      <ref column="logical_port"/> belongs to a VM or a container created
-      in the hypervisor.
+    <column name="ip">
+      The bound IP address.
     </column>
 
-    <column name="tag">
-      When <ref column="logical_port"/> identifies the interface of a container
-      spawned inside a VM, this column identifies the VLAN tag in
-      the network traffic associated with that container's network interface.
-      It is left empty if <ref column="logical_port"/> belongs to a VM or a
-      container created in the hypervisor.
+    <column name="mac">
+      The Ethernet address to which the IP is bound.
     </column>
+    <column name="datapath">
+      The logical datapath to which the logical port belongs.
+    </column>
+  </table>
+
+  <table name="DHCP_Options" title="DHCP Options supported by native OVN DHCP">
+    <p>
+      Each row in this table stores the DHCP Options supported by native OVN
+      DHCP. <code>ovn-northd</code> populates this table with the supported
+      DHCP options. <code>ovn-controller</code> looks up this table to get the
+      DHCP codes of the DHCP options defined in the "put_dhcp_opts" action.
+      Please refer to the RFC 2132 <code>"https://tools.ietf.org/html/rfc2132"</code>
+      for the possible list of DHCP options that can be defined here.
+    </p>
 
-    <column name="chassis">
-      The physical location of the logical port.  To successfully identify a
-      chassis, this column must be a <ref table="Chassis"/> record.  This is
-      populated by <code>ovn-controller</code>.
+    <column name="name">
+      <p>
+        Name of the DHCP option.
+      </p>
+
+      <p>
+        Example. name="router"
+      </p>
     </column>
 
-    <column name="mac">
+    <column name="code">
       <p>
-        The Ethernet address or addresses used as a source address on the
-        logical port, each in the form
-        <var>xx</var>:<var>xx</var>:<var>xx</var>:<var>xx</var>:<var>xx</var>:<var>xx</var>.
-        The string <code>unknown</code> is also allowed to indicate that the
-        logical port has an unknown set of (additional) source addresses.
+        DHCP option code for the DHCP option as defined in the RFC 2132.
       </p>
 
       <p>
-        A VM interface would ordinarily have a single Ethernet address.  A
-        gateway port might initially only have <code>unknown</code>, and then
-        add MAC addresses to the set as it learns new source addresses.
+        Example. code=3
       </p>
     </column>
+
+    <column name="type">
+      <p>
+        Data type of the DHCP option code.
+      </p>
+
+      <dl>
+        <dt><code>value: bool</code></dt>
+        <dd>
+          <p>
+            This indicates that the value of the DHCP option is a bool.
+          </p>
+
+          <p>
+            Example. "name=ip_forward_enable", "code=19", "type=bool".
+          </p>
+
+          <p>
+            put_dhcp_opts(..., ip_forward_enable = 1,...)
+          </p>
+        </dd>
+
+        <dt><code>value: uint8</code></dt>
+        <dd>
+          <p>
+            This indicates that the value of the DHCP option is an unsigned
+            int8 (8 bits)
+          </p>
+
+          <p>
+            Example. "name=default_ttl", "code=23", "type=uint8".
+          </p>
+
+          <p>
+            put_dhcp_opts(..., default_ttl = 50,...)
+          </p>
+        </dd>
+
+        <dt><code>value: uint16</code></dt>
+        <dd>
+          <p>
+            This indicates that the value of the DHCP option is an unsigned
+            int16 (16 bits).
+          </p>
+
+          <p>
+            Example. "name=mtu", "code=26", "type=uint16".
+          </p>
+
+          <p>
+            put_dhcp_opts(..., mtu = 1450,...)
+          </p>
+        </dd>
+
+        <dt><code>value: uint32</code></dt>
+        <dd>
+          <p>
+            This indicates that the value of the DHCP option is an unsigned
+            int32 (32 bits).
+          </p>
+
+          <p>
+            Example. "name=lease_time", "code=51", "type=uint32".
+          </p>
+
+          <p>
+            put_dhcp_opts(..., lease_time = 86400,...)
+          </p>
+        </dd>
+
+        <dt><code>value: ipv4</code></dt>
+        <dd>
+          <p>
+            This indicates that the value of the DHCP option is an IPv4
+            address or addresses.
+          </p>
+
+          <p>
+            Example. "name=router", "code=3", "type=ipv4".
+          </p>
+
+          <p>
+            put_dhcp_opts(..., router = 10.0.0.1,...)
+          </p>
+
+          <p>
+            Example. "name=dns_server", "code=6", "type=ipv4".
+          </p>
+
+          <p>
+            put_dhcp_opts(..., dns_server = {8.8.8.8 7.7.7.7},...)
+          </p>
+        </dd>
+
+        <dt><code>value: static_routes</code></dt>
+        <dd>
+          <p>
+            This indicates that the value of the DHCP option contains a pair of
+            IPv4 route and next hop addresses.
+          </p>
+
+          <p>
+            Example. "name=classless_static_route", "code=121", "type=static_routes".
+          </p>
+
+          <p>
+            put_dhcp_opts(..., classless_static_route = {30.0.0.0/24,10.0.0.4,0.0.0.0/0,10.0.0.1}...)
+          </p>
+        </dd>
+
+        <dt><code>value: str</code></dt>
+        <dd>
+          <p>
+            This indicates that the value of the DHCP option is a string.
+          </p>
+
+          <p>
+            Example. "name=host_name", "code=12", "type=str".
+          </p>
+        </dd>
+      </dl>
+    </column>
   </table>
 </database>