diff mbox

[ovs-dev] ovn-northd: Add support for static_routes.

Message ID 1461202694-15729-1-git-send-email-ruansx@cn.ibm.com
State Changes Requested
Headers show

Commit Message

steve.ruan April 21, 2016, 1:38 a.m. UTC
From: Guru Shetty <guru@ovn.org>

static routes are useful when connecting multiple
routers with each other.

Signed-off-by: steve.ruan <ruansx@cn.ibm.com>
Signed-off-by: Gurucharan Shetty <guru@ovn.org>
Co-authored-by: Gurucharan Shetty <guru@ovn.org>

Reported-by: Na Zhu <nazhu@cn.ibm.com>
Reported-by: Dustin Lundquist <dlundquist@linux.vnet.ibm.com>

Reported-at:
https://bugs.launchpad.net/networking-ovn/+bug/1545140
https://bugs.launchpad.net/networking-ovn/+bug/1539347
---
 ovn/northd/ovn-northd.8.xml   |   5 +-
 ovn/northd/ovn-northd.c       | 101 +++++++++++++++++++++
 ovn/ovn-nb.ovsschema          |  15 +++-
 ovn/ovn-nb.xml                |  31 +++++++
 ovn/utilities/ovn-nbctl.8.xml |   5 ++
 ovn/utilities/ovn-nbctl.c     |   5 ++
 tests/ovn.at                  | 204 ++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 362 insertions(+), 4 deletions(-)

Comments

Gurucharan Shetty April 21, 2016, 4:43 p.m. UTC | #1
On 20 April 2016 at 18:38, steve.ruan <rsxin25@gmail.com> wrote:

> From: Guru Shetty <guru@ovn.org>
>
Run the following command (with your name added) for your next respin:

git commit --amend --author="Author Name <email@address.com>"



