Copy advanced connectivity from Trellis-docs

Change-Id: I428eb80a4312570cc548a9804b3c0b17930a1cf7
diff --git a/advanced/connectivity.rst b/advanced/connectivity.rst
index 1221313..c9d1f01 100644
--- a/advanced/connectivity.rst
+++ b/advanced/connectivity.rst
@@ -1,2 +1,13 @@
 Advanced Connectivity
 =====================
+
+SD-Fabric supports DHCP relay, dual homing and external connectivity.
+
+.. toctree::
+   :maxdepth: 2
+   :hidden:
+   :glob:
+
+   connectivity/dhcp-relay
+   connectivity/external-connectivity
+   connectivity/dual-homing
diff --git a/advanced/connectivity/dhcp-relay.rst b/advanced/connectivity/dhcp-relay.rst
new file mode 100644
index 0000000..ac24ff1
--- /dev/null
+++ b/advanced/connectivity/dhcp-relay.rst
@@ -0,0 +1,648 @@
+DHCP Relay
+==========
+
+.. tip::
+    We strongly recommend you to setup DHCP relay and configure the hosts to
+    **obtain address via DHCP**.
+
+    See `Alternative: Configure static IP`_ if you want to statically configure
+    IP address on each host.
+
+Overview
+--------
+The DHCP relay app used in SD-Fabric is an L3 relay.
+
+That is, it support relaying DHCP packets from/to a server that's not in the
+same subnet of the client.
+
+Here's a list of features supported:
+
+- DHCPv4 and DHCPv6
+
+- DHCP server directly attached to fabric leaves, or indirectly connected via
+  upstream router
+
+- DHCP client directly attached to fabric leaves, or indirectly connected via
+  `LDRA (Light-weight DHCP Relay Agent) <https://tools.ietf.org/html/rfc6221>`_
+
+- Multiple DHCP servers for HA
+
+.. note::
+    Please pay attention to the definition of **direct/indirect server/client**.
+    You will find them many times later in this section.
+
+Configure DHCP Relay
+--------------------
+
+Server directly connected to fabric
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. image:: ../../images/config-dhcp.png
+
+In this case, the configuration involves first configuring the switch interface
+with the VLAN/subnet the DHCP service is part of.
+
+For example, if I have a switch ``of:205`` with a DHCP server on port 24 on
+VLAN 20, the port config looks like:
+
+.. code-block:: json
+
+    {
+      "ports": {
+        "of:0000000000000205/24" : {
+          "interfaces" : [ {
+            "name" : "dhcp-server-intf",
+            "ips" : [ "10.0.2.254/24", "2001:db8:1::254/64" ],
+            "vlan-tagged" : [ 20 ]
+          } ]
+        }
+      }
+    }
+
+A second part of the configuration for the DHCP relay app requires a json
+configuration under the key apps:
+
+.. code-block:: json
+
+    {
+      "apps" : {
+        "org.onosproject.dhcp-relay" : {
+          "default" : [
+            {
+              "dhcpServerConnectPoint": "of:0000000000000205/24",
+              "serverIps": ["10.0.2.253", "2001:db8:2::2"]
+            }
+          ]
+        }
+      }
+    }
+
+Note that the ``dhcprelay`` app is configured with location of the DHCP server (the
+switch port to which it is connected to the fabric).
+
+It is also configured with the DHCP server IP, but it is no longer necessary to
+configure the MAC address of the server.
+
+ONOS will automatically learn the MAC and VLAN corresponding to the ``serverIP``.
+
+
+Server reachable via external router
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+In this case, it is actually the external router that is directly connected to
+the fabric.
+
+This external router is already configured in the ports section of
+network-config (for vRouter functionality).
+
+For example, if the external router is connected to switch ``of:205`` on port 1
+
+.. code-block:: json
+
+    {
+      "ports": {
+        "of:0000000000000205/1" : {
+          "interfaces" : [ {
+            "ips" : [ "192.168.101.2/30", "2000::c0a8:6402/120" ],
+            "mac" : "a2:9b:32:9d:7f:b3",
+            "name" : "internet-router"
+          } ]
+        }
+      }
+    }
+
+As before the ``ips`` and ``mac`` configured on port 1, actually correspond to
+the addresses configured in Quagga.
+
+The app config in this case, includes an additional field necessary to inform
+the ``dhcprelay`` app of the ``gatewayIP`` through which the DHCP server can be
+reached.
+
+.. code-block:: json
+
+    {
+      "apps" : {
+        "org.onosproject.dhcp-relay" : {
+          "default" : [
+            {
+              "dhcpServerConnectPoint": "of:0000000000000205/1",
+              "serverIps": ["10.0.2.253", "2001:db8:2::2"],
+              "gatewayIps": ["192.168.101.1", "1000::100:1"]
+            }
+          ]
+        }
+      }
+    }
+
+.. note::
+    Note that the ``dhcpserverConnectPoint`` should now be the switch port to
+    which the external router is connected to the fabric.
+
+Setup DHCP server
+-----------------
+
+Install DHCP server
+^^^^^^^^^^^^^^^^^^^
+
+Modern DHCP servers should support relayed DHCP request.
+However, the way to configure them are probably different case to case.
+Here we use **isc-dhcp-server** on Ubuntu as an example.
+To install the DHCP server, simply run:
+
+.. code-block:: console
+
+    $ sudo apt-get install isc-dhcp-server
+
+
+Configure DHCP Server
+^^^^^^^^^^^^^^^^^^^^^
+
+Two configuration files are required by DHCP server.
+
+First, we need to specify which network interface the DHCP server should listen on.
+To do that, we need to modify ``/etc/default/isc-dhcp-server`` and change the following line.
+
+.. code-block:: text
+
+    INTERFACES="eth1"
+
+Next, we need to specify the subnet we want to lease.
+To do that, we need to modify ``/etc/dhcp/dhcpd.conf`` and add the following lines.
+
+Note that the subnet of ``eth1`` needs to be included.
+
+Otherwise, the DHCP server will not listen to the interface even though we have
+specified that in ``/etc/default/isc-dhcp-server``.
+
+.. code-block:: text
+
+    subnet 10.0.1.0 netmask 255.255.255.0 {
+      range 10.0.1.1 10.0.1.240;
+      option routers 10.0.1.254;
+    }
+
+    # A subnet that matches the interface IP address is required by isc-dhcp-server
+    subnet 10.0.2.0 netmask 255.255.255.0 {
+      range 10.0.2.1 10.0.2.240;
+      option routers 10.0.2.254;
+    }
+
+It's similar to configure DHCPv6.
+
+.. code-block:: text
+
+    subnet6 2001:db8:1::/64 {
+            # Range for clients
+            range6 2001:db8:1::129 2001:db8:1::250;
+
+            # Range for clients requesting a temporary address
+            range6 2001:db8:1::/64 temporary;
+    }
+    # A subnet that matches the interface IP address is required by isc-dhcp-server
+    subnet6 2001:db8:2::/64 {
+            # Range for clients
+            range6 2001:db8:2::129 2001:db8:2::254;
+
+            # Range for clients requesting a temporary address
+            range6 2001:db8:2::/64 temporary;
+
+            # Prefix range for delegation to sub-routers
+            prefix6 2001:db8:1:: 2001:db8:10:: /56;
+
+    }
+
+Finally, restart the DHCP server.
+
+.. code-block:: console
+
+    $ sudo service isc-dhcp-server restart
+
+Testing
+-------
+
+The host should be able to obtain an IP address from the pool we specified.
+Try to run ``dhclient`` and see if the host can get an IP address.
+
+.. code-block:: console
+
+    sudo dhclient eth1
+
+It's similar to test DHCPv6
+
+.. code-block:: console
+
+    sudo dhclient -6 -N eth1       # for obtaining ip address
+    sudo dhclient -6 -P -N eth1   # for obtaining ip address and prefix together
+
+    sudo dhclient -6 -r eth1       # for releasing ip address
+    sudo dhclient -6 -P -r eth1   # for releasing prefix
+
+
+If something goes wrong, check ``/var/log/syslog`` for DHCP server log and run
+``tcpdump`` on DHCP server to see if the DHCP packets from the host reach the
+server correctly.
+
+Additional Features
+-------------------
+
+DHCP Relay store
+^^^^^^^^^^^^^^^^
+
+DHCP relay application stores information from DHCP packet which processed by
+the app, administrator can use CLI command ``dhcp-relay`` to query these
+information.
+
+The store provides these functionality:
+
+- Latest state of DHCP client (e.g. client location, last seen time, DHCP
+  type...), for debugging purpose
+
+- For direct host, ONOS can find location and VLAN from relay agent option,
+  however, for indirect host, ONOS need to query last state from the store to
+  find correct destination.
+
+
+DHCPv6 Relay counter
+^^^^^^^^^^^^^^^^^^^^
+There are two DHCPv6 packet counters which are Host basis counters and Global counters.
+
+Host basis counters count and record DHCPv6 packets received on this host.
+
+It can be displayed by ``dhcp-relay counter``. These counters can be reset by
+typing ``dhcp-relay counter reset``.
+
+.. code-block:: console
+
+    onos> dhcp-relay counter
+    DHCP Relay Counters :
+    Counters for id=00:AA:BB:00:00:01/None, locations=[of:0000000000000204/3]
+    SOLICIT                         ............................  4    packets
+    REQUEST                         ............................  4    packets
+    ADVERTISE                       ............................  4    packets
+    RENEW                           ............................  1000 packets
+    REPLY                           ............................  1004 packets
+    Counters for id=00:AA:00:00:00:01/None, locations=[of:0000000000000205/3][D]
+    SOLICIT                         ............................  2    packets
+    REQUEST                         ............................  2    packets
+    ADVERTISE                       ............................  2    packets
+    RENEW                           ............................  500  packets
+    CONFIRM                         ............................  2    packets
+    REPLY                           ............................  500  packets
+
+    onos> dhcp-relay counter reset
+
+Global counters counts and records all DHCPv6 packets received in ONOS.
+
+It can be displayed by ``dhcp-relay-agg-counters``. These counters can be reset
+by typing ``dhcp-relay-agg-counters reset``.
+
+.. code-block:: console
+
+    onos> dhcp-relay-agg-counters
+    DHCP Relay Aggregate Counters :
+    SOLICIT                         ............................  12   packets
+    REQUEST                         ............................  12   packets
+    ADVERTISE                       ............................  12   packets
+    REBIND                          ............................  4    packets
+    RENEW                           ............................  3026 packets
+    CONFIRM                         ............................  4    packets
+    REPLY                           ............................  3044 packets
+
+    onos> dhcp-relay-agg-counters reset
+
+
+Indirect client support
+^^^^^^^^^^^^^^^^^^^^^^^
+DHCP relay can support hosts which do not directly connect to SD-Fabric.
+
+These hosts usually connected to another LDRA, the LDRA will forward DHCP
+packet to/from SD-Fabric.
+
+For **DHCPv4**, packets from the LDRA includes a valid DHCP relay agent option
+(option 82).
+
+DHCP Relay application checks relay agent option and determine the DHCP packet
+comes from direct or indirect host.
+
+.. image:: ../../images/config-dhcp-indirect.jpg
+
+ONOS uses circuit id option in relay agent option with specific format if DHCP
+packet comes without relay agent option, the format of circuit will be:
+``ConnectPoint:VlanId``
+
+For example, the DHCP request/discover packet comes from
+``of:000000000000001/1`` with ``VLAN 100``, the circuit ONOS put will be
+``of:000000000000001/1:100`` and send DHCP packet to DHCP server.
+
+Indirect host won't put into host store. DHCP relay app will put IP address of
+indirect host to the route store, and use IP address of relay agent as next
+hop.
+
+**DHCPv6** clients will be handled similar to DHCPv4.
+
+One major difference is that DHCPv6 supports ``RELAY-FORWARD`` message type and
+``InterfaceId`` option natively, so we utilize those fields to encode
+information.
+
+Overwrite relay agent IP
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+The DHCP relay can overwrite the relay agent address (``giaddr`` in **DHCPv4**,
+``link-addr`` in **DHCPv6**) in DHCP message for different device.
+
+If ``relayAgentIps`` is configured, the app will overwrite ``giaddr`` or
+``link-addr`` before it forward the DHCP message to the server.
+
+Otherwise, it will retain the original relay agent IP.
+
+An example configuration is shown below:
+
+.. code-block:: json
+
+    {
+      "apps" : {
+        "org.onosproject.dhcprelay" : {
+          "default": [{
+            "dhcpServerConnectPoint": "of:0000000000000002/2",
+            "serverIps": ["172.168.10.2", "2000::200:1"],
+            "gatewayIps": ["192.168.10.254", "1000::100:1"],
+            "relayAgentIps": {
+              "of:0000000000000001": {
+                "ipv4": "10.0.0.10",
+                "ipv6": "2000::10"
+              },
+              "of:0000000000000002": {
+                "ipv4": "10.0.1.10",
+                "ipv6": "2000::1:10"
+              }
+            }
+           }]
+        }
+      }
+    }
+
+
+Configure multiple servers
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+DHCP server HA can be achieved by specifying additional server configuration
+objects.
+
+Client initiated packets like ``SOLICIT`` or ``REBIND`` shall be replicated and
+sent to all server objects.
+
+Below is an example of multiple server configuration:
+
+.. code-block:: json
+
+    {
+       "apps" : {
+           "org.onosproject.dhcprelay" : {
+               "default": [
+                   {
+                       "dhcpServerConnectPoint": "of:0000000000000205/5",
+                       "serverIps": ["10.0.3.252", "2002:4::253"],
+                       "gatewayIps": ["10.0.3.100","2001:3::100"],
+                       "relayAgentIps": {
+                           "of:0000000000000204": {
+                               "ipv4": "10.0.2.254",
+                               "ipv6": "2001:2::254"
+                           }
+                       }
+                   },
+                   {
+                       "dhcpServerConnectPoint": "of:0000000000000206/3",
+                       "serverIps": ["2002:5::253"],
+                       "gatewayIps": ["2001:4::100"],
+                       "relayAgentIps": {
+                           "of:0000000000000204": {
+                               "ipv4": "10.0.2.254",
+                               "ipv6": "2001:2::254"
+                           }
+                       }
+                    }
+                ],
+                "indirect": [
+                    {
+                        "dhcpServerConnectPoint": "of:0000000000000205/5",
+                        "serverIps": ["10.0.3.252", "2002:4::253"],
+                        "gatewayIps": ["10.0.3.100", "2001:3::100"],
+                        "relayAgentIps": {
+                            "of:0000000000000204": {
+                                "ipv4": "10.0.2.254",
+                                "ipv6": "2001:2::254"
+                            }
+                        }
+                    },
+                    {
+                        "dhcpServerConnectPoint": "of:0000000000000205/5",
+                        "serverIps": ["10.0.3.252", "2002:5::253"],
+                        "gatewayIps": ["10.0.3.100", "2001:3::100"],
+                        "relayAgentIps": {
+                            "of:0000000000000204": {
+                                "ipv4": "10.0.2.254",
+                                "ipv6": "2001:2::254"
+                            }
+                        }
+                    },
+                    {
+                        "dhcpServerConnectPoint": "of:0000000000000206/3",
+                        "serverIps": ["2002:5::253"],
+                        "gatewayIps": ["2001:4::100"],
+                        "relayAgentIps": {
+                            "of:0000000000000204": {
+                                "ipv4": "10.0.2.254",
+                                "ipv6": "2001:2::254"
+                            }
+                        }
+                    },
+                    {
+                        "dhcpServerConnectPoint": "of:0000000000000206/3",
+                        "serverIps": ["2002:4::253"],
+                        "gatewayIps": ["2001:4::100"],
+                        "relayAgentIps": {
+                            "of:0000000000000204": {
+                                "ipv4": "10.0.2.254",
+                                "ipv6": "2001:2::254"
+                            }
+                        }
+                    }
+                ]
+            }
+        }
+    }
+
+- ``dhcpServerConnectPoint``: represent the location of DHCP server
+
+- ``serverIps``: IP address of the DHCP server, contains at least one IP address of DHCP server.
+  IP address can be IPv4 or IPv6 for different version of DHCP.
+  Will use first address if multiple IPv4 or IPv6 address configured.
+
+- ``gatewayIps``: Optional. Should be configured if the DHCP server is not
+  directly connected to the SD-Fabric. It tells which gateway we need to
+  send to reach the server.
+
+.. note::
+    - If ``indirect`` server configuration is not configured, the app will use
+      ``default`` configuration for all cases.
+
+
+Ignoring DHCP relay on a particular VLAN
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+In some cases, it may be necessary to avoid punting DHCP packets to the
+controller, and letting them be forwarded normally through the data plane.
+
+In such cases, the DHCP relay application can be configured to avoid punting
+DHCP packets on a particular VLAN on a particular switch.
+
+.. code-block:: json
+
+    {
+      "apps" : {
+        "org.onosproject.dhcprelay" : {
+          "ignoreDhcp" : [
+            { "deviceId": "of:0000000000000205", "vlan":24 },
+            { "deviceId": "of:0000000000000206", "vlan":24 }
+          ]
+        }
+      }
+    }
+
+In the example shown above, DHCP packets on VLAAN 24 are not punted to the
+controller from switches of:205 and of:206
+
+DHCPv6 Prefix Delegation (PD) Pushing
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. note::
+    This feature requires both ``dhcprelay`` and ``fpm`` apps to be activated
+
+PD pushing allows IPv6 prefixes from DhcpRelay to be sent over the FPM
+connection to Quagga where they will be configured as a static route.
+
+Prior to PD Pushing, the FPM connection was only used by Quagga in one
+direction to push routes to FPM.  PD pushing is disabled by default in DHCP
+Relay and FPM.
+
+To enable in DHCP relay:
+
+.. code-block:: console
+
+    onos> cfg set org.onosproject.dhcprelay.DhcpRelayManager DhcpFpmEnabled true
+
+To display PD's stored in DHCP relay, execute the following CLI command:
+
+.. code-block:: console
+
+    onos> dhcp-fpm-routes
+
+When PD pushing is enabled in FPM, by default the next-hop to be used for all
+prefixes pushed to Quagga will be retrieved from the first interface with
+``RUR`` in the name in ONOS.
+
+Next-hop may also be configured using FPM component config. This will override
+a ``RUR`` interface if present.
+
+If there is no interface with ``RUR`` in the name and the next-hop is not
+configured, no prefixes can be pushed to Quagga even if PD pushing is enabled.
+For DhcpRelay, only the IPv6 next-hop is needed.
+
+To enable in FPM:
+
+.. code-block:: console
+
+    onos> cfg set org.onosproject.routing.fpm.FpmManager pdPushNextHopIPv4 124.200.1.60
+    onos> cfg set org.onosproject.routing.fpm.FpmManager pdPushNextHopIPv6 2001:a08::2
+    onos> cfg set org.onosproject.routing.fpm.FpmManager pdPushEnabled true
+
+
+To verify that PD pushing is enabled:
+
+.. code-block:: console
+
+    onos> fpm-connections
+    PD Pushing is enabled.
+    peer 124.200.3.42:48640 connected to 127.0.0.1 since 2m23s ago * (2 routes locally)
+
+
+Prefixes pushed to Quagga can be displayed in ``vtysh`` using ``show ip route`` and ``show ipv6 route``.
+If the output is not as expected, check the Quagga log to see if it was received from FPM.
+
+.. note::
+    Quagga requires a patch to be able to receive Netlink Messages from FPM.
+
+Clean up expired address and PD prefix
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+DHCPv6 relay cleans up stale IP address and pd prefix based on timer whose
+default interval is 24 hours (24 * 3600 secs = 86400 secs).
+
+If the preferred life time of ip address or pd prefix exceeds 1/2 of poll
+interval, they will be removed from ONOS.
+
+The poll interval can be modified by ``cfg set
+org.onosproject.dhcprelay.DhcpRelayManager dhcpPollInterval <newVal>``
+
+.. code-block:: console
+
+    onos> cfg get  org.onosproject.dhcprelay.DhcpRelayManager
+    org.onosproject.dhcprelay.DhcpRelayManager
+        name=dhcpPollInterval, type=integer, value=86400, defaultValue=86400, description=dhcp relay poll interval
+
+    onos> cfg set org.onosproject.dhcprelay.DhcpRelayManager dhcpPollInterval 60
+
+    onos> cfg get  org.onosproject.dhcprelay.DhcpRelayManager
+    org.onosproject.dhcprelay.DhcpRelayManager
+        name=dhcpPollInterval, type=integer, value=60, defaultValue=86400, description=dhcp relay poll interval
+
+
+Alternative: Configure static IP
+--------------------------------
+
+Although we strongly recommend to use `DHCP Relay`_ for IP assignment, it is
+also possible to statically configure the IP address and route on the host.
+
+1. **Configure the IP address and subnet mask**
+
+   Make sure the IP address and the subnet mask on the fabric network interface
+   of the host is consistent with the information in the Network Configuration
+   section. For example, you can run
+
+   .. code-block:: console
+
+       # ip addr add 10.0.0.1/24 dev mlx0
+
+2. **Configure the default route**
+
+   Make sure you change the default route of the host to the interface IP of
+   the leaf switch it connects to.  For example, you can run
+
+   .. code-block:: console
+
+       # ip route add default via 10.0.0.254
+
+   .. note::
+       In the case that you want to keep default route through the management network,
+       you need to add routes to all other subnets in the  network one by one.
+
+3. **Trigger host learning**
+
+   We need to let ONOS learn the host in order to program corresponding flows
+   and groups.
+
+   This is automatically done as part of the DHCP process.
+
+   However, we need to manually triggers it by sending an ARP or ND packet if
+   the host is configured to use static IP.
+
+   .. code-block:: console
+
+       # arping -c 1 ${GATEWAY_IP}
+
+   .. code-block:: console
+
+       # ndsend ${HOST_IP} ${INTF}
+
+Reference
+---------
+- https://www.cisco.com/c/en/us/support/docs/security/adaptive-security-appliance-asa-software/116265-configure-product-00.html
diff --git a/advanced/connectivity/dual-homing.rst b/advanced/connectivity/dual-homing.rst
new file mode 100644
index 0000000..74e6496
--- /dev/null
+++ b/advanced/connectivity/dual-homing.rst
@@ -0,0 +1,487 @@
+Dual Homing
+===========
+
+Overview
+--------
+
+.. image:: ../../images/config-dh.png
+
+The dual-homing feature includes several sub components
+
+- **Use of "paired" ToRs**: Each rack of compute nodes have exactly two
+  Top-of-Rack switches (ToRs), that are linked to each other via a single link
+  - such a link is referred to as a **pair link**.  This pairing should NOT be
+  omitted.
+
+  Currently there is support for only a single link between paired ToRs.  In
+  future releases, we may include dual pair links.  Note that the pair link is
+  only used in failure scenarios, and not in normal operation.
+
+- **Dual-homed servers (compute-nodes)**: Each server is connected to both
+  ToRs.  The links to the paired ToRs are (Linux) bonded
+
+- **Dual-homed upstream routers**: The upstream routers MUST be connected to
+  the two ToRs that are part of a leaf-pair.  You cannot connect them to leafs
+  that are not paired. This feature also requires two Quagga instances.
+
+- **Dual-homed access devices**. This component will be added in the future.
+
+Paired ToRs
+-----------
+The reasoning behind two ToR (leaf) switches is simple.  If you only have a
+single ToR switch, and you lose it, the entire rack goes down.  Using two ToR
+switches increases your odds for continued connectivity for dual homed servers.
+The reasoning behind pairing the two ToR switches is more involved, as is
+explained in the Usage section below.
+
+Configure pair ToRs
+^^^^^^^^^^^^^^^^^^^
+Configuring paired-ToRs involves device configuration. Assume switches of:205
+and of:206 are paired ToRs.
+
+.. code-block:: json
+
+    {
+      "devices" : {
+        "of:0000000000000205" : {
+          "segmentrouting" : {
+            "name" : "Leaf1-R2",
+            "ipv4NodeSid" : 205,
+            "ipv4Loopback" : "192.168.0.205",
+            "ipv6NodeSid" : 205,
+            "ipv6Loopback" : "2000::c0a8:0205",
+            "routerMac" : "00:00:02:05:00:01",
+            "pairDeviceId" : "of:0000000000000206",
+            "pairLocalPort" : 20,
+            "isEdgeRouter" : true,
+            "adjacencySids" : []
+          }
+        },
+        "of:0000000000000206" : {
+          "segmentrouting" : {
+            "name" : "Leaf2-R2",
+            "ipv4NodeSid" : 206,
+            "ipv4Loopback" : "192.168.0.206",
+            "ipv6NodeSid" : 206,
+            "ipv6Loopback" : "2000::c0a8:0206",
+            "routerMac" : "00:00:02:05:00:01",
+            "pairDeviceId" : "of:0000000000000205",
+            "pairLocalPort" : 30,
+            "isEdgeRouter" : true,
+            "adjacencySids" : []
+          }
+        }
+      }
+    }
+
+There are two new pieces of device configuration.
+
+Each device in the ToR pair needs to specify the **deviceId of the leaf it is
+paired to**, in the ``pairDeviceId`` field.  For example, in ``of:205``
+configuration the ``pairDeviceId`` is specified as ``of:206``, and similarly in ``of:206``
+configuration the ``pairDeviceId`` is ``of:205``. Each device in the ToR pair needs to
+specify the **port on the device used for the pair link** in the
+``pairLocalPort`` field.  For example, the pair link in the config above show
+that port 20 on of:205 is connected to port 30 on of:206.
+
+In addition, there is one crucial piece of config that needs to **match for
+both ToRs** – the ``routerMac`` address.  The paired-ToRs MUST have the same
+``routerMac`` - in the example above, they both have identical 00:00:02:05:00:01
+``routerMac``.
+
+All other fields are the same as before, as explained in :doc:`Device
+Configuration <../../configuration/network>` section.
+
+
+Usage of pair link
+^^^^^^^^^^^^^^^^^^
+
+.. image:: ../../images/config-dh-pair-link.png
+
+
+Dual-Homed Servers
+------------------
+
+There are a number of things to note when connecting dual-homed servers to paired-ToRs.
+
+- The switch ports on the two ToRs have to be configured the same way, when
+  connecting a dual-homed server to the two ToRs.
+
+- The server ports have to be Linux-bonded in a particular mode.
+
+Configure Switch Ports
+^^^^^^^^^^^^^^^^^^^^^^
+
+The way to configure ports are similar as described in :doc:`Bridging and
+Unicast <../../configuration/network>`.  However, there are a couple of things to note.
+
+**First**, dual-homed servers should have the **identical configuration on each
+switch port they connect to on the ToR pairs**.  The example below shows that
+the ``vlans`` and ``ips`` configured are the same on both switch ports
+``of:205/12`` and ``of:206/29``.  They are both configured to be access ports
+in ``VLAN 20``, the subnet ``10.0.2.0/24`` is assigned to these ports, and the
+gateway-IP is ``10.0.2.254/32``.
+
+.. code-block:: json
+
+    {
+      "ports" : {
+        "of:0000000000000205/12" : {
+          "interfaces" : [{
+            "name" : "h3-intf-1",
+            "ips" : [ "10.0.2.254/24"],
+            "vlan-untagged": 20
+          }]
+        },
+        "of:0000000000000206/29" : {
+          "interfaces" : [{
+            "name" : "h3-intf-2",
+            "ips" : [ "10.0.2.254/24"],
+            "vlan-untagged": 20
+          }]
+        }
+      }
+    }
+
+It is worth noting the meaning behind the configuration above from a routing
+perspective.  Simply put, by configuring the same subnets on these switch
+ports, the fabric now believes that the entire subnet ``10.0.2.0/24`` is
+reachable by BOTH ToR switches ``of:205`` and ``of:206``.
+
+.. caution::
+    Configuring different VLANs, or different subnets, or mismatches like
+    ``vlan-untagged`` in one switch port and ``vlan-tagged`` in the corresponding
+    switch port facing the dual-homed server, will result in incorrect
+    behavior.
+
+**Second**, we need to configure the **pair link ports on both ToR switches to
+be trunk (``vlan-tagged``) ports that contains all dual-homed VLANs and subnets**.
+This is an extra piece of configuration, the need for which will be removed in
+future releases.  In the example above, a dual-homed server connects to the ToR
+pair on port 12 on of:205 and port 29 on of:206.  Assume that the pair link
+between the two ToRs is connected to port 5 of both of:205 and of:206.  The
+config for these switch ports is shown below:
+
+.. code-block:: json
+
+    {
+      "ports": {
+        "of:0000000000000205/5" : {
+          "interfaces" : [{
+              "name" : "205-pair-port",
+              "ips" : [ "10.0.2.254/24"],
+              "vlan-tagged": [20]
+          }]
+        },
+        "of:0000000000000206/5" : {
+          "interfaces" : [{
+            "name" : "206-pair-port",
+            "ips" : [ "10.0.2.254/24"],
+            "vlan-tagged": [20]
+          }]
+        }
+      }
+    }
+
+.. note::
+    Even though the ports ``of:205/12`` and ``of:206/29`` facing the dual-homed
+    server are configured as ``vlan-untagged``, the same VLAN MUST be
+    configured as ``vlan-tagged`` on the pair-ports.
+
+    If additional subnets and VLANs are configured facing other dual-homed
+    servers, they need to be similarly added to the ``ips`` and ``vlan-tagged``
+    arrays in the pair port config.
+
+
+Configure Servers
+^^^^^^^^^^^^^^^^^
+
+Assuming the interfaces we are going to use for bonding are ``eth1`` and ``eth2``.
+
+- Bring down interfaces
+
+  .. code-block:: console
+
+      $ sudo ifdown eth1
+      $ sudo ifdown eth2
+
+- Modify ``/etc/network/interfaces``
+
+  .. code-block:: text
+
+      auto bond0
+      iface bond0 inet dhcp
+      bond-mode balance-xor
+      bond-xmit_hash_policy layer2+3
+      bond-slaves none
+
+      auto eth1
+      iface eth1 inet manual
+      bond-master bond0
+
+      auto eth2
+      iface eth2 inet manual
+      bond-master bond0
+
+
+- Start interfaces
+
+  .. code-block:: console
+
+      $ sudo ifup bond0
+      $ sudo ifup eth1
+      $ sudo ifup eth2
+
+- Useful command to check bonding status
+
+  .. code-block:: console
+
+      # cat /proc/net/bonding/bond0
+      Ethernet Channel Bonding Driver: v3.7.1 (April 27, 2011)
+
+      Bonding Mode: load balancing (xor)
+      Transmit Hash Policy: layer2+3 (2)
+      MII Status: up
+      MII Polling Interval (ms): 0
+      Up Delay (ms): 0
+      Down Delay (ms): 0
+
+      Slave Interface: eth1
+      MII Status: up
+      Speed: 1000 Mbps
+      Duplex: full
+      Link Failure Count: 0
+      Permanent HW addr: 00:1c:42:5b:07:6a
+      Slave queue ID: 0
+
+      Slave Interface: eth2
+      MII Status: up
+      Speed: Unknown
+      Duplex: Unknown
+      Link Failure Count: 0
+      Permanent HW addr: 00:1c:42:1c:a1:7c
+      Slave queue ID: 0
+
+.. caution::
+    **Dual-homed host should not be statically configured.**
+
+    Currently in ONOS, configured hosts are not updated when the ``connectPoint``
+    is lost.  This is not a problem with single-homed hosts because there is no
+    other way to reach them anyway if their ``connectPoint`` goes down.  But in
+    dual-homed scenarios, the controller should take corrective action if one
+    of the ``connectPoint`` go down – the trigger for this event does not happen
+    when the dual-homed host's connect points are configured (not discovered).
+
+.. note::
+    We also support static routes with dual-homed next hop.  The way to
+    configure it is exactly the same as regular single-homed next hop, as
+    described in :doc:`External Connectivity <external-connectivity>`.
+
+    ONOS will automatically recognize when the next-hop IP resolves to a
+    dual-homed host and program both switches (the host connects to)
+    accordingly.
+
+    The failure recovery mechanism for dual-homed hosts also applies to static
+    routes that point to the host as their next hop.
+
+Dual External Routers
+---------------------
+
+.. image:: ../../images/config-dh-vr.png
+
+.. image:: ../../images/config-dh-vr-logical.png
+    :width: 200px
+
+In addition to what we describe in :doc:`External Connectivity
+<external-connectivity>`, SD-Fabric also supports dual external routers, which
+view the SD-Fabric as 2 individual routers, as shown above.
+
+As before the vRouter control plane is implemented as a combination of Quagga,
+which peers with the upstream routers, and ONOS which listens to Quagga (via
+FPM) and programs the underlying fabric.  **In dual-router scenarios, there are
+two instances of Quagga required**.
+
+As before the hardware fabric serves as the data-plane of vRouter.  In
+dual-router scenarios, the **external routers MUST be connected to
+paired-ToRs**.
+
+ToR connects to one upstream
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Lets consider the simpler case where the external routers are each connected to
+a single leaf in a ToR pair.  The figure on the left below shows the logical
+view. The figure on the right shows the physical connectivity.
+
+.. image:: ../../images/config-dh-vr-logical-simple.png
+    :width: 200px
+
+.. image:: ../../images/config-dh-vr-physical-simple.png
+    :width: 400px
+
+One of the upstream routers is connected to ``of:205`` and the other is
+connected to ``of:206``.  Note that ``of:205`` and ``of:206`` are paired ToRs.
+
+The ToRs are connected via a physical port to separate Quagga VMs or
+containers.  These Quagga instances can be placed in any compute node. They do
+not need to be in the same server, and are only shown to be co-located for
+simplicity.
+
+The two Quagga instances do NOT talk to each other.
+
+Switch port configuration
+"""""""""""""""""""""""""
+
+The ToRs follow the same rules as single router case described in
+:doc:`External Connectivity <external-connectivity>`.  In the example shown
+above, the switch port config would look like this:
+
+.. code-block:: json
+
+    {
+      "ports": {
+        "of:0000000000000205/1" : {
+          "interfaces" : [{
+            "ips" : [ "10.0.100.3/29", "2000::6403/125" ],
+            "vlan-untagged": 100,
+            "name" : "internet-router-1"
+          }]
+        },
+
+        "of:0000000000000205/48" : {
+          "interfaces" : [{
+            "ips" : [ "10.0.100.3/29", "2000::6403/125" ],
+            "vlan-untagged": 100,
+            "name" : "quagga-1"
+          }]
+        },
+
+        "of:0000000000000206/1" : {
+          "interfaces" : [{
+            "ips" : [ "10.0.200.3/29", "2000::6503/125" ],
+            "vlan-untagged": 200,
+            "name" : "internet-router-2"
+          }]
+        },
+
+        "of:0000000000000206/48" : {
+          "interfaces" : [{
+            "ips" : [ "10.0.200.3/29", "2000::6503/125" ],
+            "vlan-untagged": 200,
+            "name" : "quagga2"
+          }]
+        }
+      }
+    }
+
+.. note::
+    In the example shown above, switch ``of:205`` uses ``VLAN 100`` for
+    bridging the peering session between Quagga1 and ExtRouter1, while switch
+    ``of:205`` uses ``VLAN 200`` to do the same for the other peering session.
+    But since these VLANs and bridging domains are defined on different
+    switches, the VLAN ids could have been the same.
+
+    This philosophy is consistent with the fabric use of :doc:`bridging
+    <../../configuration/network>`.
+
+
+Quagga configuration
+""""""""""""""""""""
+Configuring Quagga for dual external routers are similar to what we described
+in :doc:`External Connectivity <external-connectivity>`. However, it is worth
+noting that:
+
+- The two Zebra instances **should point to two different ONOS instances** for
+  their FPM connections.  For example Zebra in Quagga1 could point to ONOS
+  instance with ``fpm connection ip 10.6.0.1 port 2620``, while the other Zebra
+  should point to a different ONOS instance with ``fpm connection ip 10.6.0.2
+  port 2620``.  It does not matter which ONOS instances they point to as long
+  as they are different.
+
+- The two Quagga BGP sessions should appear to come from different routers but
+  still use the same AS number – i.e. the two Quaggas' belong to the same AS,
+  the one used to represent the entire SD-Fabric.
+
+- The two upstream routers can belong to the same or different AS, but these AS
+  numbers should be different from the one used to represent the SD-Fabric AS.
+
+- Typically both Quagga instances advertise the same routes to the upstream.
+  These prefixes belonging to various infrastructure nodes in the deployment
+  should be reachable from either of the leaf switches connected to the
+  upstream routers.
+
+- The upstream routers may or may not advertise the same routes.  SD-Fabric will
+  ensure that traffic directed to a route reachable only one upstream router is
+  directed to the appropriate leaf.
+
+ToR connects to both upstream
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Now lets consider the **more-complicated but more fault-tolerant** case of each
+Quagga instance peering with BOTH external routers.  Again the logical view is
+shown on the left and the physical view on the right.
+
+.. image:: ../../images/config-dh-vr-logical.png
+    :width: 200px
+
+.. image:: ../../images/config-dh-vr-physical.png
+    :width: 500px
+
+First lets talk about the physical connectivity
+
+- Quagga instance 1 peers with external router R1 via port 1 on switch of:205
+- Quagga instance 1 peers with external router R2 via port 2 on switch of:205
+
+Similarly
+
+- Quagga instance 2 peers with external router R1 via port 2 on switch of:206
+- Quagga instance 2 peers with external router R2 via port 1 on switch of:206
+
+To distinguish between the two peering sessions in the same physical switch,
+say of:205, the physical ports 1 and 2 need to be configured in **different
+VLANs and subnets**.  For example, port 1 on of:205 is (untagged) in VLAN 100,
+while port 2 is in VLAN 101.
+
+Note that peering for **Quagga1 and R1** happens with IPs in the
+``10.0.100.0/29`` subnet, and for **Quagga 1 and R2** in the **10.0.101.0/29**
+subnet.
+
+Furthermore, **pair link** (port 48) on of:205 carries both peering sessions to
+Quagga1.  Thus port 48 should now be configured as a **trunk port (vlan-tagged)
+with both VLANs and both subnets**.
+
+Finally the **Quagga interface** on the VM now needs **sub-interface
+configuration for each VLAN ID**.
+
+Similar configuration concepts apply to IPv6 as well. Here is a look at the
+switch port config in ONOS for of:205
+
+.. code-block:: json
+
+    {
+      "ports": {
+        "of:0000000000000205/1" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.100.3/29", "2000::6403/125" ],
+                "vlan-untagged": 100,
+                "name" : "internet-router1"
+            }]
+        },
+
+
+        "of:0000000000000205/2" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.101.3/29", "2000::7403/125" ],
+                "vlan-untagged": 101,
+                "name" : "internet-router2"
+            }]
+        },
+        "of:0000000000000205/48" : {
+            "interfaces" : [{
+                "ips" : [ "10.0.100.3/29", "2000::6403/125", "10.0.101.3/29", "2000::7403/125" ],
+                "vlan-tagged": [100, 101],
+                "name" : "quagga1"
+            }]
+
+        }
+      }
+    }
diff --git a/advanced/connectivity/external-connectivity.rst b/advanced/connectivity/external-connectivity.rst
new file mode 100644
index 0000000..c8b1612
--- /dev/null
+++ b/advanced/connectivity/external-connectivity.rst
@@ -0,0 +1,445 @@
+External Connectivity
+=====================
+
+vRouter
+-------
+
+Physical Connectivity
+^^^^^^^^^^^^^^^^^^^^^
+
+External routers must be physically connected to one of the fabric leaf
+switches.
+
+Currently there is a limitation that the **external/upstream router and the
+Quagga instance must be connected to the same fabric leaf switch**.
+
+Therefore it is necessary to use an additional front panel port on the
+leaf-switch (or at least an additional VLAN) to connect to the compute node
+hosting Quagga.
+
+.. image:: ../../images/config-vr-physical.png
+
+Configure vRouter
+^^^^^^^^^^^^^^^^^
+
+The operator will need to configure a subnet between the Leaf-switch, the
+external/upstream router and the Quagga instance. There are 3 IP addresses we
+need to allocate - 1 on the switch port, 1 in Quagga, and 1 on the upstream
+router. This means the peering subnet **cannot be smaller than a /29**.
+
+BGP peering happens between the IP addresses configured on the interfaces in
+Quagga and the external router.
+
+Routes are advertised by Quagga to the upstream with the next-hop set to the
+switch port IP address.  This means that when traffic comes to the fabric leaf
+switch from outside, the switch is able to distinguish peering traffic from
+data traffic and treat each appropriately.
+
+The following shows an ONOS interface configuration example:
+
+.. code-block:: json
+
+    {
+      "ports" : {
+        "of:0000000000000001/1" : {
+          "interfaces" : [
+            {
+              "name" : "upstream1",
+              "ips"  : [ "10.0.1.2/24" ],
+              "vlan-untagged" : 4000
+            }
+          ]
+        },
+        "of:0000000000000001/2" : {
+          "interfaces" : [
+              {
+                "name" : "quagga",
+                "ips"  : [ "10.0.1.2/24" ],
+                "vlan-untagged" : 4000
+              }
+          ]
+        }
+      }
+    }
+
+- ``name``: An arbitrary name string for the interface. Optional.
+
+- ``ips``: Configure the peering subnet (10.0.1.0/24) and the switch port IP
+  (10.0.1.2).  Note that we use the same IP address on both the Quagga and
+  upstream interfaces.
+
+- ``vlan-untagged``: Configure the same VLAN ID on both interfaces.  It doesn't
+  matter exactly what the VLAN ID is, but it must be the same on both the
+  Quagga-facing and upstream-facing interfaces.
+
+In this case the peering subnet is ``10.0.1.0/24``.
+The upstream router is using the ``10.0.1.1`` address.
+Quagga is assigned ``10.0.1.3``, which is the address used for peering.
+
+The upstream router needs to be configured with ``10.0.1.3`` as its BGP
+neighbor, and the BGP peering will be established between ``10.0.1.1`` and
+``10.0.1.3``. The ``10.0.1.2`` address is used by the fabric switch and for the
+next-hop for routes advertised by Quagga.
+
+Of course you are not obliged to use ``10.0.1.0/24``, you should use a subnet
+that makes sense for your peering environment.
+
+.. note::
+    This configuration will set up an L2 link between the two fabric switch
+    ports, over which the Quagga and external router can communicate.
+
+    Both Quagga and the upstream router will receive untagged packets (i.e they
+    will never see packets with VLAN id 4000, which is used inside the leaf
+    switch to establish a bridging domain).
+
+    If you need a VLAN tag in the compute node to distinguish the traffic going
+    to Quagga, you can change the VLAN assignment on the switch port
+    "of:0000000000000001/2" to be ``vlan-tagged`` instead of ``vlan-untagged``.
+
+Deploy the Quagga Docker Image
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+SD-Fabric uses a slightly modified version of Quagga, so the easiest way to
+deploy this is to use the provided docker image.
+
+.. code-block:: console
+
+    $ docker pull opencord/quagga
+
+We also need to download the **pipework** tool which will be used to connect
+the docker image to the physical interface that we set aside earlier.
+
+.. code-block:: console
+
+    $ wget https://raw.githubusercontent.com/jpetazzo/pipework/master/pipework
+    $ chmod +x pipework
+
+Create a directory for your Quagga configuration files, and create a ``bgpd.conf``
+and ``zebra.conf`` in there.  This folder is going to be mounted into the Quagga
+container.  More on configuring Quagga later.
+
+.. code-block:: console
+
+    $ mkdir configs
+    $ touch zebra.conf bgpd.conf
+
+Now run the docker image (make sure the path the config directory matches what
+is on your system):
+
+.. code-block:: console
+
+    $ sudo docker run --privileged -d -v configs:/etc/quagga -n quagga opencord/quagga
+
+Finally, we can use the pipework tool to add the physical interface into the
+container so that Quagga can talk out over the fabric:
+
+.. code-block:: console
+
+    $ sudo ./pipework mlx1 -i eth1 quagga 10.0.1.3/24
+
+This will add host interface ``mlx1`` to the container with name ``quagga``
+with interface name ``eth1`` inside the container.  The newly added interface
+will have the IP ``10.0.1.3``.  This IP address should be the peering subnet
+address that you want to assign to Quagga.
+
+If you need to change anything about the container (for example if you change
+the Quagga configuration) you can remove the original container and run a new
+one:
+
+.. code-block:: console
+
+    $ sudo docker rm -f quagga
+    $ sudo docker run --privileged -d -v configs:/etc/quagga -n quagga opencord/quagga
+
+Configure Quagga
+^^^^^^^^^^^^^^^^
+
+At this point Quagga should have IP connectivity to the external routers, and
+it should be able to ping them on the peering subnet.
+
+Now Quagga and the upstream routers can be configured to peer with one another.
+This configuration of Quagga is going to be highly dependent on the
+configuration of the upstream network, so it won't be possible to give
+comprehensive configuration examples here.
+
+It is recommended to consult the Quagga documentation for exhaustive
+information on Quagga's capabilities and configuration.  Here I will attempt to
+provide a few basic examples of Quagga configuration to get you started.
+You'll have to enhance these with the features and functions that are needed in
+your network.
+
+Zebra configuration
+"""""""""""""""""""
+
+Regardless of which routing protocols you are using in your network, it is
+important to configure Zebra's FPM connection to send routes to the FPM app
+running on ONOS.  This feature was enabled by the patch that was applied
+earlier when we installed Quagga.
+
+A minimal Zebra configuration might look like this:
+
+.. code-block:: text
+
+    !
+    hostname cord-zebra
+    password cord
+    !
+    fpm connection ip 10.6.0.1 port 2620
+    !
+
+The FPM connection IP address is the IP address of **one of the onos cluster
+instances** - does not matter which one.  If you have other configuration that
+needs to go in ``zebra.conf`` you should add that here as well.
+
+BGP configuration
+"""""""""""""""""
+
+An example simple BGP configuration for peering with one BGP peer might look
+like this:
+
+.. code-block:: text
+
+    hostname bgp
+    password cord
+    !
+    ip prefix-list 1 seq 10 permit 192.168.0.0/16
+    !
+    route-map NEXTHOP permit 10
+    match ip address prefix-list 1
+    set ip next-hop 10.0.1.2
+    !
+    router bgp 65535
+      bgp router-id 10.0.1.3
+      !
+      network 192.168.0.0/16
+      !
+      neighbor 10.0.1.1 remote-as 65540
+      neighbor 10.0.1.1 description upstream1
+      neighbor 10.0.1.1 route-map NEXTHOP out
+      !
+
+This configuration peers with one upstream router ``10.0.1.1`` and advertises
+one route ``192.168.0.0/16``.  Note that Quagga (and as a result SD-Fabric) is in
+a different AS ``65535`` from the upstream router AS ``65540``, as we are using
+E-BGP for this connectivity.
+
+.. note::
+    Pay attention to the configuration to rewrite the next hop of routes that
+    are advertised to the upstream router.
+
+    A ``route-map`` is used to set the next hop of advertised routes to
+    ``10.0.1.2``, which is **different from the address that Quagga is using to
+    peer with the external router**.
+
+    As mentioned above, it is important that this rewriting is done correctly
+    so that the fabric switch is able to **distinguish data plane and control
+    plane** traffic.
+
+Route service and static route
+------------------------------
+
+Access route service via CLI
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+View routes
+"""""""""""
+
+This will show routes from all sources, including static and dynamic routes.
+
+The example below shows routes learned from the upstream router (Source: FPM)
+and routes configured manually (Source: STATIC)
+
+.. code-block:: text
+
+    onos> routes
+
+    B: Best route, R: Resolved route
+
+    Table: ipv4
+    B R  Network            Next Hop        Source (Node)
+         0.0.0.0/0          172.16.0.1      FPM (127.0.0.1)
+    > *  1.1.0.0/18         10.0.1.20       STATIC
+    > *  10.0.99.0/24       10.0.1.1        FPM (127.0.0.1)
+      *  10.0.99.0/24       10.0.6.1        FPM (127.0.0.1)
+       Total: 2
+
+    Table: ipv6
+    B R  Network                                     Next Hop                                Source (Node)
+    > *  2000::7700/120                              fe80::288:ff:fe00:1                     FPM (127.0.0.1)
+    > *  2000::8800/120                              fe80::288:ff:fe00:2                     FPM (127.0.0.1)
+    > *  2000::9900/120                              fe80::288:ff:fe00:1                     FPM (127.0.0.1)
+      *  2000::9900/120                              fe80::288:ff:fe00:2                     FPM (127.0.0.1)
+       Total: 3
+
+
+Add a static route
+""""""""""""""""""
+
+.. code-block:: console
+
+    onos> route-add <prefix> <nexthop>
+    onos> route-add 1.1.0.0/18 10.0.1.20
+    onos> route-add 2020::101/120 2000::1
+
+
+Remove a static route
+"""""""""""""""""""""
+
+.. code-block:: console
+
+    onos> route-remove <prefix> <nexthop>
+    onos> route-remove 1.1.0.0/18 10.0.1.20
+
+
+Access route service via REST
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Single route
+""""""""""""
+
+.. code-block:: console
+
+    $ curl --user onos:rocks -X POST -H 'Content-Type:application/json' http://<controller-ip>:8181/onos/routeservice/routes -d@routes.json
+    $ curl --user onos:rocks -X GET -H 'Accept:application/json' http://<controller-ip>:8181/onos/routeservice/routes | python -mjson.tool
+    $ curl --user onos:rocks -X DELETE -H 'Content-Type:application/json' http://<controller-ip>:8181/onos/routeservice/routes -d@routes.json
+
+with identical json format for both POST and DELETE:
+
+.. code-block:: json
+
+    {
+      "prefix": "20.0.0.1/24",
+      "nextHop": "10.0.1.10"
+    }
+
+
+Bulk routes
+"""""""""""
+
+.. code-block:: console
+
+    $ curl --user onos:rocks -X POST -H 'Content-Type:application/json' http://<controller-ip>:8181/onos/routeservice/routes/bulk -d@routes.json
+    $ curl --user onos:rocks -X DELETE -H 'Content-Type:application/json' http://<controller-ip>:8181/onos/routeservice/routes/bulk -d@routes.json
+
+with identical json format for both POST and DELETE:
+
+.. code-block:: json
+
+    {
+      "routes": [
+        {
+          "prefix": "20.0.0.1/24",
+          "nextHop": "10.0.1.10"
+        },
+        {
+          "prefix": "30.0.0.1/24",
+          "nextHop": "10.0.2.15"
+        }
+      ]
+    }
+
+
+Verify routes
+^^^^^^^^^^^^^
+Check the leaf switches that the route (e.g. 1.1.0.0/18) has been programmed in
+the routing table (table 30).
+
+.. code-block:: console
+
+    onos> flows any of:0000000000000205 30
+    <snip>
+    id=670000d1f6782c, state=ADDED, bytes=0, packets=0, duration=39, liveType=UNKNOWN, priority=36010, tableId=30, appId=org.onosproject.segmentrouting, payLoad=null, selector=[ETH_TYPE:ipv4, IPV4_DST:1.1.0.0/18],
+     treatment=DefaultTrafficTreatment{immediate=[], deferred=[GROUP:0x70000014], transition=TABLE:60, meter=None, cleared=false, metadata=null}
+    <snip>
+
+Notes about next hops
+^^^^^^^^^^^^^^^^^^^^^
+The next hop of a route should be resolvable to a MAC address that is known to
+ONOS.  Typically the next hop is a server interface that is known to ONOS as a
+host learned via ARP or DHCP.  If you are not sure, check the ``hosts`` command
+on the ONOS CLI.
+
+.. code-block:: console
+
+    onos> hosts
+    <snip>
+    id=A2:9B:32:9D:7F:B3/None, mac=A2:9B:32:9D:7F:B3, location=of:0000000000000205/48, vlan=None, ip(s)=[192.168.101.2], configured=false
+    id=B2:A4:E2:72:D1:91/None, mac=B2:A4:E2:72:D1:91, location=of:0000000000000204/16, vlan=None, ip(s)=[10.0.1.20], configured=false
+    id=EE:22:F7:BE:86:50/None, mac=EE:22:F7:BE:86:50, location=of:0000000000000205/16, vlan=None, ip(s)=[10.0.2.15], configured=false
+
+If the next hop has not been resolved for any reason, it would be necessary to
+configure the next hop as a host (/32 prefix) together with MAC address and
+location.
+
+Learn more about how to configure a host using `Network Config Host Provider
+<https://wiki.onosproject.org/display/ONOS/Network+Config+Host+Provider>`_
+
+Finally note that if you are configuring routes manually/statically and they
+are publicly routable IPs that should be reachable from “outside”, you would
+need to configure Quagga to advertise them upstream.
+
+
+Route blackhole
+---------------
+The blackhole consists of a rule on table 30 on every edge device on the
+fabric.  The Table 30 rule matches on a given IP address and mask and has
+nothing but a ``clearDeferred`` action, practically dropping the packet.  Every IP
+we want to blackhole will have it's own rule in every edge switch.
+
+An example of such rule is:
+
+.. code-block:: text
+
+    ADDED, bytes=0, packets=0, table=30, priority=48010, selector=[ETH_TYPE:ipv4, IPV4_DST:50.0.0.0/24], treatment=[transition=TABLE:60]
+
+Route blackholing can be done via network configuration.
+
+.. code-block:: json
+
+    {
+      "apps" : {
+        "org.onosproject.segmentrouting" : {
+          "segmentrouting": {
+            "blackholeIps": [
+              "50.0.0.0/24"
+            ]
+          }
+        }
+      }
+    }
+
+Ignore certain FPM peer
+-----------------------
+The ``FpmConnectionInfo`` consists a new flag ``acceptRoutes``, indicating
+whether we want to accept or discard the routes advertised by certain FPM peer.
+Per current requirement, we always have the ``acceptRoutes`` flag set to
+``true`` by default, meaning that we will accept routes from all peers.
+
+We can updated the flag using REST API and CLI command as below
+
+REST API
+^^^^^^^^
+- ``POST /acceptRoutes`` to enable or disable ``acceptRoutes`` flag
+- ``GET /acceptRoutes`` to fetch the current status of the FPM connection
+
+.. image:: ../../images/config-fpm-rest.png
+    :width: 900px
+
+CLI
+^^^
+
+- ``fpm-set-accept-routes`` to enable or disable ``acceptRoutes`` flag
+
+  .. code-block:: console
+
+     onos> fpm-set-accept-routes 10.250.16.40 52560 false
+
+- ``fpm-get-accept-route`` to fetch the current status of the FPM connection
+
+  .. code-block:: console
+
+    onos> fpm-get-accept-route
+    <snip>
+    peer 10.250.16.40  port 52560 acceptRoutes false
+    peer 10.250.16.41  port 52594 acceptRoutes true
+    <snip>
diff --git a/dict.txt b/dict.txt
index 18f34c2..0576d63 100644
--- a/dict.txt
+++ b/dict.txt
@@ -1,22 +1,21 @@
-# Please keep lines in ascending case-sensitive order, so it's easier to spot duplicates
-# during merge conflict resolution.
 Aether
 Analytics
 Broadcom
 Clos
