Can you share an example of how this would benefit E/W routing. I'm just
not seeing the specific use case myself out of ignorance.

It'd be great if you could explain how would it work between several ports
in the networks and routers (may be a diagram?) otherwise I can't be really
helpful reviewing :)

Cheers, and thanks for the patience.

On Wed, Sep 20, 2017 at 12:25 PM, Gao Zhenyu <[email protected]>
wrote:

> Thanks for the suggestions!
>
> Not all Logical port has a real ofp_port connect with it. And
> bundle_load/bundle actions need real ovs port.
> Especially in ovn router port, all router port are virtual port which just
> a number/reg in our ovs-flows.
>
> This implement of multipath can seperate ovn east-west traffic, it helps
> dispatch traffic to gateways and routers easily.
>
> For south-north traffic, we can have bundle/bundle_load action to consider
> the remote tunnel up/down status. I would like to make it step by step and
> implement it in my next series patches.
>
> Thanks
> Zhenyu Gao
>
> 2017-09-20 17:53 GMT+08:00 Miguel Angel Ajo Pelayo <[email protected]>:
>
>> I'm not very familiar with multipath implementations,
>>
>> but would it be possible to use bundle( ouput action with hrw algorithm
>> instead of multipath calculation to a register?.
>>
>> I say this, because if you look at lib/multipath.c lib/bundle.c you will
>> find that bundle.c is going to consider the up/down status (slave_enabled
>> check) of the links.
>>
>> That way the controller doesn't need to modify any flow based on link
>> status.
>>
>> On Wed, Sep 20, 2017 at 5:45 AM, Gao Zhenyu <[email protected]>
>> wrote:
>>
>>> Thansk for the questions.
>>>
>>> the multipath_port can be set via ovn-nbctl.
>>> Like : ovn-nbctl   -- --id=@lrt create Logical_Router_Static_Route
>>> ip_prefix=0.0.0.0/0 nexthop=10.88.77.1 multipath_port=[mp1,mp2] -- add
>>> Logical_Router edge1 static_routes @lrt
>>> This patch haven't implement a ovn-nbctl command to configure multipath
>>> routing. Because I am still considering reusing nexthop or output_port(make
>>> them become array entries), and want to collect suggestions on it.
>>>
>>> About the status of next -hop, I would like to introduce bundle_load and
>>> bfd to make it later.
>>>
>>> Thanks
>>> Zhenyu Gao
>>>
>>> 2017-09-20 11:13 GMT+08:00 <[email protected]>:
>>>
>>>> How to configure multipath_port in static_route? I think the the
>>>> multipath
>>>> can be figured out from exist data of static_route, may not need to add
>>>> this multipath_port column.
>>>>
>>>> And I think we should add a status column to indicate the nexthop state.
>>>> When some of nexthop in multipath is down, ovn should change the
>>>> correspond flows.
>>>>
>>>> Thanks.
>>>>
>>>>
>>>>
>>>>
>>>>
>>>> Zhenyu Gao <[email protected]>
>>>> 发件人: [email protected]
>>>> 2017/09/19 19:37
>>>>
>>>>         收件人:        [email protected], [email protected],
>>>> [email protected], [email protected], [email protected],
>>>>         抄送:
>>>>         主题:  [ovs-dev] [PATCH v1 1/3] Add multipath static router in
>>>> OVN northd      and north-db
>>>>
>>>>
>>>> 1. ovn-nb.ovsschema was updated to add new field multipath_port.
>>>> 2. Add multipath feature in ovn-northd part. northd generates multipath
>>>> flows to dispatch traffic by using packet's IP dst address if user set
>>>> Logical_Router_Static_Route's multipath_port with ports.
>>>> 3. Add new table(lr_in_multipath) in ovn-northd's router ingress stages
>>>> to dispatch traffic to ports.
>>>> 4. Add multipath flow in Table 5(lr_in_ip_routing) and store hash result
>>>> into reg0. reg9[2] was used to indicate packet which need dispatching.
>>>> 5. Add multipath feature description in ovn/northd/ovn-northd.8.xml
>>>> and ovn/ovn-nb.xml
>>>>
>>>> Signed-off-by: Zhenyu Gao <[email protected]>
>>>> ---
>>>>  ovn/northd/ovn-northd.8.xml |  67 +++++++++++-
>>>>  ovn/northd/ovn-northd.c     | 245
>>>> ++++++++++++++++++++++++++++++++++++++------
>>>>  ovn/ovn-nb.ovsschema        |   6 +-
>>>>  ovn/ovn-nb.xml              |   9 ++
>>>>  4 files changed, 289 insertions(+), 38 deletions(-)
>>>>
>>>> diff --git a/ovn/northd/ovn-northd.8.xml b/ovn/northd/ovn-northd.8.xml
>>>> index 0d85ec0..b1ce9a9 100644
>>>> --- a/ovn/northd/ovn-northd.8.xml
>>>> +++ b/ovn/northd/ovn-northd.8.xml
>>>> @@ -1598,6 +1598,9 @@ icmp4 {
>>>>        port (ingress table <code>ARP Request</code> will generate an ARP
>>>>        request, if needed, with <code>reg0</code> as the target protocol
>>>>        address and <code>reg1</code> as the source protocol address).
>>>> +      A IP route can be configured that it has multipath to next-hop.
>>>> +      If a packet has multipath to destination, OVN assign the port
>>>> +      index into reg[0] to indicate the packet's output port in table
>>>> 6.
>>>>      </p>
>>>>
>>>>      <p>
>>>> @@ -1617,6 +1620,28 @@ icmp4 {
>>>>
>>>>        <li>
>>>>          <p>
>>>> +          IPv4/IPV6 multipath routing table. For each route to
>>>> IPv4/IPv6
>>>> +          network <var>N</var> with netmask <var>M</var>, on multipath
>>>> port
>>>> +          <var>P</var> with IP address <var>A</var> and Ethernet
>>>> +          address <var>E</var>, a logical flow with match
>>>> +          <code>ip4.dst ==<var>N</var>/<var>M</var></code>,whose
>>>> priority
>>>> +          is the number of 1-bits plus 10 in <var>M</var>,
>>>> +          has the following actions:
>>>> +        </p>
>>>> +
>>>> +        <pre>
>>>> +ip.ttl--;
>>>> +multipath (nw_dst, 0, modulo_n, <var>n_links</var>, 0, reg0);
>>>> +reg9[2] = 1
>>>> +next;
>>>> +        </pre>
>>>> +        <p>
>>>> +          <var>n_links</var> is the number of multipath port.
>>>> +        </p>
>>>> +      </li>
>>>> +
>>>> +      <li>
>>>> +        <p>
>>>>            IPv4 routing table.  For each route to IPv4 network
>>>> <var>N</var> with
>>>>            netmask <var>M</var>, on router port <var>P</var> with IP
>>>> address
>>>>            <var>A</var> and Ethernet
>>>> @@ -1686,7 +1711,43 @@ next;
>>>>        </li>
>>>>      </ul>
>>>>
>>>> -    <h3>Ingress Table 6: ARP/ND Resolution</h3>
>>>> +    <h3>Ingress Table 6: Multipath</h3>
>>>> +    <p>
>>>> +      Any packet taht reaches this table is an IP packet and reg9[2]=1
>>>> +      using the following flows to route to corresponding port. This
>>>> table
>>>> +      implement dispatching by consuming reg0.
>>>> +    </p>
>>>> +
>>>> +    <ul>
>>>> +      <li>
>>>> +        <p>
>>>> +          A packet with netmask <var>M</var>, IP address <var>A</var>
>>>> and
>>>> +          <code>reg9[2] = 1</code>, whose priority above 1 has
>>>> following
>>>> +          actions:
>>>> +        </p>
>>>> +
>>>> +        <pre>
>>>> +reg0 = <var>G</var>;
>>>> +reg1 = <var>A</var>;
>>>> +eth.src = <var>E</var>;
>>>> +outport = <var>P</var>;
>>>> +flags.loopback = 1;
>>>> +next;
>>>> +        </pre>
>>>> +
>>>> +        <p>
>>>> +          <var>G</var> is the gateway IP address. <var>A</var>,
>>>> <var>E</var>
>>>> +          and <var>P</var> are the values that were described in
>>>> multipath
>>>> +          routeing in table 5
>>>> +        </p>
>>>> +
>>>> +        <p>
>>>> +          A priority-0 logical flow with match has actions
>>>> <code>next;</code>.
>>>> +        </p>
>>>> +      </li>
>>>> +    </ul>
>>>> +
>>>> +    <h3>Ingress Table 7: ARP/ND Resolution</h3>
>>>>
>>>>      <p>
>>>>        Any packet that reaches this table is an IP packet whose next-hop
>>>> @@ -1779,7 +1840,7 @@ next;
>>>>        </li>
>>>>      </ul>
>>>>
>>>> -    <h3>Ingress Table 7: Gateway Redirect</h3>
>>>> +    <h3>Ingress Table 8: Gateway Redirect</h3>
>>>>
>>>>      <p>
>>>>        For distributed logical routers where one of the logical router
>>>> @@ -1836,7 +1897,7 @@ next;
>>>>        </li>
>>>>      </ul>
>>>>
>>>> -    <h3>Ingress Table 8: ARP Request</h3>
>>>> +    <h3>Ingress Table 9: ARP Request</h3>
>>>>
>>>>      <p>
>>>>        In the common case where the Ethernet destination has been
>>>> resolved, this
>>>> diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
>>>> index 49e4ac3..44d1fd4 100644
>>>> --- a/ovn/northd/ovn-northd.c
>>>> +++ b/ovn/northd/ovn-northd.c
>>>> @@ -135,9 +135,10 @@ enum ovn_stage {
>>>>      PIPELINE_STAGE(ROUTER, IN,  UNSNAT,      3, "lr_in_unsnat")       \
>>>>      PIPELINE_STAGE(ROUTER, IN,  DNAT,        4, "lr_in_dnat")         \
>>>>      PIPELINE_STAGE(ROUTER, IN,  IP_ROUTING,  5, "lr_in_ip_routing")   \
>>>> -    PIPELINE_STAGE(ROUTER, IN,  ARP_RESOLVE, 6, "lr_in_arp_resolve")  \
>>>> -    PIPELINE_STAGE(ROUTER, IN,  GW_REDIRECT, 7, "lr_in_gw_redirect")  \
>>>> -    PIPELINE_STAGE(ROUTER, IN,  ARP_REQUEST, 8, "lr_in_arp_request")  \
>>>> +    PIPELINE_STAGE(ROUTER, IN,  MULTIPATH,   6, "lr_in_multipath")    \
>>>> +    PIPELINE_STAGE(ROUTER, IN,  ARP_RESOLVE, 7, "lr_in_arp_resolve")  \
>>>> +    PIPELINE_STAGE(ROUTER, IN,  GW_REDIRECT, 8, "lr_in_gw_redirect")  \
>>>> +    PIPELINE_STAGE(ROUTER, IN,  ARP_REQUEST, 9, "lr_in_arp_request")  \
>>>>                                                                        \
>>>>      /* Logical router egress stages. */                               \
>>>>      PIPELINE_STAGE(ROUTER, OUT, UNDNAT,    0, "lr_out_undnat")        \
>>>> @@ -173,6 +174,11 @@ enum ovn_stage {
>>>>   * one of the logical router's own IP addresses. */
>>>>  #define REGBIT_EGRESS_LOOPBACK  "reg9[1]"
>>>>
>>>> +/* Indicate multipath action has process this packet and store hash
>>>> result
>>>> + * into other regX. Should consume the hash result to determin the
>>>> right
>>>> + * output port. */
>>>> +#define REGBIT_MULTIPATH "reg9[2]"
>>>> +
>>>>  /* Returns an "enum ovn_stage" built from the arguments. */
>>>>  static enum ovn_stage
>>>>  ovn_stage_build(enum ovn_datapath_type dp_type, enum ovn_pipeline
>>>> pipeline,
>>>> @@ -4142,72 +4148,165 @@ add_route(struct hmap *lflows, const struct
>>>> ovn_port *op,
>>>>  }
>>>>
>>>>  static void
>>>> -build_static_route_flow(struct hmap *lflows, struct ovn_datapath *od,
>>>> -                        struct hmap *ports,
>>>> -                        const struct nbrec_logical_router_static_route
>>>> *route)
>>>> +add_multipath_route(struct hmap *lflows, uint32_t port_num,
>>>> +                    struct ovn_port **out_ports,
>>>> +                    const char **lrp_addr_s,
>>>> +                    struct ovn_datapath *od,
>>>> +                    const char *network_s, int plen,
>>>> +                    const char *gateway, const char *policy)
>>>> +{
>>>> +    bool is_ipv4 = strchr(network_s, '.') ? true : false;
>>>> +    struct ds match = DS_EMPTY_INITIALIZER;
>>>> +    const char *dir;
>>>> +    uint16_t priority;
>>>> +
>>>> +    if (policy && !strcmp(policy, "src-ip")) {
>>>> +        dir = "src";
>>>> +        priority = plen * 2;
>>>> +    } else {
>>>> +        dir = "dst";
>>>> +        priority = (plen * 2) + 1;
>>>> +    }
>>>> +
>>>> +    /* Set higer priority than regular route. */
>>>> +    priority += 10;
>>>> +
>>>> +    ds_put_format(&match, "ip%s.%s == %s/%d", is_ipv4 ? "4" : "6", dir,
>>>> +                  network_s, plen);
>>>> +
>>>> +    struct ds actions = DS_EMPTY_INITIALIZER;
>>>> +
>>>> +    ds_put_format(&actions, "ip.ttl--; ");
>>>> +    ds_put_format(&actions,
>>>> +                  "multipath (nw_dst, 0, modulo_n, %u, 0, reg0); "
>>>> +                  "%s = 1; "
>>>> +                  "next;",
>>>> +                  port_num, REGBIT_MULTIPATH);
>>>> +
>>>> +    /* The priority here is calculated to implement
>>>> longest-prefix-match
>>>> +     * routing. */
>>>> +    ovn_lflow_add(lflows, od, S_ROUTER_IN_IP_ROUTING, priority,
>>>> +                  ds_cstr(&match), ds_cstr(&actions));
>>>> +
>>>> +    for (int i = 0; i < port_num; i++) {
>>>> +        struct ds mp_match = DS_EMPTY_INITIALIZER;
>>>> +        struct ds mp_actions = DS_EMPTY_INITIALIZER;
>>>> +
>>>> +        ds_put_format(&mp_match, "%s == 1 && reg0 == %d && ",
>>>> +                      REGBIT_MULTIPATH, i);
>>>> +        ds_put_format(&mp_match, "ip%s.%s == %s/%d",
>>>> +                      is_ipv4 ? "4" : "6", dir,
>>>> +                      network_s, plen);
>>>> +
>>>> +        ds_put_format(&mp_actions, "%sreg0 = ", is_ipv4 ? "" : "xx");
>>>> +        if (gateway) {
>>>> +            ds_put_cstr(&mp_actions, gateway);
>>>> +        } else {
>>>> +            ds_put_format(&mp_actions, "ip%s.dst", is_ipv4 ? "4" :
>>>> "6");
>>>> +        }
>>>> +
>>>> +        ds_put_format(&mp_actions, "; "
>>>> +                      "%sreg1 = %s; "
>>>> +                      "eth.src = %s; "
>>>> +                      "outport = %s; "
>>>> +                      "flags.loopback = 1; "
>>>> +                      "next;",
>>>> +                      is_ipv4 ? "" : "xx",
>>>> +                      lrp_addr_s[i],
>>>> +                      out_ports[i]->lrp_networks.ea_s,
>>>> +                      out_ports[i]->json_key);
>>>> +
>>>> +        /* Add flow in table 6 to determin the right output port
>>>> +         * for this traffic. */
>>>> +        ovn_lflow_add(lflows, od, S_ROUTER_IN_MULTIPATH, priority,
>>>> +                      ds_cstr(&mp_match), ds_cstr(&mp_actions));
>>>> +        ds_destroy(&mp_match);
>>>> +        ds_destroy(&mp_actions);
>>>> +    }
>>>> +    ds_destroy(&match);
>>>> +    ds_destroy(&actions);
>>>> +}
>>>> +
>>>> +static bool
>>>> +verify_nexthop_prefix(const struct nbrec_logical_router_static_route
>>>> *route,
>>>> +                      bool *is_ipv4, char **prefix_s, unsigned int
>>>> *plen)
>>>>  {
>>>>      ovs_be32 nexthop;
>>>> -    const char *lrp_addr_s = NULL;
>>>> -    unsigned int plen;
>>>> -    bool is_ipv4;
>>>>
>>>>      /* Verify that the next hop is an IP address with an all-ones mask.
>>>> */
>>>> -    char *error = ip_parse_cidr(route->nexthop, &nexthop, &plen);
>>>> +    char *error = ip_parse_cidr(route->nexthop, &nexthop, plen);
>>>>      if (!error) {
>>>> -        if (plen != 32) {
>>>> +        if (*plen != 32) {
>>>>              static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5,
>>>> 1);
>>>>              VLOG_WARN_RL(&rl, "bad next hop mask %s", route->nexthop);
>>>> -            return;
>>>> +            return false;
>>>>          }
>>>> -        is_ipv4 = true;
>>>> +        *is_ipv4 = true;
>>>>      } else {
>>>>          free(error);
>>>>
>>>>          struct in6_addr ip6;
>>>> -        error = ipv6_parse_cidr(route->nexthop, &ip6, &plen);
>>>> +        error = ipv6_parse_cidr(route->nexthop, &ip6, plen);
>>>>          if (!error) {
>>>> -            if (plen != 128) {
>>>> +            if (*plen != 128) {
>>>>                  static struct vlog_rate_limit rl =
>>>> VLOG_RATE_LIMIT_INIT(5, 1);
>>>>                  VLOG_WARN_RL(&rl, "bad next hop mask %s",
>>>> route->nexthop);
>>>> -                return;
>>>> +                return false;
>>>>              }
>>>> -            is_ipv4 = false;
>>>> +            *is_ipv4 = false;
>>>>          } else {
>>>>              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;
>>>> +            return false;
>>>>          }
>>>>      }
>>>>
>>>> -    char *prefix_s;
>>>> -    if (is_ipv4) {
>>>> +    if (*is_ipv4) {
>>>>          ovs_be32 prefix;
>>>>          /* Verify that ip prefix is a valid IPv4 address. */
>>>> -        error = ip_parse_cidr(route->ip_prefix, &prefix, &plen);
>>>> +        error = ip_parse_cidr(route->ip_prefix, &prefix, plen);
>>>>          if (error) {
>>>>              static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5,
>>>> 1);
>>>>              VLOG_WARN_RL(&rl, "bad 'ip_prefix' in static routes %s",
>>>>                           route->ip_prefix);
>>>>              free(error);
>>>> -            return;
>>>> +            return false;
>>>>          }
>>>> -        prefix_s = xasprintf(IP_FMT, IP_ARGS(prefix &
>>>> be32_prefix_mask(plen)));
>>>> +        *prefix_s = xasprintf(IP_FMT, IP_ARGS(prefix
>>>> +                                              &
>>>> be32_prefix_mask(*plen)));
>>>>      } else {
>>>>          /* Verify that ip prefix is a valid IPv6 address. */
>>>>          struct in6_addr prefix;
>>>> -        error = ipv6_parse_cidr(route->ip_prefix, &prefix, &plen);
>>>> +        error = ipv6_parse_cidr(route->ip_prefix, &prefix, plen);
>>>>          if (error) {
>>>>              static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5,
>>>> 1);
>>>>              VLOG_WARN_RL(&rl, "bad 'ip_prefix' in static routes %s",
>>>>                           route->ip_prefix);
>>>>              free(error);
>>>> -            return;
>>>> +            return false;
>>>>          }
>>>> -        struct in6_addr mask = ipv6_create_mask(plen);
>>>> +        struct in6_addr mask = ipv6_create_mask(*plen);
>>>>          struct in6_addr network = ipv6_addr_bitand(&prefix, &mask);
>>>> -        prefix_s = xmalloc(INET6_ADDRSTRLEN);
>>>> -        inet_ntop(AF_INET6, &network, prefix_s, INET6_ADDRSTRLEN);
>>>> +        *prefix_s = xmalloc(INET6_ADDRSTRLEN);
>>>> +        inet_ntop(AF_INET6, &network, *prefix_s, INET6_ADDRSTRLEN);
>>>> +    }
>>>> +
>>>> +    return true;
>>>> +}
>>>> +
>>>> +static void
>>>> +build_static_route_flow(struct hmap *lflows, struct ovn_datapath *od,
>>>> +                        struct hmap *ports,
>>>> +                        const struct nbrec_logical_router_static_route
>>>> *route)
>>>> +{
>>>> +    const char *lrp_addr_s = NULL;
>>>> +    unsigned int plen;
>>>> +    bool is_ipv4;
>>>> +    char *prefix_s = NULL;
>>>> +
>>>> +    if (!verify_nexthop_prefix(route, &is_ipv4, &prefix_s, &plen)) {
>>>> +        return;
>>>>      }
>>>>
>>>>      /* Find the outgoing port. */
>>>> @@ -4270,7 +4369,75 @@ build_static_route_flow(struct hmap *lflows,
>>>> struct
>>>> ovn_datapath *od,
>>>>                policy);
>>>>
>>>>  free_prefix_s:
>>>> -    free(prefix_s);
>>>> +    if (prefix_s) {
>>>> +        free(prefix_s);
>>>> +    }
>>>> +}
>>>> +
>>>> +static void
>>>> +build_multipath_flow(struct hmap *lflows, struct ovn_datapath *od,
>>>> +                     struct hmap *ports,
>>>> +                     const struct nbrec_logical_router_static_route
>>>> *route)
>>>> +{
>>>> +    unsigned int plen;
>>>> +    bool is_ipv4;
>>>> +    char *prefix_s = NULL;
>>>> +
>>>> +    if (!verify_nexthop_prefix(route, &is_ipv4, &prefix_s, &plen)) {
>>>> +        return;
>>>> +    }
>>>> +
>>>> +    /* Find the outgoing port. */
>>>> +    struct ovn_port **out_ports = xmalloc(route->n_multipath_port *
>>>> +                                             sizeof(struct ovn_port
>>>> *));
>>>> +    const char **lrp_addr_s = xmalloc(route->n_multipath_port *
>>>> +                                         sizeof(const char *));
>>>> +    for (int i = 0; i < route->n_multipath_port; i++) {
>>>> +        // TODO May need to consider some ports are not found?
>>>> +        out_ports[i] = ovn_port_find(ports, route->multipath_port[i]);
>>>> +        if (!out_ports[i]) {
>>>> +            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5,
>>>> 1);
>>>> +            VLOG_WARN_RL(&rl, "Bad out port %s for static route %s",
>>>> +                         route->multipath_port[i], route->ip_prefix);
>>>> +            goto free_ports_lrp_addr;
>>>> +        }
>>>> +
>>>> +        lrp_addr_s[i] = find_lrp_member_ip(out_ports[i],
>>>> route->nexthop);
>>>> +        if (!lrp_addr_s[i]) {
>>>> +            if (is_ipv4) {
>>>> +                if (out_ports[i]->lrp_networks.n_ipv4_addrs) {
>>>> +                    lrp_addr_s[i] = out_ports[i]->
>>>> + lrp_networks.ipv4_addrs[0].addr_s;
>>>> +                }
>>>> +            } else {
>>>> +                if (out_ports[i]->lrp_networks.n_ipv6_addrs) {
>>>> +                    lrp_addr_s[i] = out_ports[i]->
>>>> + lrp_networks.ipv6_addrs[0].addr_s;
>>>> +                }
>>>> +            }
>>>> +        }
>>>> +        if (!lrp_addr_s[i]) {
>>>> +            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5,
>>>> 1);
>>>> +            VLOG_WARN_RL(&rl,
>>>> +                         "%s has no path for static route %s; next hop
>>>> %s",
>>>> +                         route->multipath_port[i], route->ip_prefix,
>>>> +                         route->nexthop);
>>>> +            goto free_ports_lrp_addr;
>>>> +        }
>>>> +    }
>>>> +
>>>> +
>>>> +    char *policy = route->policy ? route->policy : "dst-ip";
>>>> +    add_multipath_route(lflows, route->n_multipath_port,
>>>> +                        out_ports, lrp_addr_s, od,
>>>> +                        prefix_s, plen, route->nexthop, policy);
>>>> +
>>>> +free_ports_lrp_addr:
>>>> +    free(out_ports);
>>>> +    free(lrp_addr_s);
>>>> +    if (prefix_s) {
>>>> +        free(prefix_s);
>>>> +    }
>>>>  }
>>>>
>>>>  static void
>>>> @@ -5344,7 +5511,7 @@ build_lrouter_flows(struct hmap *datapaths, struct
>>>> hmap *ports,
>>>>          }
>>>>      }
>>>>
>>>> -    /* Convert the static routes to flows. */
>>>> +    /* Convert the static routes and multipath route to flows. */
>>>>      HMAP_FOR_EACH (od, key_node, datapaths) {
>>>>          if (!od->nbr) {
>>>>              continue;
>>>> @@ -5355,12 +5522,24 @@ build_lrouter_flows(struct hmap *datapaths,
>>>> struct
>>>> hmap *ports,
>>>>
>>>>              route = od->nbr->static_routes[i];
>>>>              build_static_route_flow(lflows, od, ports, route);
>>>> +            /* Logical router ingress table 5-6: Multipath Routing.
>>>> +             *
>>>> +             * If router has configured a traffic has multiple paths
>>>> +             * to destination. The right output port should be firgured
>>>> +             * out by computing IP packet's header */
>>>> +            if (route->n_multipath_port > 1) {
>>>> +                /* Generate multipath routes in table 5,6 for
>>>> +                 * dedicated traffic */
>>>> +                build_multipath_flow(lflows, od, ports, route);
>>>> +            }
>>>>          }
>>>> +        /* Packets are allowed by default in table 6. */
>>>> +        ovn_lflow_add(lflows, od, S_ROUTER_IN_MULTIPATH, 0, "1",
>>>> "next;");
>>>>      }
>>>>
>>>>      /* XXX destination unreachable */
>>>>
>>>> -    /* Local router ingress table 6: ARP Resolution.
>>>> +    /* Local router ingress table 7: ARP Resolution.
>>>>       *
>>>>       * Any packet that reaches this table is an IP packet whose
>>>> next-hop
>>>> IP
>>>>       * address is in reg0. (ip4.dst is the final destination.) This
>>>> table
>>>> @@ -5555,7 +5734,7 @@ build_lrouter_flows(struct hmap *datapaths, struct
>>>> hmap *ports,
>>>>                        "get_nd(outport, xxreg0); next;");
>>>>      }
>>>>
>>>> -    /* Logical router ingress table 7: Gateway redirect.
>>>> +    /* Logical router ingress table 8: Gateway redirect.
>>>>       *
>>>>       * For traffic with outport equal to the l3dgw_port
>>>>       * on a distributed router, this table redirects a subset
>>>> @@ -5595,7 +5774,7 @@ build_lrouter_flows(struct hmap *datapaths, struct
>>>> hmap *ports,
>>>>          ovn_lflow_add(lflows, od, S_ROUTER_IN_GW_REDIRECT, 0, "1",
>>>> "next;");
>>>>      }
>>>>
>>>> -    /* Local router ingress table 8: ARP request.
>>>> +    /* Local router ingress table 9: ARP request.
>>>>       *
>>>>       * In the common case where the Ethernet destination has been
>>>> resolved,
>>>>       * this table outputs the packet (priority 0).  Otherwise, it
>>>> composes
>>>> diff --git a/ovn/ovn-nb.ovsschema b/ovn/ovn-nb.ovsschema
>>>> index a077bfb..b8bdd42 100644
>>>> --- a/ovn/ovn-nb.ovsschema
>>>> +++ b/ovn/ovn-nb.ovsschema
>>>> @@ -1,7 +1,7 @@
>>>>  {
>>>>      "name": "OVN_Northbound",
>>>>      "version": "5.8.0",
>>>> -    "cksum": "2812300190 <(281)%20230-0190> 16766",
>>>> +    "cksum": "1967092589 16903",
>>>>      "tables": {
>>>>          "NB_Global": {
>>>>              "columns": {
>>>> @@ -235,7 +235,9 @@
>>>>
>>>> "dst-ip"]]},
>>>>                                      "min": 0, "max": 1}},
>>>>                  "nexthop": {"type": "string"},
>>>> -                "output_port": {"type": {"key": "string", "min": 0,
>>>> "max": 1}}},
>>>> +                "output_port": {"type": {"key": "string", "min": 0,
>>>> "max": 1}},
>>>> +                "multipath_port": {"type": {"key": "string", "min": 0,
>>>> +                                            "max": "unlimited"}}},
>>>>              "isRoot": false},
>>>>          "NAT": {
>>>>              "columns": {
>>>> diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml
>>>> index 9869d7e..15feb97 100644
>>>> --- a/ovn/ovn-nb.xml
>>>> +++ b/ovn/ovn-nb.xml
>>>> @@ -1487,6 +1487,15 @@
>>>>          address as the one via which the <ref column="nexthop"/> is
>>>> reachable.
>>>>        </p>
>>>>      </column>
>>>> +    <column name="multipath_port">
>>>> +      <p>
>>>> +        The name of the <ref table="Logical_Router_Port"/> via which
>>>> the
>>>> packet
>>>> +        needs to be sent out. When it contains more than two ports, it
>>>> means
>>>> +        packet has multiple candidate output ports. OVN uses the packet
>>>> header
>>>> +        to determin which port the packet would be delivered to.
>>>> +        Currently, OVN consumes destination IP address to figure out
>>>> port.
>>>> +      </p>
>>>> +    </column>
>>>>    </table>
>>>>
>>>>    <table name="NAT" title="NAT rules">
>>>> --
>>>> 1.8.3.1
>>>>
>>>> _______________________________________________
>>>> dev mailing list
>>>> [email protected]
>>>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>>>>
>>>>
>>>> _______________________________________________
>>>> dev mailing list
>>>> [email protected]
>>>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>>>>
>>>
>>>
>>
>
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to