>
> static routes are useful when connecting multiple
> routers with each other.
>
> Signed-off-by: steve.ruan <ruansx@cn.ibm.com>
> Signed-off-by: Gurucharan Shetty <guru@ovn.org>
> Co-authored-by: Gurucharan Shetty <guru@ovn.org>
>
> Reported-by: Na Zhu <nazhu@cn.ibm.com>
> Reported-by: Dustin Lundquist <dlundquist@linux.vnet.ibm.com>
>
> Reported-at:
> https://bugs.launchpad.net/networking-ovn/+bug/1545140
> https://bugs.launchpad.net/networking-ovn/+bug/1539347
> ---
>  ovn/northd/ovn-northd.8.xml   |   5 +-
>  ovn/northd/ovn-northd.c       | 101 +++++++++++++++++++++
>  ovn/ovn-nb.ovsschema          |  15 +++-
>  ovn/ovn-nb.xml                |  31 +++++++
>  ovn/utilities/ovn-nbctl.8.xml |   5 ++
>  ovn/utilities/ovn-nbctl.c     |   5 ++
>  tests/ovn.at                  | 204
> ++++++++++++++++++++++++++++++++++++++++++
>  7 files changed, 362 insertions(+), 4 deletions(-)
>
> diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
> index da776e1..978853c 100644
> --- a/ovn/northd/ovn-northd.8.xml
> +++ b/ovn/northd/ovn-northd.8.xml
> @@ -682,8 +682,9 @@ next;
>          </p>
>
>          <p>
> -          If the route has a gateway, <var>G</var> is the gateway IP
> address,
> -          otherwise it is <code>ip4.dst</code>.
> +          If the route has a gateway, <var>G</var> is the gateway IP
> address.
> +          Instead, if the route is from a configured static route,
> <var>G</var>
> +          is the next hop IP address.  Else it is <code>ip4.dst</code>.
>          </p>
>        </li>
>
> diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
> index 260c02f..5d86219 100644
> --- a/ovn/northd/ovn-northd.c
> +++ b/ovn/northd/ovn-northd.c
> @@ -234,6 +234,18 @@ allocate_tnlid(struct hmap *set, const char *name,
> uint32_t max,
>      return 0;
>  }
>
> +/* Holds the next hop ip address and the logical router port via which
> + * a static route is reachable. */
> +struct route_to_port {
> +    ovs_be32 ip;                /* network address of the route. */
> +    ovs_be32 mask;              /* network mask of the route. */
> +    ovs_be32 next_hop;          /* next_hop ip address for the above
> route. */
> +    struct uuid rport;          /* output port specified by CMS, or null
> if not specified */
> +    struct ovn_port *ovn_port;  /* The logical router port via which the
> packet
> +                                 * needs to exit to reach the next hop. */
> +};
> +
> +
>  /* The 'key' comes from nbs->header_.uuid or nbr->header_.uuid or
>   * sb->external_ids:logical-switch. */
>  struct ovn_datapath {
> @@ -249,6 +261,9 @@ struct ovn_datapath {
>      /* Logical router data (digested from nbr). */
>      const struct ovn_port *gateway_port;
>      ovs_be32 gateway;
> +    /* Maps a static route to a ovn logical router port via which packet
> +     * needs to exit. */
> +    struct shash routes_map;
>
>      /* Logical switch data. */
>      struct ovn_port **router_ports;
> @@ -272,6 +287,7 @@ ovn_datapath_create(struct hmap *datapaths, const
> struct uuid *key,
>      od->nbs = nbs;
>      od->nbr = nbr;
>      hmap_init(&od->port_tnlids);
> +    shash_init(&od->routes_map);
>      od->port_key_hint = 0;
>      hmap_insert(datapaths, &od->key_node, uuid_hash(&od->key));
>      return od;
> @@ -286,6 +302,7 @@ ovn_datapath_destroy(struct hmap *datapaths, struct
> ovn_datapath *od)
>           * use it. */
>          hmap_remove(datapaths, &od->key_node);
>          destroy_tnlids(&od->port_tnlids);
> +        shash_destroy_free_data(&od->routes_map);
>          free(od->router_ports);
>          free(od);
>      }
> @@ -318,6 +335,50 @@ ovn_datapath_from_sbrec(struct hmap *datapaths,
>  }
>
>  static void
> +build_static_route(struct ovn_datapath *od,
> +         const struct nbrec_logical_router_static_route *route)
> +{
> +    ovs_be32 prefix, next_hop, mask;
> +
> +    /* verify nexthop */
> +    char *error = ip_parse_masked(route->nexthop, &next_hop, &mask);
> +    if (error || mask != OVS_BE32_MAX) {
> +        static struct vlog_rate_limit rl
> +                        = VLOG_RATE_LIMIT_INIT(5, 1);
> +        VLOG_WARN_RL(&rl, "bad next hop ip address %s",
> +            route->nexthop);
> +        free(error);
> +        return;
> +    }
> +
> +    /* verify prefix */
> +    error = ip_parse_masked(route->prefix, &prefix, &mask);
> +    if (error || !ip_is_cidr(mask)) {
> +        static struct vlog_rate_limit rl
> +                        = VLOG_RATE_LIMIT_INIT(5, 1);
> +        VLOG_WARN_RL(&rl, "bad 'network' in static routes %s",
> +                          route->prefix);
> +        free(error);
> +        return;
> +    }
> +
> +    struct uuid lrp_uuid;
> +    struct route_to_port *route_port = xmalloc(sizeof *route_port);
> +    route_port->ip = prefix;
> +    route_port->mask = mask;
> +    route_port->next_hop = next_hop;
> +    /* The correct ovn_port will be filled while traversing
> +     * logical_router_ports. */
> +    route_port->ovn_port = NULL;
> +    memset(&route_port->rport, 0, sizeof (struct uuid));
> +    if (route->output_port
> +        && uuid_from_string(&lrp_uuid, route->output_port)){
>
+        memcpy(&route_port->rport, &lrp_uuid, sizeof (struct uuid));
> +    }
> +    shash_add(&od->routes_map, route->prefix, route_port);
> +}
> +
> +static void
>  join_datapaths(struct northd_context *ctx, struct hmap *datapaths,
>                 struct ovs_list *sb_only, struct ovs_list *nb_only,
>                 struct ovs_list *both)
> @@ -409,6 +470,14 @@ join_datapaths(struct northd_context *ctx, struct
> hmap *datapaths,
>          /* Set the gateway port to NULL.  If there is a gateway, it will
> get
>           * filled in as we go through the ports later. */
>          od->gateway_port = NULL;
> +
> +        /* Handle static routes set in this logical router. */
> +        for (int i = 0; i < nbr->n_static_routes; i++) {
> +            const struct nbrec_logical_router_static_route *route;
> +
> +            route = od->nbr->static_routes[i];
> +            build_static_route(od, route);
> +        }
>      }
>  }
>
> @@ -644,6 +713,29 @@ join_logical_ports(struct northd_context *ctx,
>                          od->gateway_port = op;
>                      }
>                  }
> +
> +                /* Go through the static routes of the router to see which
> +                 * route is reachable via this logical router port. */
> +                struct shash_node *node;
> +                SHASH_FOR_EACH(node, &od->routes_map) {
> +                    struct route_to_port *route_port = node->data;
> +
> +                    if (uuid_equals(&route_port->rport,
> &nbr->header_.uuid)){
> +                        route_port->ovn_port = op;
> +                    }
> +                    else{
> +                        /* If 'od' has a static route and 'op' routes to
> it... */
> +                        if (!((op->network ^ route_port->next_hop) &
> op->mask)) {
> +                            /* .....and if 'op' is a longer match than
> the current
> +                             * choice... */
> +                            const struct ovn_port *curr =
> route_port->ovn_port;
> +                            int len = curr ?
> ip_count_cidr_bits(curr->mask) : 0;
> +                            if (ip_count_cidr_bits(op->mask) > len) {
> +                                route_port->ovn_port = op;
> +                            }
> +                        }
> +                    }
> +                }
>              }
>          }
>      }
> @@ -1986,6 +2078,15 @@ build_lrouter_flows(struct hmap *datapaths, struct
> hmap *ports,
>              continue;
>          }
>
> +        struct shash_node *node;
> +        SHASH_FOR_EACH(node, &od->routes_map) {
> +            struct route_to_port *route_port = node->data;
> +            if (route_port->ovn_port) {
> +                add_route(lflows, route_port->ovn_port, route_port->ip,
> +                          route_port->mask, route_port->next_hop);
> +            }
> +        }
> +
>          if (od->gateway && od->gateway_port) {
>              add_route(lflows, od->gateway_port, 0, 0, od->gateway);
>          }
> diff --git a/ovn/ovn-nb.ovsschema b/ovn/ovn-nb.ovsschema
> index 40a7a97..ce2e72d 100644
> --- a/ovn/ovn-nb.ovsschema
> +++ b/ovn/ovn-nb.ovsschema
> @@ -1,7 +1,7 @@
>  {
>      "name": "OVN_Northbound",
> -    "version": "2.0.2",
> -    "cksum": "4289495412 4436",
> +    "version": "2.0.3",
> +    "cksum": "3119329675 4999",
>      "tables": {
>          "Logical_Switch": {
>              "columns": {
> @@ -72,6 +72,11 @@
>                                     "min": 0,
>                                     "max": "unlimited"}},
>                  "default_gw": {"type": {"key": "string", "min": 0, "max":
> 1}},
> +                "static_routes": {"type": {"key": {"type": "uuid",
> +                                            "refTable":
> "Logical_Router_Static_Route",
> +                                            "refType": "strong"},
> +                                   "min": 0,
> +                                   "max": "unlimited"}},
>                  "external_ids": {
>                      "type": {"key": "string", "value": "string",
>                               "min": 0, "max": "unlimited"}}},
> @@ -87,6 +92,12 @@
>                      "type": {"key": "string", "value": "string",
>                               "min": 0, "max": "unlimited"}}},
>              "indexes": [["name"]],
> +            "isRoot": false},
> +        "Logical_Router_Static_Route": {
> +            "columns": {
> +                "prefix": {"type": "string"},
> +                "nexthop": {"type": "string"},
> +                "output_port": {"type": "string"}},
>              "isRoot": false}
>      }
>  }
> diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml
> index e65bc3a..9e2c742 100644
> --- a/ovn/ovn-nb.xml
> +++ b/ovn/ovn-nb.xml
> @@ -627,6 +627,10 @@
>        IP address to use as default gateway, if any.
>      </column>
>
> +    <column name="static_routes">
> +      One or more static routes, mapping IP prefixes to next hop IP
> addresses.
> +    </column>
> +
>      <group title="Common Columns">
>        <column name="external_ids">
>          See <em>External IDs</em> at the beginning of this document.
> @@ -717,4 +721,31 @@
>        </column>
>      </group>
>    </table>
> +
> +  <table name="Logical_Router_Static_Route" title="logical router static
> routes">
> +    <p>
> +      Each route represents a static route.
> +    </p>
> +
> +    <column name="prefix">
> +      <p>
> +        Prefix of this route, example 192.168.100.0/24
> +      </p>
> +    </column>
> +
> +    <column name="nexthop">
> +      <p>
> +        Nexthop of this route, nexthop can be a IP address of neutron
> port, or
> +        IP address which has been learnt by dynamic ARP
> +      </p>
> +    </column>
> +
> +    <column name="output_port">
> +      <p>
> +        Port ID of the logical router port table. It may be configured or
> may
> +               not be configured
> +      </p>
> +    </column>
> +  </table>
> +
>  </database>
> diff --git a/ovn/utilities/ovn-nbctl.8.xml b/ovn/utilities/ovn-nbctl.8.xml
> index 3b337dd..f6ef803 100644
> --- a/ovn/utilities/ovn-nbctl.8.xml
> +++ b/ovn/utilities/ovn-nbctl.8.xml
> @@ -231,6 +231,11 @@
>          A port within an L3 logical router.  Records may be identified by
> name.
>        </dd>
>
> +    <dt><code>Logical_Router_Static_Route</code></dt>
> +    <dd>
> +      A static route belonging to an L3 logical router.
> +    </dd>
> +
>      </dl>
>
>      <xi:include href="lib/db-ctl-base.xml" xmlns:xi="
> http://www.w3.org/2003/XInclude"/>
> diff --git a/ovn/utilities/ovn-nbctl.c b/ovn/utilities/ovn-nbctl.c
> index bdad368..bb3ce3e 100644
> --- a/ovn/utilities/ovn-nbctl.c
> +++ b/ovn/utilities/ovn-nbctl.c
> @@ -1101,6 +1101,11 @@ static const struct ctl_table_class tables[] = {
>         NULL},
>        {NULL, NULL, NULL}}},
>
> +    {&nbrec_table_logical_router_static_route,
> +     {{&nbrec_table_logical_router_static_route, NULL,
> +       NULL},
> +      {NULL, NULL, NULL}}},
> +
>      {NULL, {{NULL, NULL, NULL}, {NULL, NULL, NULL}}}
>  };
>
> diff --git a/tests/ovn.at b/tests/ovn.at
> index 6fea4e0..af3e1c4 100644
> --- a/tests/ovn.at
> +++ b/tests/ovn.at
> @@ -2192,3 +2192,207 @@ OVS_APP_EXIT_AND_WAIT([ovs-vswitchd])
>  OVS_APP_EXIT_AND_WAIT([ovsdb-server])
>
>  AT_CLEANUP
> +
> +AT_SETUP([ovn -- 2 HVs, 3 LS, 1 lport/LS, 2 peer LRs, static routes])
> +AT_KEYWORDS([ovnstaticroutespeer])
> +AT_SKIP_IF([test $HAVE_PYTHON = no])
> +ovn_start
> +
> +# Logical network:
> +# Two LRs - R1 and R2 that are connected to each other as peers in
> 20.0.0.0/24
> +# network. R1 has switchess foo (192.168.1.0/24)
> +# connected to it.
> +# R2 has alice (172.16.1.0/24) and bob (172.16.2.0/24) connected to it.
> +
> +ovn-nbctl create Logical_Router name=R1
> +ovn-nbctl create Logical_Router name=R2
> +
> +ovn-nbctl lswitch-add foo
> +ovn-nbctl lswitch-add alice
> +ovn-nbctl lswitch-add bob
> +
> +# Connect foo to R1
> +ovn-nbctl -- --id=@lrp create Logical_Router_port name=foo \
> +network=192.168.1.1/24 mac=\"00:00:00:01:02:03\" -- add Logical_Router
> R1 \
> +ports @lrp -- lport-add foo rp-foo
> +
> +ovn-nbctl set Logical_port rp-foo type=router options:router-port=foo \
> +addresses=\"00:00:00:01:02:03\"
> +
> +# Connect alice to R2
> +ovn-nbctl -- --id=@lrp create Logical_Router_port name=alice \
> +network=172.16.1.1/24 mac=\"00:00:00:01:02:04\" -- add Logical_Router R2
> \
> +ports @lrp -- lport-add alice rp-alice
> +
> +ovn-nbctl set Logical_port rp-alice type=router options:router-port=alice
> \
> +addresses=\"00:00:00:01:02:04\"
> +
> +# Connect bob to R2
> +ovn-nbctl -- --id=@lrp create Logical_Router_port name=bob \
> +network=172.16.2.1/24 mac=\"00:00:00:01:02:05\" -- add Logical_Router R2
> \
> +ports @lrp -- lport-add bob rp-bob
> +
> +ovn-nbctl set Logical_port rp-bob type=router options:router-port=bob \
> +addresses=\"00:00:00:01:02:05\"
> +
> +# Connect R1 to R2
> +lrp1_uuid=`ovn-nbctl -- --id=@lrp create Logical_Router_port name=R1_R2 \
> +network="20.0.0.1/24" mac=\"00:00:00:02:03:04\" \
> +-- add Logical_Router R1 ports @lrp`
> +
> +lrp2_uuid=`ovn-nbctl -- --id=@lrp create Logical_Router_port name=R2_R1 \
> +network="20.0.0.2/24" mac=\"00:00:00:02:03:05\" \
> +-- add Logical_Router R2 ports @lrp`
> +
> +ovn-nbctl set logical_router_port $lrp1_uuid peer="R2_R1"
> +ovn-nbctl set logical_router_port $lrp2_uuid peer="R1_R2"
> +
> +#install static routes
> +ovn-nbctl -- --id=@lrt create Logical_Router_Static_Route \
> +prefix=172.16.1.0/24 nexthop=20.0.0.2 -- add Logical_Router \
> +R1 static_routes @lrt
> +
> +ovn-nbctl -- --id=@lrt create Logical_Router_Static_Route \
> +prefix=172.16.2.0/24 nexthop=20.0.0.2 -- add Logical_Router \
> +R1 static_routes @lrt
> +
> +ovn-nbctl -- --id=@lrt create Logical_Router_Static_Route \
> +prefix=192.168.1.0/24 nexthop=20.0.0.1 -- add Logical_Router \
> +R2 static_routes @lrt
> +
> +# Create logical port foo1 in foo
> +ovn-nbctl lport-add foo foo1 \
> +-- lport-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2"
> +
> +# Create logical port alice1 in alice
> +ovn-nbctl lport-add alice alice1 \
> +-- lport-set-addresses alice1 "f0:00:00:01:02:04 172.16.1.2"
> +
> +# Create logical port bob1 in bob
> +ovn-nbctl lport-add bob bob1 \
> +-- lport-set-addresses bob1 "f0:00:00:01:02:05 172.16.2.2"
> +
> +# Create two hypervisor and create OVS ports corresponding to logical
> ports.
> +net_add n1
> +
> +sim_add hv1
> +as hv1
> +ovs-vsctl add-br br-phys
> +ovn_attach n1 br-phys 192.168.0.1
> +ovs-vsctl -- add-port br-int hv1-vif1 -- \
> +    set interface hv1-vif1 external-ids:iface-id=foo1 \
> +    options:tx_pcap=hv1/vif1-tx.pcap \
> +    options:rxq_pcap=hv1/vif1-rx.pcap \
> +    ofport-request=1
> +
> +ovs-vsctl -- add-port br-int hv1-vif2 -- \
> +    set interface hv1-vif2 external-ids:iface-id=alice1 \
> +    options:tx_pcap=hv1/vif2-tx.pcap \
> +    options:rxq_pcap=hv1/vif2-rx.pcap \
> +    ofport-request=2
> +
> +sim_add hv2
> +as hv2
> +ovs-vsctl add-br br-phys
> +ovn_attach n1 br-phys 192.168.0.2
> +ovs-vsctl -- add-port br-int hv2-vif1 -- \
> +    set interface hv2-vif1 external-ids:iface-id=bob1 \
> +    options:tx_pcap=hv2/vif1-tx.pcap \
> +    options:rxq_pcap=hv2/vif1-rx.pcap \
> +    ofport-request=1
> +
> +
> +# Pre-populate the hypervisors' ARP tables so that we don't lose any
> +# packets for ARP resolution (native tunneling doesn't queue packets
> +# for ARP resolution).
> +ovn_populate_arp
> +
> +# Allow some time for ovn-northd and ovn-controller to catch up.
> +# XXX This should be more systematic.
> +sleep 1
> +
> +ip_to_hex() {
> +    printf "%02x%02x%02x%02x" "$@"
> +}
> +trim_zeros() {
> +    sed 's/\(00\)\{1,\}$//'
> +}
> +
> +# Send ip packets between foo1 and alice1
> +src_mac="f00000010203"
> +dst_mac="000000010203"
> +src_ip=`ip_to_hex 192 168 1 2`
> +dst_ip=`ip_to_hex 172 16 1 2`
>
> +packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
> +as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
> +
> +# Send ip packets between foo1 and bob1
> +src_mac="f00000010203"
> +dst_mac="000000010203"
> +src_ip=`ip_to_hex 192 168 1 2`
> +dst_ip=`ip_to_hex 172 16 2 2`
>
> +packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
> +as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
> +
> +echo "---------NB dump-----"
> +ovn-nbctl show
> +echo "---------------------"
> +ovn-nbctl list logical_router
> +echo "---------------------"
> +ovn-nbctl list logical_router_port
> +echo "---------------------"
> +
> +echo "---------SB dump-----"
> +ovn-sbctl list datapath_binding
> +echo "---------------------"
> +ovn-sbctl list port_binding
> +echo "---------------------"
> +
> +echo "------ hv1 dump ----------"
> +as hv1 ovs-ofctl dump-flows br-int
> +echo "------ hv2 dump ----------"
> +as hv2 ovs-ofctl dump-flows br-int
> +
> +# Packet to Expect at bob1
> +src_mac="000000010205"
> +dst_mac="f00000010205"
> +src_ip=`ip_to_hex 192 168 1 2`
> +dst_ip=`ip_to_hex 172 16 2 2`
>
> +expected=${dst_mac}${src_mac}08004500001c000000003e110200${src_ip}${dst_ip}0035111100080000
> +
> +$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv2/vif1-tx.pcap |
> trim_zeros > received.packets
> +echo $expected | trim_zeros > expout
> +AT_CHECK([cat received.packets], [0], [expout])
> +
> +# Packet to Expect at alice1
> +src_mac="000000010204"
> +dst_mac="f00000010204"
> +src_ip=`ip_to_hex 192 168 1 2`
> +dst_ip=`ip_to_hex 172 16 1 2`
>
> +expected=${dst_mac}${src_mac}08004500001c000000003e110200${src_ip}${dst_ip}0035111100080000
> +
> +$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap |
> trim_zeros > received1.packets
> +echo $expected | trim_zeros > expout
> +AT_CHECK([cat received1.packets], [0], [expout])
> +
> +for sim in hv1 hv2; do
> +    as $sim
> +    OVS_APP_EXIT_AND_WAIT([ovn-controller])
> +    OVS_APP_EXIT_AND_WAIT([ovs-vswitchd])
> +    OVS_APP_EXIT_AND_WAIT([ovsdb-server])
> +done
> +
> +as ovn-sb
> +OVS_APP_EXIT_AND_WAIT([ovsdb-server])
> +
> +as ovn-nb
> +OVS_APP_EXIT_AND_WAIT([ovsdb-server])
> +
> +as northd
> +OVS_APP_EXIT_AND_WAIT([ovn-northd])
> +
> +as main
> +OVS_APP_EXIT_AND_WAIT([ovs-vswitchd])
> +OVS_APP_EXIT_AND_WAIT([ovsdb-server])
> +
> +AT_CLEANUP
> --
> 2.5.0
>
>
diff mbox