+DDoS
 Edgecore
 Fluentbit
-DDoS
 GPP
 Grafana
+IPv
 Inband
 Inventec
 IoT
-IPv
 Kibana
 Kubernetes
 Multicast
 Netburg
+Netlink
 NxM
 ONF
 OpenConfig
@@ -26,23 +25,25 @@
 QinQ
 QoS
 Quagga
+Quaggas
 Runtime
+SD-Fabric
+SDN
+SKU
 SaaS
 Scalability
-SDN
-SD-Fabric
-SKU
 Subnetting
 TLS
-Tofino
 ToR
 ToRs
-Unicast
+Tofino
 UPF
+Unicast
 backdoors
 backend
 backhaul
 bitrate
+blackhole
 blackholing
 centric
 chipset
@@ -55,11 +56,13 @@
 downlink
 encap
 failover
-gRPC
 gNMI
 gNOI
+gRPC
+gatewayIP
 hyperscalers
 instantiation
+ip
 keepalives
 lifecycle
 linecard
@@ -94,7 +97,7 @@
 unicast
 untagged
 uplink
-verifiability
 virtualenv
 vRouter
+verifiability
 whitepaper
diff --git a/images/config-dh-pair-link.png b/images/config-dh-pair-link.png
new file mode 100644
index 0000000..cee6bc4
--- /dev/null
+++ b/images/config-dh-pair-link.png
Binary files differ
diff --git a/images/config-dh-vr-logical-simple.png b/images/config-dh-vr-logical-simple.png
new file mode 100644
index 0000000..fb724bb
--- /dev/null
+++ b/images/config-dh-vr-logical-simple.png
Binary files differ
diff --git a/images/config-dh-vr-logical.png b/images/config-dh-vr-logical.png
new file mode 100644
index 0000000..2e6b277
--- /dev/null
+++ b/images/config-dh-vr-logical.png
Binary files differ
diff --git a/images/config-dh-vr-physical-simple.png b/images/config-dh-vr-physical-simple.png
new file mode 100644
index 0000000..c3d3b4b
--- /dev/null
+++ b/images/config-dh-vr-physical-simple.png
Binary files differ
diff --git a/images/config-dh-vr-physical.png b/images/config-dh-vr-physical.png
new file mode 100644
index 0000000..de67f9b
--- /dev/null
+++ b/images/config-dh-vr-physical.png
Binary files differ
diff --git a/images/config-dh-vr.png b/images/config-dh-vr.png
new file mode 100644
index 0000000..6bf6813
--- /dev/null
+++ b/images/config-dh-vr.png
Binary files differ
diff --git a/images/config-dh.png b/images/config-dh.png
new file mode 100644
index 0000000..24c5c39
--- /dev/null
+++ b/images/config-dh.png
Binary files differ
diff --git a/images/config-dhcp-indirect.jpg b/images/config-dhcp-indirect.jpg
new file mode 100644
index 0000000..d13d584
--- /dev/null
+++ b/images/config-dhcp-indirect.jpg
Binary files differ
diff --git a/images/config-dhcp.png b/images/config-dhcp.png
new file mode 100644
index 0000000..23ff592
--- /dev/null
+++ b/images/config-dhcp.png
Binary files differ
diff --git a/images/config-fpm-rest.png b/images/config-fpm-rest.png
new file mode 100644
index 0000000..e840520
--- /dev/null
+++ b/images/config-fpm-rest.png
Binary files differ
diff --git a/images/config-vr-physical.png b/images/config-vr-physical.png
new file mode 100644
index 0000000..e3504dd
--- /dev/null
+++ b/images/config-vr-physical.png
Binary files differ