On Fri, Apr 07, 2017 at 04:12:48PM +0300, Roi Dayan wrote:
> From: Paul Blakey <[email protected]>
> 
> Add tc flower interface that will be used to offload flows via tc
> flower classifier. Depending on the flag used (skip_sw/hw) flower
> will pass those to HW or handle them itself.
> Move some tc related functions from netdev-linux.c to tc.c
> 
> Co-authored-by: Shahar Klein <[email protected]>
> Signed-off-by: Shahar Klein <[email protected]>
> Signed-off-by: Paul Blakey <[email protected]>
> Reviewed-by: Roi Dayan <[email protected]>
> Reviewed-by: Simon Horman <[email protected]>
> ---
>  lib/automake.mk    |    2 +
>  lib/netdev-linux.c |  164 ++------
>  lib/tc.c           | 1109 
> ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  lib/tc.h           |  128 ++++++
>  4 files changed, 1279 insertions(+), 124 deletions(-)
>  create mode 100644 lib/tc.c
>  create mode 100644 lib/tc.h
> 
> diff --git a/lib/automake.mk b/lib/automake.mk
> index b266af1..7c1b67b 100644
> --- a/lib/automake.mk
> +++ b/lib/automake.mk
> @@ -352,6 +352,8 @@ if LINUX
>  lib_libopenvswitch_la_SOURCES += \
>       lib/dpif-netlink.c \
>       lib/dpif-netlink.h \
> +     lib/tc.h \
> +     lib/tc.c \
>       lib/if-notifier.c \
>       lib/if-notifier.h \
>       lib/netdev-linux.c \
> diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
> index 9ff1333..085f530 100644
> --- a/lib/netdev-linux.c
> +++ b/lib/netdev-linux.c
> @@ -29,8 +29,6 @@
>  #include <linux/types.h>
>  #include <linux/ethtool.h>
>  #include <linux/mii.h>
> -#include <linux/pkt_cls.h>
> -#include <linux/pkt_sched.h>
>  #include <linux/rtnetlink.h>
>  #include <linux/sockios.h>
>  #include <sys/types.h>
> @@ -74,6 +72,7 @@
>  #include "unaligned.h"
>  #include "openvswitch/vlog.h"
>  #include "util.h"
> +#include "tc.h"
>  
>  VLOG_DEFINE_THIS_MODULE(netdev_linux);
>  
> @@ -434,18 +433,14 @@ static const struct tc_ops *const tcs[] = {
>      NULL
>  };
>  
> -static unsigned int tc_make_handle(unsigned int major, unsigned int minor);
> -static unsigned int tc_get_major(unsigned int handle);
> -static unsigned int tc_get_minor(unsigned int handle);
> -
>  static unsigned int tc_ticks_to_bytes(unsigned int rate, unsigned int ticks);
>  static unsigned int tc_bytes_to_ticks(unsigned int rate, unsigned int size);
>  static unsigned int tc_buffer_per_jiffy(unsigned int rate);
> +static struct tcmsg *netdev_linux_tc_make_request(const struct netdev *,
> +                                                  int type,
> +                                                  unsigned int flags,
> +                                                  struct ofpbuf *);
>  
> -static struct tcmsg *tc_make_request(const struct netdev *, int type,
> -                                     unsigned int flags, struct ofpbuf *);
> -static int tc_transact(struct ofpbuf *request, struct ofpbuf **replyp);
> -static int tc_add_del_ingress_qdisc(struct netdev *netdev, bool add);
>  static int tc_add_policer(struct netdev *,
>                            uint32_t kbits_rate, uint32_t kbits_burst);
>  
> @@ -2055,12 +2050,18 @@ netdev_linux_set_policing(struct netdev *netdev_,
>      struct netdev_linux *netdev = netdev_linux_cast(netdev_);
>      const char *netdev_name = netdev_get_name(netdev_);
>      int error;
> +    int ifindex;
>  
>      kbits_burst = (!kbits_rate ? 0       /* Force to 0 if no rate specified. 
> */
>                     : !kbits_burst ? 8000 /* Default to 8000 kbits if 0. */
>                     : kbits_burst);       /* Stick with user-specified value. 
> */
>  
>      ovs_mutex_lock(&netdev->mutex);
> +    error = get_ifindex(netdev_, &ifindex);
> +    if (error) {
> +        goto out;
> +    }
> +
>      if (netdev->cache_valid & VALID_POLICING) {
>          error = netdev->netdev_policing_error;
>          if (error || (netdev->kbits_rate == kbits_rate &&
> @@ -2073,7 +2074,7 @@ netdev_linux_set_policing(struct netdev *netdev_,
>  
>      COVERAGE_INC(netdev_set_policing);
>      /* Remove any existing ingress qdisc. */
> -    error = tc_add_del_ingress_qdisc(netdev_, false);
> +    error = tc_add_del_ingress_qdisc(ifindex, false);
>      if (error) {
>          VLOG_WARN_RL(&rl, "%s: removing policing failed: %s",
>                       netdev_name, ovs_strerror(error));
> @@ -2081,7 +2082,7 @@ netdev_linux_set_policing(struct netdev *netdev_,
>      }
>  
>      if (kbits_rate) {
> -        error = tc_add_del_ingress_qdisc(netdev_, true);
> +        error = tc_add_del_ingress_qdisc(ifindex, true);
>          if (error) {
>              VLOG_WARN_RL(&rl, "%s: adding policing qdisc failed: %s",
>                           netdev_name, ovs_strerror(error));
> @@ -2350,7 +2351,7 @@ start_queue_dump(const struct netdev *netdev, struct 
> queue_dump_state *state)
>      struct ofpbuf request;
>      struct tcmsg *tcmsg;
>  
> -    tcmsg = tc_make_request(netdev, RTM_GETTCLASS, 0, &request);
> +    tcmsg = netdev_linux_tc_make_request(netdev, RTM_GETTCLASS, 0, &request);
>      if (!tcmsg) {
>          return false;
>      }
> @@ -2909,8 +2910,8 @@ codel_setup_qdisc__(struct netdev *netdev, uint32_t 
> target, uint32_t limit,
>  
>      tc_del_qdisc(netdev);
>  
> -    tcmsg = tc_make_request(netdev, RTM_NEWQDISC,
> -                            NLM_F_EXCL | NLM_F_CREATE, &request);
> +    tcmsg = netdev_linux_tc_make_request(netdev, RTM_NEWQDISC,
> +                                         NLM_F_EXCL | NLM_F_CREATE, 
> &request);
>      if (!tcmsg) {
>          return ENODEV;
>      }
> @@ -3127,8 +3128,8 @@ fqcodel_setup_qdisc__(struct netdev *netdev, uint32_t 
> target, uint32_t limit,
>  
>      tc_del_qdisc(netdev);
>  
> -    tcmsg = tc_make_request(netdev, RTM_NEWQDISC,
> -                            NLM_F_EXCL | NLM_F_CREATE, &request);
> +    tcmsg = netdev_linux_tc_make_request(netdev, RTM_NEWQDISC,
> +                                         NLM_F_EXCL | NLM_F_CREATE, 
> &request);
>      if (!tcmsg) {
>          return ENODEV;
>      }
> @@ -3351,8 +3352,8 @@ sfq_setup_qdisc__(struct netdev *netdev, uint32_t 
> quantum, uint32_t perturb)
>  
>      tc_del_qdisc(netdev);
>  
> -    tcmsg = tc_make_request(netdev, RTM_NEWQDISC,
> -                            NLM_F_EXCL | NLM_F_CREATE, &request);
> +    tcmsg = netdev_linux_tc_make_request(netdev, RTM_NEWQDISC,
> +                                         NLM_F_EXCL | NLM_F_CREATE, 
> &request);
>      if (!tcmsg) {
>          return ENODEV;
>      }
> @@ -3538,8 +3539,8 @@ htb_setup_qdisc__(struct netdev *netdev)
>  
>      tc_del_qdisc(netdev);
>  
> -    tcmsg = tc_make_request(netdev, RTM_NEWQDISC,
> -                            NLM_F_EXCL | NLM_F_CREATE, &request);
> +    tcmsg = netdev_linux_tc_make_request(netdev, RTM_NEWQDISC,
> +                                         NLM_F_EXCL | NLM_F_CREATE, 
> &request);
>      if (!tcmsg) {
>          return ENODEV;
>      }
> @@ -3592,7 +3593,8 @@ htb_setup_class__(struct netdev *netdev, unsigned int 
> handle,
>      opt.cbuffer = tc_calc_buffer(opt.ceil.rate, mtu, class->burst);
>      opt.prio = class->priority;
>  
> -    tcmsg = tc_make_request(netdev, RTM_NEWTCLASS, NLM_F_CREATE, &request);
> +    tcmsg = netdev_linux_tc_make_request(netdev, RTM_NEWTCLASS,
> +                                         NLM_F_CREATE, &request);
>      if (!tcmsg) {
>          return ENODEV;
>      }
> @@ -4201,13 +4203,11 @@ hfsc_setup_qdisc__(struct netdev * netdev)
>  
>      tc_del_qdisc(netdev);
>  
> -    tcmsg = tc_make_request(netdev, RTM_NEWQDISC,
> -                            NLM_F_EXCL | NLM_F_CREATE, &request);
> -
> +    tcmsg = netdev_linux_tc_make_request(netdev, RTM_NEWQDISC,
> +                                         NLM_F_EXCL | NLM_F_CREATE, 
> &request);
>      if (!tcmsg) {
>          return ENODEV;
>      }
> -
>      tcmsg->tcm_handle = tc_make_handle(1, 0);
>      tcmsg->tcm_parent = TC_H_ROOT;
>  
> @@ -4234,12 +4234,11 @@ hfsc_setup_class__(struct netdev *netdev, unsigned 
> int handle,
>      struct ofpbuf request;
>      struct tc_service_curve min, max;
>  
> -    tcmsg = tc_make_request(netdev, RTM_NEWTCLASS, NLM_F_CREATE, &request);
> -
> +    tcmsg = netdev_linux_tc_make_request(netdev, RTM_NEWTCLASS,
> +                                         NLM_F_CREATE, &request);
>      if (!tcmsg) {
>          return ENODEV;
>      }
> -
>      tcmsg->tcm_handle = handle;
>      tcmsg->tcm_parent = parent;
>  
> @@ -4610,102 +4609,17 @@ static double ticks_per_s;
>   */
>  static unsigned int buffer_hz;
>  
> -/* Returns tc handle 'major':'minor'. */
> -static unsigned int
> -tc_make_handle(unsigned int major, unsigned int minor)
> -{
> -    return TC_H_MAKE(major << 16, minor);
> -}
> -
> -/* Returns the major number from 'handle'. */
> -static unsigned int
> -tc_get_major(unsigned int handle)
> -{
> -    return TC_H_MAJ(handle) >> 16;
> -}
> -
> -/* Returns the minor number from 'handle'. */
> -static unsigned int
> -tc_get_minor(unsigned int handle)
> -{
> -    return TC_H_MIN(handle);
> -}
> -
>  static struct tcmsg *
> -tc_make_request(const struct netdev *netdev, int type, unsigned int flags,
> -                struct ofpbuf *request)
> +netdev_linux_tc_make_request(const struct netdev *netdev, int type,
> +                             unsigned int flags, struct ofpbuf *request)
>  {
> -    struct tcmsg *tcmsg;
>      int ifindex;
> -    int error;
>  
> -    error = get_ifindex(netdev, &ifindex);
> -    if (error) {
> +    if (get_ifindex(netdev, &ifindex)) {
>          return NULL;
>      }
>  
> -    ofpbuf_init(request, 512);
> -    nl_msg_put_nlmsghdr(request, sizeof *tcmsg, type, NLM_F_REQUEST | flags);
> -    tcmsg = ofpbuf_put_zeros(request, sizeof *tcmsg);
> -    tcmsg->tcm_family = AF_UNSPEC;
> -    tcmsg->tcm_ifindex = ifindex;
> -    /* Caller should fill in tcmsg->tcm_handle. */
> -    /* Caller should fill in tcmsg->tcm_parent. */
> -
> -    return tcmsg;
> -}
> -
> -static int
> -tc_transact(struct ofpbuf *request, struct ofpbuf **replyp)
> -{
> -    int error = nl_transact(NETLINK_ROUTE, request, replyp);
> -    ofpbuf_uninit(request);
> -    return error;
> -}
> -
> -/* Adds or deletes a root ingress qdisc on 'netdev'.  We use this for
> - * policing configuration.
> - *
> - * This function is equivalent to running the following when 'add' is true:
> - *     /sbin/tc qdisc add dev <devname> handle ffff: ingress
> - *
> - * This function is equivalent to running the following when 'add' is false:
> - *     /sbin/tc qdisc del dev <devname> handle ffff: ingress
> - *
> - * The configuration and stats may be seen with the following command:
> - *     /sbin/tc -s qdisc show dev <devname>
> - *
> - * Returns 0 if successful, otherwise a positive errno value.
> - */
> -static int
> -tc_add_del_ingress_qdisc(struct netdev *netdev, bool add)
> -{
> -    struct ofpbuf request;
> -    struct tcmsg *tcmsg;
> -    int error;
> -    int type = add ? RTM_NEWQDISC : RTM_DELQDISC;
> -    int flags = add ? NLM_F_EXCL | NLM_F_CREATE : 0;
> -
> -    tcmsg = tc_make_request(netdev, type, flags, &request);
> -    if (!tcmsg) {
> -        return ENODEV;
> -    }
> -    tcmsg->tcm_handle = tc_make_handle(0xffff, 0);
> -    tcmsg->tcm_parent = TC_H_INGRESS;
> -    nl_msg_put_string(&request, TCA_KIND, "ingress");
> -    nl_msg_put_unspec(&request, TCA_OPTIONS, NULL, 0);
> -
> -    error = tc_transact(&request, NULL);
> -    if (error) {
> -        /* If we're deleting the qdisc, don't worry about some of the
> -         * error conditions. */
> -        if (!add && (error == ENOENT || error == EINVAL)) {
> -            return 0;
> -        }
> -        return error;
> -    }
> -
> -    return 0;
> +    return tc_make_request(ifindex, type, flags, request);
>  }
>  
>  /* Adds a policer to 'netdev' with a rate of 'kbits_rate' and a burst size
> @@ -4748,8 +4662,8 @@ tc_add_policer(struct netdev *netdev,
>      tc_police.burst = tc_bytes_to_ticks(
>          tc_police.rate.rate, MIN(UINT32_MAX / 1024, kbits_burst) * 1024 / 8);
>  
> -    tcmsg = tc_make_request(netdev, RTM_NEWTFILTER,
> -                            NLM_F_EXCL | NLM_F_CREATE, &request);
> +    tcmsg = netdev_linux_tc_make_request(netdev, RTM_NEWTFILTER,
> +                                         NLM_F_EXCL | NLM_F_CREATE, 
> &request);
>      if (!tcmsg) {
>          return ENODEV;
>      }
> @@ -5014,7 +4928,8 @@ tc_query_class(const struct netdev *netdev,
>      struct tcmsg *tcmsg;
>      int error;
>  
> -    tcmsg = tc_make_request(netdev, RTM_GETTCLASS, NLM_F_ECHO, &request);
> +    tcmsg = netdev_linux_tc_make_request(netdev, RTM_GETTCLASS,
> +                                         NLM_F_ECHO, &request);
>      if (!tcmsg) {
>          return ENODEV;
>      }
> @@ -5040,7 +4955,7 @@ tc_delete_class(const struct netdev *netdev, unsigned 
> int handle)
>      struct tcmsg *tcmsg;
>      int error;
>  
> -    tcmsg = tc_make_request(netdev, RTM_DELTCLASS, 0, &request);
> +    tcmsg = netdev_linux_tc_make_request(netdev, RTM_DELTCLASS, 0, &request);
>      if (!tcmsg) {
>          return ENODEV;
>      }
> @@ -5066,7 +4981,7 @@ tc_del_qdisc(struct netdev *netdev_)
>      struct tcmsg *tcmsg;
>      int error;
>  
> -    tcmsg = tc_make_request(netdev_, RTM_DELQDISC, 0, &request);
> +    tcmsg = netdev_linux_tc_make_request(netdev_, RTM_DELQDISC, 0, &request);
>      if (!tcmsg) {
>          return ENODEV;
>      }
> @@ -5147,7 +5062,8 @@ tc_query_qdisc(const struct netdev *netdev_)
>       * in such a case we get no response at all from the kernel (!) if a
>       * builtin qdisc is in use (which is later caught by "!error &&
>       * !qdisc->size"). */
> -    tcmsg = tc_make_request(netdev_, RTM_GETQDISC, NLM_F_ECHO, &request);
> +    tcmsg = netdev_linux_tc_make_request(netdev_, RTM_GETQDISC,
> +                                         NLM_F_ECHO, &request);
>      if (!tcmsg) {
>          return ENODEV;
>      }
> diff --git a/lib/tc.c b/lib/tc.c
> new file mode 100644
> index 0000000..cd06025
> --- /dev/null
> +++ b/lib/tc.c
> @@ -0,0 +1,1109 @@
> +/*
> + * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc.
> + * Copyright (c) 2016 Mellanox Technologies, Ltd.
> + *
> + * Licensed under the Apache License, Version 2.0 (the "License");
> + * you may not use this file except in compliance with the License.
> + * You may obtain a copy of the License at:
> + *
> + *     http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing, software
> + * distributed under the License is distributed on an "AS IS" BASIS,
> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> + * See the License for the specific language governing permissions and
> + * limitations under the License.
> + */
> +
> +#include <config.h>
> +
> +#include <errno.h>
> +#include <linux/rtnetlink.h>
> +#include <net/if.h>
> +#include <linux/tc_act/tc_gact.h>
> +#include <linux/tc_act/tc_mirred.h>
> +#include <linux/tc_act/tc_vlan.h>
> +#include <linux/tc_act/tc_tunnel_key.h>
> +#include <linux/gen_stats.h>
> +#include "timeval.h"
> +#include "netlink-socket.h"
> +#include "netlink.h"
> +#include "rtnetlink.h"
> +#include "openvswitch/vlog.h"
> +#include "openvswitch/ofpbuf.h"
> +#include "tc.h"
> +#include "util.h"
> +#include "byte-order.h"
> +
> +VLOG_DEFINE_THIS_MODULE(tc);
> +
> +static struct vlog_rate_limit parse_err = VLOG_RATE_LIMIT_INIT(5, 5);
> +
> +/* Returns tc handle 'major':'minor'. */
> +unsigned int
> +tc_make_handle(unsigned int major, unsigned int minor)
> +{
> +    return TC_H_MAKE(major << 16, minor);
> +}
> +
> +/* Returns the major number from 'handle'. */
> +unsigned int
> +tc_get_major(unsigned int handle)
> +{
> +    return TC_H_MAJ(handle) >> 16;
> +}
> +
> +/* Returns the minor number from 'handle'. */
> +unsigned int
> +tc_get_minor(unsigned int handle)
> +{
> +    return TC_H_MIN(handle);
> +}
> +
> +struct tcmsg *
> +tc_make_request(int ifindex, int type, unsigned int flags,
> +                struct ofpbuf *request)
> +{
> +    struct tcmsg *tcmsg;
> +
> +    ofpbuf_init(request, 512);
> +    nl_msg_put_nlmsghdr(request, sizeof *tcmsg, type, NLM_F_REQUEST | flags);
> +    tcmsg = ofpbuf_put_zeros(request, sizeof *tcmsg);
> +    tcmsg->tcm_family = AF_UNSPEC;
> +    tcmsg->tcm_ifindex = ifindex;
> +    /* Caller should fill in tcmsg->tcm_handle. */
> +    /* Caller should fill in tcmsg->tcm_parent. */
> +
> +    return tcmsg;
> +}
> +
> +int
> +tc_transact(struct ofpbuf *request, struct ofpbuf **replyp)
> +{
> +    int error = nl_transact(NETLINK_ROUTE, request, replyp);
> +    ofpbuf_uninit(request);
> +    return error;
> +}
> +
> +/* Adds or deletes a root ingress qdisc on device with specified ifindex.
> + *
> + * This function is equivalent to running the following when 'add' is true:
> + *     /sbin/tc qdisc add dev <devname> handle ffff: ingress
> + *
> + * This function is equivalent to running the following when 'add' is false:
> + *     /sbin/tc qdisc del dev <devname> handle ffff: ingress
> + *
> + * Where dev <devname> is the device with specified ifindex name.
> + *
> + * The configuration and stats may be seen with the following command:
> + *     /sbin/tc -s qdisc show dev <devname>
> + *
> + *
> + * Returns 0 if successful, otherwise a positive errno value.
> + */
> +int
> +tc_add_del_ingress_qdisc(int ifindex, bool add)
> +{
> +    struct ofpbuf request;
> +    struct tcmsg *tcmsg;
> +    int error;
> +    int type = add ? RTM_NEWQDISC : RTM_DELQDISC;
> +    int flags = add ? NLM_F_EXCL | NLM_F_CREATE : 0;
> +
> +    tcmsg = tc_make_request(ifindex, type, flags, &request);
> +    tcmsg->tcm_handle = tc_make_handle(0xffff, 0);
> +    tcmsg->tcm_parent = TC_H_INGRESS;
> +    nl_msg_put_string(&request, TCA_KIND, "ingress");
> +    nl_msg_put_unspec(&request, TCA_OPTIONS, NULL, 0);
> +
> +    error = tc_transact(&request, NULL);
> +    if (error) {
> +        /* If we're deleting the qdisc, don't worry about some of the
> +         * error conditions. */
> +        if (!add && (error == ENOENT || error == EINVAL)) {
> +            return 0;
> +        }
> +        return error;
> +    }
> +
> +    return 0;
> +}
> +
> +static const struct nl_policy tca_policy[] = {
> +    [TCA_KIND] = { .type = NL_A_STRING, .optional = false, },
> +    [TCA_OPTIONS] = { .type = NL_A_NESTED, .optional = false, },
> +    [TCA_STATS] = { .type = NL_A_UNSPEC,
> +                    .min_len = sizeof(struct tc_stats), .optional = true, },
> +    [TCA_STATS2] = { .type = NL_A_NESTED, .optional = true, },
> +};
> +
> +static const struct nl_policy tca_flower_policy[] = {
> +    [TCA_FLOWER_CLASSID] = { .type = NL_A_U32, .optional = true, },
> +    [TCA_FLOWER_INDEV] = { .type = NL_A_STRING, .max_len = IFNAMSIZ,
> +                           .optional = true, },
> +    [TCA_FLOWER_KEY_ETH_SRC] = { .type = NL_A_UNSPEC,
> +                                 .min_len = ETH_ALEN, .optional = true, },
> +    [TCA_FLOWER_KEY_ETH_DST] = { .type = NL_A_UNSPEC,
> +                                 .min_len = ETH_ALEN, .optional = true, },
> +    [TCA_FLOWER_KEY_ETH_SRC_MASK] = { .type = NL_A_UNSPEC,
> +                                      .min_len = ETH_ALEN,
> +                                      .optional = true, },
> +    [TCA_FLOWER_KEY_ETH_DST_MASK] = { .type = NL_A_UNSPEC,
> +                                      .min_len = ETH_ALEN,
> +                                      .optional = true, },
> +    [TCA_FLOWER_KEY_ETH_TYPE] = { .type = NL_A_U16, .optional = false, },
> +    [TCA_FLOWER_FLAGS] = { .type = NL_A_U32, .optional = false, },
> +    [TCA_FLOWER_ACT] = { .type = NL_A_NESTED, .optional = false, },
> +    [TCA_FLOWER_KEY_IP_PROTO] = { .type = NL_A_U8, .optional = true, },
> +    [TCA_FLOWER_KEY_IPV4_SRC] = { .type = NL_A_U32, .optional = true, },
> +    [TCA_FLOWER_KEY_IPV4_DST] = {.type = NL_A_U32, .optional = true, },
> +    [TCA_FLOWER_KEY_IPV4_SRC_MASK] = { .type = NL_A_U32, .optional = true, },
> +    [TCA_FLOWER_KEY_IPV4_DST_MASK] = { .type = NL_A_U32, .optional = true, },
> +    [TCA_FLOWER_KEY_IPV6_SRC] = { .type = NL_A_UNSPEC,
> +                                  .min_len = sizeof(struct in6_addr),
> +                                  .optional = true, },
> +    [TCA_FLOWER_KEY_IPV6_DST] = { .type = NL_A_UNSPEC,
> +                                  .min_len = sizeof(struct in6_addr),
> +                                  .optional = true, },
> +    [TCA_FLOWER_KEY_IPV6_SRC_MASK] = { .type = NL_A_UNSPEC,
> +                                       .min_len = sizeof(struct in6_addr),
> +                                       .optional = true, },
> +    [TCA_FLOWER_KEY_IPV6_DST_MASK] = { .type = NL_A_UNSPEC,
> +                                       .min_len = sizeof(struct in6_addr),
> +                                       .optional = true, },
> +    [TCA_FLOWER_KEY_TCP_SRC] = { .type = NL_A_U16, .optional = true, },
> +    [TCA_FLOWER_KEY_TCP_DST] = { .type = NL_A_U16, .optional = true, },
> +    [TCA_FLOWER_KEY_TCP_SRC_MASK] = { .type = NL_A_U16, .optional = true, },
> +    [TCA_FLOWER_KEY_TCP_DST_MASK] = { .type = NL_A_U16, .optional = true, },
> +    [TCA_FLOWER_KEY_UDP_SRC] = { .type = NL_A_U16, .optional = true, },
> +    [TCA_FLOWER_KEY_UDP_DST] = { .type = NL_A_U16, .optional = true, },
> +    [TCA_FLOWER_KEY_UDP_SRC_MASK] = { .type = NL_A_U16, .optional = true, },
> +    [TCA_FLOWER_KEY_UDP_DST_MASK] = { .type = NL_A_U16, .optional = true, },
> +    [TCA_FLOWER_KEY_VLAN_ID] = { .type = NL_A_U16, .optional = true, },
> +    [TCA_FLOWER_KEY_VLAN_PRIO] = { .type = NL_A_U8, .optional = true, },
> +    [TCA_FLOWER_KEY_VLAN_ETH_TYPE] = { .type = NL_A_U16, .optional = true, },
> +    [TCA_FLOWER_KEY_ENC_KEY_ID] = { .type = NL_A_BE32, .optional = true, },
> +    [TCA_FLOWER_KEY_ENC_IPV4_SRC] = { .type = NL_A_BE32, .optional = true, },
> +    [TCA_FLOWER_KEY_ENC_IPV4_DST] = { .type = NL_A_BE32, .optional = true, },
> +    [TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK] = { .type = NL_A_BE32, .optional = 
> true, },
> +    [TCA_FLOWER_KEY_ENC_IPV4_DST_MASK] = { .type = NL_A_BE32, .optional = 
> true, },
> +    [TCA_FLOWER_KEY_ENC_IPV6_SRC] = { .type = NL_A_UNSPEC,
> +                                      .min_len = sizeof(struct in6_addr),
> +                                      .optional = true, },
> +    [TCA_FLOWER_KEY_ENC_IPV6_DST] = { .type = NL_A_UNSPEC,
> +                                      .min_len = sizeof(struct in6_addr),
> +                                      .optional = true, },
> +    [TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK] = { .type = NL_A_UNSPEC,
> +                                           .min_len = sizeof(struct 
> in6_addr),
> +                                           .optional = true, },
> +    [TCA_FLOWER_KEY_ENC_IPV6_DST_MASK] = { .type = NL_A_UNSPEC,
> +                                           .min_len = sizeof(struct 
> in6_addr),
> +                                           .optional = true, },
> +    [TCA_FLOWER_KEY_ENC_UDP_DST_PORT] = { .type = NL_A_BE16,
> +                                          .optional = true, },
> +};
> +
> +static void
> +nl_parse_flower_eth(struct nlattr **attrs, struct tc_flower *flower)
> +{
> +    const struct eth_addr *eth;
> +
> +    if (attrs[TCA_FLOWER_KEY_ETH_SRC_MASK]) {
> +        eth = nl_attr_get_unspec(attrs[TCA_FLOWER_KEY_ETH_SRC], ETH_ALEN);
> +        memcpy(&flower->key.src_mac, eth, sizeof flower->key.src_mac);
> +
> +        eth = nl_attr_get_unspec(attrs[TCA_FLOWER_KEY_ETH_SRC_MASK], 
> ETH_ALEN);
> +        memcpy(&flower->mask.src_mac, eth, sizeof flower->mask.src_mac);
> +    }
> +    if (attrs[TCA_FLOWER_KEY_ETH_DST_MASK]) {
> +        eth = nl_attr_get_unspec(attrs[TCA_FLOWER_KEY_ETH_DST], ETH_ALEN);
> +        memcpy(&flower->key.dst_mac, eth, sizeof flower->key.dst_mac);
> +
> +        eth = nl_attr_get_unspec(attrs[TCA_FLOWER_KEY_ETH_DST_MASK], 
> ETH_ALEN);
> +        memcpy(&flower->mask.dst_mac, eth, sizeof flower->mask.dst_mac);
> +    }
> +}
> +
> +static void
> +nl_parse_flower_vlan(struct nlattr **attrs, struct tc_flower *flower)
> +{
> +    if (flower->key.eth_type != htons(ETH_P_8021Q)) {
> +        return;
> +    }
> +
> +    flower->key.encap_eth_type =
> +        nl_attr_get_be16(attrs[TCA_FLOWER_KEY_ETH_TYPE]);
> +
> +    if (attrs[TCA_FLOWER_KEY_VLAN_ID]) {
> +        flower->key.vlan_id =
> +            nl_attr_get_u16(attrs[TCA_FLOWER_KEY_VLAN_ID]);
> +    }
> +    if (attrs[TCA_FLOWER_KEY_VLAN_PRIO]) {
> +        flower->key.vlan_prio =
> +            nl_attr_get_u8(attrs[TCA_FLOWER_KEY_VLAN_PRIO]);
> +    }
> +}
> +
> +static void
> +nl_parse_flower_tunnel(struct nlattr **attrs, struct tc_flower *flower)
> +{
> +    if (attrs[TCA_FLOWER_KEY_ENC_KEY_ID]) {
> +        ovs_be32 id = nl_attr_get_be32(attrs[TCA_FLOWER_KEY_ENC_KEY_ID]);
> +
> +        flower->tunnel.id = be32_to_be64(id);
> +    }
> +    if (attrs[TCA_FLOWER_KEY_ENC_IPV4_SRC_MASK]) {
> +        flower->tunnel.ipv4.ipv4_src =
> +            nl_attr_get_be32(attrs[TCA_FLOWER_KEY_ENC_IPV4_SRC]);
> +    }
> +    if (attrs[TCA_FLOWER_KEY_ENC_IPV4_DST_MASK]) {
> +        flower->tunnel.ipv4.ipv4_dst =
> +            nl_attr_get_be32(attrs[TCA_FLOWER_KEY_ENC_IPV4_DST]);
> +    }
> +    if (attrs[TCA_FLOWER_KEY_ENC_IPV6_SRC_MASK]) {
> +        flower->tunnel.ipv6.ipv6_src =
> +            nl_attr_get_in6_addr(attrs[TCA_FLOWER_KEY_ENC_IPV6_SRC]);
> +    }
> +    if (attrs[TCA_FLOWER_KEY_ENC_IPV6_DST_MASK]) {
> +        flower->tunnel.ipv6.ipv6_dst =
> +            nl_attr_get_in6_addr(attrs[TCA_FLOWER_KEY_ENC_IPV6_DST]);
> +    }
> +    if (attrs[TCA_FLOWER_KEY_ENC_UDP_DST_PORT]) {
> +        flower->tunnel.tp_dst =
> +            nl_attr_get_be16(attrs[TCA_FLOWER_KEY_ENC_UDP_DST_PORT]);
> +    }
> +}
> +
> +static void
> +nl_parse_flower_ip(struct nlattr **attrs, struct tc_flower *flower) {
> +    uint8_t ip_proto = 0;
> +    struct tc_flower_key *key = &flower->key;
> +    struct tc_flower_key *mask = &flower->mask;
> +
> +    if (attrs[TCA_FLOWER_KEY_IP_PROTO]) {
> +        ip_proto = nl_attr_get_u8(attrs[TCA_FLOWER_KEY_IP_PROTO]);
> +        key->ip_proto = ip_proto;
> +        mask->ip_proto = UINT8_MAX;
> +    }
> +
> +    if (attrs[TCA_FLOWER_KEY_IPV4_SRC_MASK]) {
> +        key->ipv4.ipv4_src =
> +            nl_attr_get_be32(attrs[TCA_FLOWER_KEY_IPV4_SRC]);
> +        mask->ipv4.ipv4_src =
> +            nl_attr_get_be32(attrs[TCA_FLOWER_KEY_IPV4_SRC_MASK]);
> +    }
> +    if (attrs[TCA_FLOWER_KEY_IPV4_DST_MASK]) {
> +        key->ipv4.ipv4_dst =
> +            nl_attr_get_be32(attrs[TCA_FLOWER_KEY_IPV4_DST]);
> +        mask->ipv4.ipv4_dst =
> +            nl_attr_get_be32(attrs[TCA_FLOWER_KEY_IPV4_DST_MASK]);
> +    }
> +    if (attrs[TCA_FLOWER_KEY_IPV6_SRC_MASK]) {
> +        struct nlattr *attr = attrs[TCA_FLOWER_KEY_IPV6_SRC];
> +        struct nlattr *attr_mask = attrs[TCA_FLOWER_KEY_IPV6_SRC_MASK];
> +
> +        key->ipv6.ipv6_src = nl_attr_get_in6_addr(attr);
> +        mask->ipv6.ipv6_src = nl_attr_get_in6_addr(attr_mask);
> +    }
> +    if (attrs[TCA_FLOWER_KEY_IPV6_DST_MASK]) {
> +        struct nlattr *attr = attrs[TCA_FLOWER_KEY_IPV6_DST];
> +        struct nlattr *attr_mask = attrs[TCA_FLOWER_KEY_IPV6_DST_MASK];
> +
> +        key->ipv6.ipv6_dst = nl_attr_get_in6_addr(attr);
> +        mask->ipv6.ipv6_dst = nl_attr_get_in6_addr(attr_mask);
> +    }
> +
> +    if (ip_proto == IPPROTO_TCP) {
> +        if (attrs[TCA_FLOWER_KEY_TCP_SRC_MASK]) {
> +            key->src_port =
> +                nl_attr_get_be16(attrs[TCA_FLOWER_KEY_TCP_SRC]);
> +            mask->src_port =
> +                nl_attr_get_be16(attrs[TCA_FLOWER_KEY_TCP_SRC_MASK]);
> +        }
> +        if (attrs[TCA_FLOWER_KEY_TCP_DST_MASK]) {
> +            key->dst_port =
> +                nl_attr_get_be16(attrs[TCA_FLOWER_KEY_TCP_DST]);
> +            mask->dst_port =
> +                nl_attr_get_be16(attrs[TCA_FLOWER_KEY_TCP_DST_MASK]);
> +        }
> +    } else if (ip_proto == IPPROTO_UDP) {
> +        if (attrs[TCA_FLOWER_KEY_UDP_SRC_MASK]) {
> +            key->src_port = nl_attr_get_be16(attrs[TCA_FLOWER_KEY_UDP_SRC]);
> +            mask->src_port =
> +                nl_attr_get_be16(attrs[TCA_FLOWER_KEY_UDP_SRC_MASK]);
> +        }
> +        if (attrs[TCA_FLOWER_KEY_UDP_DST_MASK]) {
> +            key->dst_port = nl_attr_get_be16(attrs[TCA_FLOWER_KEY_UDP_DST]);
> +            mask->dst_port =
> +                nl_attr_get_be16(attrs[TCA_FLOWER_KEY_UDP_DST_MASK]);
> +        }


OVS does support SCTP as well and tc-flower too, can you include in
this patch also?

commit 5976c5f45c40588b90dda173ded9010917f8f45e
Author: Simon Horman <[email protected]>
Date:   Thu Nov 3 13:24:21 2016 +0100

    net/sched: cls_flower: Support matching on SCTP ports



> +    }
> +}
> +
> +static const struct nl_policy tunnel_key_policy[] = {
> +    [TCA_TUNNEL_KEY_PARMS] = { .type = NL_A_UNSPEC,
> +                               .min_len = sizeof(struct tc_tunnel_key),
> +                               .optional = false, },
> +    [TCA_TUNNEL_KEY_ENC_KEY_ID] = { .type = NL_A_BE32, .optional = true, },
> +    [TCA_TUNNEL_KEY_ENC_DST_PORT] = { .type = NL_A_BE16, .optional = true, },
> +    [TCA_TUNNEL_KEY_ENC_IPV4_SRC] = { .type = NL_A_BE32, .optional = true, },
> +    [TCA_TUNNEL_KEY_ENC_IPV4_DST] = { .type = NL_A_BE32, .optional = true, },
> +    [TCA_TUNNEL_KEY_ENC_IPV6_SRC] = { .type = NL_A_UNSPEC,
> +                                      .min_len = sizeof(struct in6_addr),
> +                                      .optional = true, },
> +    [TCA_TUNNEL_KEY_ENC_IPV6_DST] = { .type = NL_A_UNSPEC,
> +                                      .min_len = sizeof(struct in6_addr),
> +                                      .optional = true, },
> +};
> +
> +static int
> +nl_parse_act_tunnel_key(struct nlattr *options, struct tc_flower *flower)
> +{
> +    struct nlattr *tun_attrs[ARRAY_SIZE(tunnel_key_policy)];
> +    const struct nlattr *tun_parms;
> +    const struct tc_tunnel_key *tun;
> +
> +    if (!nl_parse_nested(options, tunnel_key_policy, tun_attrs,
> +                ARRAY_SIZE(tunnel_key_policy))) {
> +        VLOG_ERR_RL(&parse_err, "failed to parse tunnel_key action options");
> +        return EPROTO;
> +    }
> +
> +    tun_parms = tun_attrs[TCA_TUNNEL_KEY_PARMS];
> +    tun = nl_attr_get_unspec(tun_parms, sizeof *tun);
> +    if (tun->t_action == TCA_TUNNEL_KEY_ACT_SET) {
> +        struct nlattr *id = tun_attrs[TCA_TUNNEL_KEY_ENC_KEY_ID];
> +        struct nlattr *dst_port = tun_attrs[TCA_TUNNEL_KEY_ENC_DST_PORT];
> +        struct nlattr *ipv4_src = tun_attrs[TCA_TUNNEL_KEY_ENC_IPV4_SRC];
> +        struct nlattr *ipv4_dst = tun_attrs[TCA_TUNNEL_KEY_ENC_IPV4_DST];
> +        struct nlattr *ipv6_src = tun_attrs[TCA_TUNNEL_KEY_ENC_IPV6_SRC];
> +        struct nlattr *ipv6_dst = tun_attrs[TCA_TUNNEL_KEY_ENC_IPV6_DST];
> +
> +        flower->set.set = true;
> +        flower->set.ipv4.ipv4_src = ipv4_src ? nl_attr_get_be32(ipv4_src) : 
> 0;
> +        flower->set.ipv4.ipv4_dst = ipv4_dst ? nl_attr_get_be32(ipv4_dst) : 
> 0;
> +        if (ipv6_src) {
> +            flower->set.ipv6.ipv6_src = nl_attr_get_in6_addr(ipv6_src);
> +        }
> +        if (ipv6_dst) {
> +            flower->set.ipv6.ipv6_dst = nl_attr_get_in6_addr(ipv6_dst);
> +        }
> +        flower->set.id = id ? be32_to_be64(nl_attr_get_be32(id)) : 0;
> +        flower->set.tp_dst = dst_port ? nl_attr_get_be16(dst_port) : 0;
> +    } else if (tun->t_action == TCA_TUNNEL_KEY_ACT_RELEASE) {
> +        flower->tunnel.tunnel = true;
> +    } else {
> +        VLOG_ERR_RL(&parse_err, "unknown tunnel actions: %d, %d",
> +                    tun->action, tun->t_action);
> +        return EINVAL;
> +    }
> +    return 0;
> +}
> +
> +static const struct nl_policy gact_policy[] = {
> +    [TCA_GACT_PARMS] = { .type = NL_A_UNSPEC,
> +                         .min_len = sizeof(struct tc_gact),
> +                         .optional = false, },
> +    [TCA_GACT_TM] = { .type = NL_A_UNSPEC,
> +                      .min_len = sizeof(struct tcf_t),
> +                      .optional = false, },
> +};
> +
> +static void
> +nl_parse_tcf(const struct tcf_t *tm, struct tc_flower *flower)
> +{
> +    unsigned long long int lastuse = tm->lastuse * 10;
> +    unsigned long long int now = time_msec();
> +
> +    flower->lastused = now - lastuse;
> +}
> +
> +static int
> +nl_parse_act_drop(struct nlattr *options, struct tc_flower *flower)
> +{
> +    struct nlattr *gact_attrs[ARRAY_SIZE(gact_policy)];
> +    const struct tc_gact *p;
> +    struct nlattr *gact_parms;
> +    const struct tcf_t *tm;
> +
> +    if (!nl_parse_nested(options, gact_policy, gact_attrs,
> +                         ARRAY_SIZE(gact_policy))) {
> +        VLOG_ERR_RL(&parse_err, "failed to parse gact action options");
> +        return EPROTO;
> +    }
> +
> +    gact_parms = gact_attrs[TCA_GACT_PARMS];
> +    p = nl_attr_get_unspec(gact_parms, sizeof *p);
> +
> +    if (p->action == TC_ACT_SHOT) {
> +    } else {
> +            VLOG_ERR_RL(&parse_err, "unknown gact action: %d", p->action);
> +            return EINVAL;
> +    }
> +
> +    tm = nl_attr_get_unspec(gact_attrs[TCA_GACT_TM], sizeof *tm);
> +    nl_parse_tcf(tm, flower);
> +
> +    return 0;
> +}
> +
> +static const struct nl_policy mirred_policy[] = {
> +    [TCA_MIRRED_PARMS] = { .type = NL_A_UNSPEC,
> +                           .min_len = sizeof(struct tc_mirred),
> +                           .optional = false, },
> +    [TCA_MIRRED_TM] = { .type = NL_A_UNSPEC,
> +                        .min_len = sizeof(struct tcf_t),
> +                        .optional = false, },
> +};
> +
> +static int
> +nl_parse_act_mirred(struct nlattr *options, struct tc_flower *flower)
> +{
> +
> +    struct nlattr *mirred_attrs[ARRAY_SIZE(mirred_policy)];
> +    const struct tc_mirred *m;
> +    const struct nlattr *mirred_parms;
> +    const struct tcf_t *tm;
> +    struct nlattr *mirred_tm;
> +
> +    if (!nl_parse_nested(options, mirred_policy, mirred_attrs,
> +                         ARRAY_SIZE(mirred_policy))) {
> +        VLOG_ERR_RL(&parse_err, "failed to parse mirred action options");
> +        return EPROTO;
> +    }
> +
> +    mirred_parms = mirred_attrs[TCA_MIRRED_PARMS];
> +    m = nl_attr_get_unspec(mirred_parms, sizeof *m);
> +
> +    if (m->action != TC_ACT_STOLEN ||  m->eaction != TCA_EGRESS_REDIR) {
> +        VLOG_ERR_RL(&parse_err, "unknown mirred action: %d, %d, %d",
> +                 m->action, m->eaction, m->ifindex);
> +        return EINVAL;
> +    }
> +
> +    flower->ifindex_out = m->ifindex;
> +
> +    mirred_tm = mirred_attrs[TCA_MIRRED_TM];
> +    tm = nl_attr_get_unspec(mirred_tm, sizeof *tm);
> +    nl_parse_tcf(tm, flower);
> +
> +    return 0;
> +}
> +
> +static const struct nl_policy vlan_policy[] = {
> +    [TCA_VLAN_PARMS] = { .type = NL_A_UNSPEC,
> +                         .min_len = sizeof(struct tc_vlan),
> +                         .optional = false, },
> +    [TCA_VLAN_PUSH_VLAN_ID] = { .type = NL_A_U16, .optional = true, },
> +    [TCA_VLAN_PUSH_VLAN_PROTOCOL] = { .type = NL_A_U16, .optional = true, },
> +    [TCA_VLAN_PUSH_VLAN_PRIORITY] = { .type = NL_A_U8, .optional = true, },
> +};
> +
> +static int
> +nl_parse_act_vlan(struct nlattr *options, struct tc_flower *flower)
> +{
> +    struct nlattr *vlan_attrs[ARRAY_SIZE(vlan_policy)];
> +    const struct tc_vlan *v;
> +    const struct nlattr *vlan_parms;
> +
> +    if (!nl_parse_nested(options, vlan_policy, vlan_attrs,
> +                         ARRAY_SIZE(vlan_policy))) {
> +        VLOG_ERR_RL(&parse_err, "failed to parse vlan action options");
> +        return EPROTO;
> +    }
> +
> +    vlan_parms = vlan_attrs[TCA_VLAN_PARMS];
> +    v = nl_attr_get_unspec(vlan_parms, sizeof *v);
> +    if (v->v_action == TCA_VLAN_ACT_PUSH) {
> +        struct nlattr *vlan_id = vlan_attrs[TCA_VLAN_PUSH_VLAN_ID];
> +        struct nlattr *vlan_prio = vlan_attrs[TCA_VLAN_PUSH_VLAN_PRIORITY];
> +
> +        flower->vlan_push_id = nl_attr_get_u16(vlan_id);
> +        flower->vlan_push_prio = nl_attr_get_u8(vlan_prio);
> +    } else if (v->v_action == TCA_VLAN_ACT_POP) {
> +        flower->vlan_pop = 1;
> +    } else {
> +        VLOG_ERR_RL(&parse_err, "unknown vlan action: %d, %d",
> +                    v->action, v->v_action);
> +        return EINVAL;
> +    }
> +    return 0;
> +}
> +
> +static const struct nl_policy act_policy[] = {
> +    [TCA_ACT_KIND] = { .type = NL_A_STRING, .optional = false, },
> +    [TCA_ACT_COOKIE] = { .type = NL_A_UNSPEC, .optional = true, },
> +    [TCA_ACT_OPTIONS] = { .type = NL_A_NESTED, .optional = false, },
> +    [TCA_ACT_STATS] = { .type = NL_A_NESTED, .optional = false, },


This patch breaks with not so recent kernels because some defines
are not available yet. E.g.: TCA_ACT_COOKIE included in v4.11-rc1.

I'd suggest to look at lib/netlink-conntrack.c as an example including
missing defines to allow userspace to compile with older kernels.


Thanks,
-- 
Flavio

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to