Patch

diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
index da776e1..978853c 100644
--- a/ovn/northd/ovn-northd.8.xml
+++ b/ovn/northd/ovn-northd.8.xml
@@ -682,8 +682,9 @@  next;
         </p>
 
         <p>
-          If the route has a gateway, <var>G</var> is the gateway IP address,
-          otherwise it is <code>ip4.dst</code>.
+          If the route has a gateway, <var>G</var> is the gateway IP address.
+          Instead, if the route is from a configured static route, <var>G</var>
+          is the next hop IP address.  Else it is <code>ip4.dst</code>.
         </p>
       </li>
 
diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
index 260c02f..5d86219 100644
--- a/ovn/northd/ovn-northd.c
+++ b/ovn/northd/ovn-northd.c
@@ -234,6 +234,18 @@  allocate_tnlid(struct hmap *set, const char *name, uint32_t max,
     return 0;
 }
 
+/* Holds the next hop ip address and the logical router port via which
+ * a static route is reachable. */
+struct route_to_port {
+    ovs_be32 ip;                /* network address of the route. */
+    ovs_be32 mask;              /* network mask of the route. */
+    ovs_be32 next_hop;          /* next_hop ip address for the above route. */
+    struct uuid rport;          /* output port specified by CMS, or null if not specified */
+    struct ovn_port *ovn_port;  /* The logical router port via which the packet
+                                 * needs to exit to reach the next hop. */
+};
+
+
 /* The 'key' comes from nbs->header_.uuid or nbr->header_.uuid or
  * sb->external_ids:logical-switch. */
 struct ovn_datapath {
@@ -249,6 +261,9 @@  struct ovn_datapath {
     /* Logical router data (digested from nbr). */
     const struct ovn_port *gateway_port;
     ovs_be32 gateway;
+    /* Maps a static route to a ovn logical router port via which packet
+     * needs to exit. */
+    struct shash routes_map;
 
     /* Logical switch data. */
     struct ovn_port **router_ports;
@@ -272,6 +287,7 @@  ovn_datapath_create(struct hmap *datapaths, const struct uuid *key,
     od->nbs = nbs;
     od->nbr = nbr;
     hmap_init(&od->port_tnlids);
+    shash_init(&od->routes_map);
     od->port_key_hint = 0;
     hmap_insert(datapaths, &od->key_node, uuid_hash(&od->key));
     return od;
@@ -286,6 +302,7 @@  ovn_datapath_destroy(struct hmap *datapaths, struct ovn_datapath *od)
          * use it. */
         hmap_remove(datapaths, &od->key_node);
         destroy_tnlids(&od->port_tnlids);
+        shash_destroy_free_data(&od->routes_map);
         free(od->router_ports);
         free(od);
     }
@@ -318,6 +335,50 @@  ovn_datapath_from_sbrec(struct hmap *datapaths,
 }
 
 static void
+build_static_route(struct ovn_datapath *od,
+         const struct nbrec_logical_router_static_route *route)
+{
+    ovs_be32 prefix, next_hop, mask;
+
+    /* verify nexthop */
+    char *error = ip_parse_masked(route->nexthop, &next_hop, &mask);
+    if (error || mask != OVS_BE32_MAX) {
+        static struct vlog_rate_limit rl
+                        = VLOG_RATE_LIMIT_INIT(5, 1);
+        VLOG_WARN_RL(&rl, "bad next hop ip address %s",
+            route->nexthop);
+        free(error);
+        return;
+    }
+
+    /* verify prefix */
+    error = ip_parse_masked(route->prefix, &prefix, &mask);
+    if (error || !ip_is_cidr(mask)) {
+        static struct vlog_rate_limit rl
+                        = VLOG_RATE_LIMIT_INIT(5, 1);
+        VLOG_WARN_RL(&rl, "bad 'network' in static routes %s",
+                          route->prefix);
+        free(error);
+        return;
+    }
+
+    struct uuid lrp_uuid;
+    struct route_to_port *route_port = xmalloc(sizeof *route_port);
+    route_port->ip = prefix;
+    route_port->mask = mask;
+    route_port->next_hop = next_hop;
+    /* The correct ovn_port will be filled while traversing
+     * logical_router_ports. */
+    route_port->ovn_port = NULL;
+    memset(&route_port->rport, 0, sizeof (struct uuid));
+    if (route->output_port
+        && uuid_from_string(&lrp_uuid, route->output_port)){
+        memcpy(&route_port->rport, &lrp_uuid, sizeof (struct uuid));
+    }
+    shash_add(&od->routes_map, route->prefix, route_port);
+}
+
+static void
 join_datapaths(struct northd_context *ctx, struct hmap *datapaths,
                struct ovs_list *sb_only, struct ovs_list *nb_only,
                struct ovs_list *both)
@@ -409,6 +470,14 @@  join_datapaths(struct northd_context *ctx, struct hmap *datapaths,
         /* Set the gateway port to NULL.  If there is a gateway, it will get
          * filled in as we go through the ports later. */
         od->gateway_port = NULL;
+
+        /* Handle static routes set in this logical router. */
+        for (int i = 0; i < nbr->n_static_routes; i++) {
+            const struct nbrec_logical_router_static_route *route;
+ 
+            route = od->nbr->static_routes[i];
+            build_static_route(od, route);
+        }
     }
 }
 
@@ -644,6 +713,29 @@  join_logical_ports(struct northd_context *ctx,
                         od->gateway_port = op;
                     }
                 }
+
+                /* Go through the static routes of the router to see which
+                 * route is reachable via this logical router port. */
+                struct shash_node *node;
+                SHASH_FOR_EACH(node, &od->routes_map) {
+                    struct route_to_port *route_port = node->data;
+
+                    if (uuid_equals(&route_port->rport, &nbr->header_.uuid)){
+                        route_port->ovn_port = op;
+                    }
+                    else{
+                        /* If 'od' has a static route and 'op' routes to it... */
+                        if (!((op->network ^ route_port->next_hop) & op->mask)) {
+                            /* .....and if 'op' is a longer match than the current
+                             * choice... */
+                            const struct ovn_port *curr = route_port->ovn_port;
+                            int len = curr ? ip_count_cidr_bits(curr->mask) : 0;
+                            if (ip_count_cidr_bits(op->mask) > len) {
+                                route_port->ovn_port = op;
+                            }
+                        }
+                    }
+                }
             }
         }
     }
@@ -1986,6 +2078,15 @@  build_lrouter_flows(struct hmap *datapaths, struct hmap *ports,
             continue;
         }
 
+        struct shash_node *node;
+        SHASH_FOR_EACH(node, &od->routes_map) {
+            struct route_to_port *route_port = node->data;
+            if (route_port->ovn_port) {
+                add_route(lflows, route_port->ovn_port, route_port->ip,
+                          route_port->mask, route_port->next_hop);
+            }
+        }
+
         if (od->gateway && od->gateway_port) {
             add_route(lflows, od->gateway_port, 0, 0, od->gateway);
         }
diff --git a/ovn/ovn-nb.ovsschema b/ovn/ovn-nb.ovsschema
index 40a7a97..ce2e72d 100644
--- a/ovn/ovn-nb.ovsschema
+++ b/ovn/ovn-nb.ovsschema
@@ -1,7 +1,7 @@ 
 {
     "name": "OVN_Northbound",
-    "version": "2.0.2",
-    "cksum": "4289495412 4436",
+    "version": "2.0.3",
+    "cksum": "3119329675 4999",
     "tables": {
         "Logical_Switch": {
             "columns": {
@@ -72,6 +72,11 @@ 
                                    "min": 0,
                                    "max": "unlimited"}},
                 "default_gw": {"type": {"key": "string", "min": 0, "max": 1}},
+                "static_routes": {"type": {"key": {"type": "uuid",
+                                            "refTable": "Logical_Router_Static_Route",
+                                            "refType": "strong"},
+                                   "min": 0,
+                                   "max": "unlimited"}},
                 "external_ids": {
                     "type": {"key": "string", "value": "string",
                              "min": 0, "max": "unlimited"}}},
@@ -87,6 +92,12 @@ 
                     "type": {"key": "string", "value": "string",
                              "min": 0, "max": "unlimited"}}},
             "indexes": [["name"]],
+            "isRoot": false},
+        "Logical_Router_Static_Route": {
+            "columns": {
+                "prefix": {"type": "string"},
+                "nexthop": {"type": "string"},
+                "output_port": {"type": "string"}},
             "isRoot": false}
     }
 }
diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml
index e65bc3a..9e2c742 100644
--- a/ovn/ovn-nb.xml
+++ b/ovn/ovn-nb.xml
@@ -627,6 +627,10 @@ 
       IP address to use as default gateway, if any.
     </column>
 
+    <column name="static_routes">
+      One or more static routes, mapping IP prefixes to next hop IP addresses.
+    </column>
+
     <group title="Common Columns">
       <column name="external_ids">
         See <em>External IDs</em> at the beginning of this document.
@@ -717,4 +721,31 @@ 
       </column>
     </group>
   </table>
+
+  <table name="Logical_Router_Static_Route" title="logical router static routes">
+    <p>
+      Each route represents a static route.
+    </p>
+
+    <column name="prefix">
+      <p>
+        Prefix of this route, example 192.168.100.0/24
+      </p>
+    </column>
+    
+    <column name="nexthop">
+      <p>
+        Nexthop of this route, nexthop can be a IP address of neutron port, or
+        IP address which has been learnt by dynamic ARP
+      </p>
+    </column>
+    
+    <column name="output_port">
+      <p>
+        Port ID of the logical router port table. It may be configured or may 
+		not be configured
+      </p>
+    </column>
+  </table>
+
 </database>
diff --git a/ovn/utilities/ovn-nbctl.8.xml b/ovn/utilities/ovn-nbctl.8.xml
index 3b337dd..f6ef803 100644
--- a/ovn/utilities/ovn-nbctl.8.xml
+++ b/ovn/utilities/ovn-nbctl.8.xml
@@ -231,6 +231,11 @@ 
         A port within an L3 logical router.  Records may be identified by name.
       </dd>
 
+    <dt><code>Logical_Router_Static_Route</code></dt>
+    <dd>
+      A static route belonging to an L3 logical router.
+    </dd>
+
     </dl>
 
     <xi:include href="lib/db-ctl-base.xml" xmlns:xi="http://www.w3.org/2003/XInclude"/>
diff --git a/ovn/utilities/ovn-nbctl.c b/ovn/utilities/ovn-nbctl.c
index bdad368..bb3ce3e 100644
--- a/ovn/utilities/ovn-nbctl.c
+++ b/ovn/utilities/ovn-nbctl.c
@@ -1101,6 +1101,11 @@  static const struct ctl_table_class tables[] = {
        NULL},
       {NULL, NULL, NULL}}},
 
+    {&nbrec_table_logical_router_static_route,
+     {{&nbrec_table_logical_router_static_route, NULL,
+       NULL},
+      {NULL, NULL, NULL}}},
+
     {NULL, {{NULL, NULL, NULL}, {NULL, NULL, NULL}}}
 };
 
diff --git a/tests/ovn.at b/tests/ovn.at
index 6fea4e0..af3e1c4 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -2192,3 +2192,207 @@  OVS_APP_EXIT_AND_WAIT([ovs-vswitchd])
 OVS_APP_EXIT_AND_WAIT([ovsdb-server])
 
 AT_CLEANUP
+
+AT_SETUP([ovn -- 2 HVs, 3 LS, 1 lport/LS, 2 peer LRs, static routes])
+AT_KEYWORDS([ovnstaticroutespeer])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
+ovn_start
+
+# Logical network:
+# Two LRs - R1 and R2 that are connected to each other as peers in 20.0.0.0/24
+# network. R1 has switchess foo (192.168.1.0/24)
+# connected to it.
+# R2 has alice (172.16.1.0/24) and bob (172.16.2.0/24) connected to it.
+
+ovn-nbctl create Logical_Router name=R1
+ovn-nbctl create Logical_Router name=R2
+
+ovn-nbctl lswitch-add foo
+ovn-nbctl lswitch-add alice
+ovn-nbctl lswitch-add bob
+
+# Connect foo to R1
+ovn-nbctl -- --id=@lrp create Logical_Router_port name=foo \
+network=192.168.1.1/24 mac=\"00:00:00:01:02:03\" -- add Logical_Router R1 \
+ports @lrp -- lport-add foo rp-foo
+
+ovn-nbctl set Logical_port rp-foo type=router options:router-port=foo \
+addresses=\"00:00:00:01:02:03\"
+
+# Connect alice to R2
+ovn-nbctl -- --id=@lrp create Logical_Router_port name=alice \
+network=172.16.1.1/24 mac=\"00:00:00:01:02:04\" -- add Logical_Router R2 \
+ports @lrp -- lport-add alice rp-alice
+
+ovn-nbctl set Logical_port rp-alice type=router options:router-port=alice \
+addresses=\"00:00:00:01:02:04\"
+
+# Connect bob to R2
+ovn-nbctl -- --id=@lrp create Logical_Router_port name=bob \
+network=172.16.2.1/24 mac=\"00:00:00:01:02:05\" -- add Logical_Router R2 \
+ports @lrp -- lport-add bob rp-bob
+
+ovn-nbctl set Logical_port rp-bob type=router options:router-port=bob \
+addresses=\"00:00:00:01:02:05\"
+
+# Connect R1 to R2
+lrp1_uuid=`ovn-nbctl -- --id=@lrp create Logical_Router_port name=R1_R2 \
+network="20.0.0.1/24" mac=\"00:00:00:02:03:04\" \
+-- add Logical_Router R1 ports @lrp`
+
+lrp2_uuid=`ovn-nbctl -- --id=@lrp create Logical_Router_port name=R2_R1 \
+network="20.0.0.2/24" mac=\"00:00:00:02:03:05\" \
+-- add Logical_Router R2 ports @lrp`
+
+ovn-nbctl set logical_router_port $lrp1_uuid peer="R2_R1"
+ovn-nbctl set logical_router_port $lrp2_uuid peer="R1_R2"
+
+#install static routes
+ovn-nbctl -- --id=@lrt create Logical_Router_Static_Route \
+prefix=172.16.1.0/24 nexthop=20.0.0.2 -- add Logical_Router \
+R1 static_routes @lrt
+
+ovn-nbctl -- --id=@lrt create Logical_Router_Static_Route \
+prefix=172.16.2.0/24 nexthop=20.0.0.2 -- add Logical_Router \
+R1 static_routes @lrt
+
+ovn-nbctl -- --id=@lrt create Logical_Router_Static_Route \
+prefix=192.168.1.0/24 nexthop=20.0.0.1 -- add Logical_Router \
+R2 static_routes @lrt
+
+# Create logical port foo1 in foo
+ovn-nbctl lport-add foo foo1 \
+-- lport-set-addresses foo1 "f0:00:00:01:02:03 192.168.1.2"
+
+# Create logical port alice1 in alice
+ovn-nbctl lport-add alice alice1 \
+-- lport-set-addresses alice1 "f0:00:00:01:02:04 172.16.1.2"
+
+# Create logical port bob1 in bob
+ovn-nbctl lport-add bob bob1 \
+-- lport-set-addresses bob1 "f0:00:00:01:02:05 172.16.2.2"
+
+# Create two hypervisor and create OVS ports corresponding to logical ports.
+net_add n1
+
+sim_add hv1
+as hv1
+ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.1
+ovs-vsctl -- add-port br-int hv1-vif1 -- \
+    set interface hv1-vif1 external-ids:iface-id=foo1 \
+    options:tx_pcap=hv1/vif1-tx.pcap \
+    options:rxq_pcap=hv1/vif1-rx.pcap \
+    ofport-request=1
+
+ovs-vsctl -- add-port br-int hv1-vif2 -- \
+    set interface hv1-vif2 external-ids:iface-id=alice1 \
+    options:tx_pcap=hv1/vif2-tx.pcap \
+    options:rxq_pcap=hv1/vif2-rx.pcap \
+    ofport-request=2
+
+sim_add hv2
+as hv2
+ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.2
+ovs-vsctl -- add-port br-int hv2-vif1 -- \
+    set interface hv2-vif1 external-ids:iface-id=bob1 \
+    options:tx_pcap=hv2/vif1-tx.pcap \
+    options:rxq_pcap=hv2/vif1-rx.pcap \
+    ofport-request=1
+
+
+# Pre-populate the hypervisors' ARP tables so that we don't lose any
+# packets for ARP resolution (native tunneling doesn't queue packets
+# for ARP resolution).
+ovn_populate_arp
+
+# Allow some time for ovn-northd and ovn-controller to catch up.
+# XXX This should be more systematic.
+sleep 1
+
+ip_to_hex() {
+    printf "%02x%02x%02x%02x" "$@"
+}
+trim_zeros() {
+    sed 's/\(00\)\{1,\}$//'
+}
+
+# Send ip packets between foo1 and alice1
+src_mac="f00000010203"
+dst_mac="000000010203"
+src_ip=`ip_to_hex 192 168 1 2`
+dst_ip=`ip_to_hex 172 16 1 2`
+packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
+as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
+
+# Send ip packets between foo1 and bob1
+src_mac="f00000010203"
+dst_mac="000000010203"
+src_ip=`ip_to_hex 192 168 1 2`
+dst_ip=`ip_to_hex 172 16 2 2`
+packet=${dst_mac}${src_mac}08004500001c0000000040110000${src_ip}${dst_ip}0035111100080000
+as hv1 ovs-appctl netdev-dummy/receive hv1-vif1 $packet
+
+echo "---------NB dump-----"
+ovn-nbctl show
+echo "---------------------"
+ovn-nbctl list logical_router
+echo "---------------------"
+ovn-nbctl list logical_router_port
+echo "---------------------"
+
+echo "---------SB dump-----"
+ovn-sbctl list datapath_binding
+echo "---------------------"
+ovn-sbctl list port_binding
+echo "---------------------"
+
+echo "------ hv1 dump ----------"
+as hv1 ovs-ofctl dump-flows br-int
+echo "------ hv2 dump ----------"
+as hv2 ovs-ofctl dump-flows br-int
+
+# Packet to Expect at bob1
+src_mac="000000010205"
+dst_mac="f00000010205"
+src_ip=`ip_to_hex 192 168 1 2`
+dst_ip=`ip_to_hex 172 16 2 2`
+expected=${dst_mac}${src_mac}08004500001c000000003e110200${src_ip}${dst_ip}0035111100080000
+
+$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv2/vif1-tx.pcap | trim_zeros > received.packets
+echo $expected | trim_zeros > expout
+AT_CHECK([cat received.packets], [0], [expout])
+
+# Packet to Expect at alice1
+src_mac="000000010204"
+dst_mac="f00000010204"
+src_ip=`ip_to_hex 192 168 1 2`
+dst_ip=`ip_to_hex 172 16 1 2`
+expected=${dst_mac}${src_mac}08004500001c000000003e110200${src_ip}${dst_ip}0035111100080000
+
+$PYTHON "$top_srcdir/utilities/ovs-pcap.in" hv1/vif2-tx.pcap | trim_zeros > received1.packets
+echo $expected | trim_zeros > expout
+AT_CHECK([cat received1.packets], [0], [expout])
+
+for sim in hv1 hv2; do
+    as $sim
+    OVS_APP_EXIT_AND_WAIT([ovn-controller])
+    OVS_APP_EXIT_AND_WAIT([ovs-vswitchd])
+    OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+done
+
+as ovn-sb
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+as ovn-nb
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+as northd
+OVS_APP_EXIT_AND_WAIT([ovn-northd])
+
+as main
+OVS_APP_EXIT_AND_WAIT([ovs-vswitchd])
+OVS_APP_EXIT_AND_WAIT([ovsdb-server])
+
+AT_CLEANUP