On Sat, Feb 23, 2019 at 2:35 AM Gregory Rose <gvrose8...@gmail.com> wrote:

> Numan,
>
> I intend to test and review this on my local net-next branch but I'm not
> feeling well so it will probably be
> early next week before I can do that.  Sorry for the delay...
>
>
No worries. Please take your time.
If you want to test it out, you probably need the corresponding
ovs-vswitchd patch.
I still need to address the review comments from Ben. But you can use the
one from here -
https://github.com/numansiddique/ovs/tree/ovn_mtu_fix/rfc_v4_p4
https://github.com/numansiddique/ovs/commits/ovn_mtu_fix/rfc_v4_p4 for your
testing.

Thanks
Numan



- Greg
>
> On 2/21/2019 10:42 AM, nusid...@redhat.com wrote:
> > From: Numan Siddique <nusid...@redhat.com>
> >
> > [Please note, this patch is submitted as RFC in ovs-dev ML to
> > get feedback before submitting to netdev ML. You need net-next tree
> > to apply this patch]
> >
> > This patch adds a new action - 'check_pkt_len' which checks the
> > packet length and executes a set of actions if the packet
> > length is greater than the specified length or executes
> > another set of actions if the packet length is lesser or equal to.
> >
> > This action takes below nlattrs
> >    * OVS_CHECK_PKT_LEN_ATTR_PKT_LEN - 'pkt_len' to check for
> >
> >    * OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER - Nested actions
> >      to apply if the packet length is greater than the specified
> 'pkt_len'
> >
> >    * OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL - Nested
> >      actions to apply if the packet length is lesser or equal to the
> >      specified 'pkt_len'.
> >
> > The main use case for adding this action is to solve the packet
> > drops because of MTU mismatch in OVN virtual networking solution.
> > When a VM (which belongs to a logical switch of OVN) sends a packet
> > destined to go via the gateway router and if the nic which provides
> > external connectivity, has a lesser MTU, OVS drops the packet
> > if the packet length is greater than this MTU.
> >
> > With the help of this action, OVN will check the packet length
> > and if it is greater than the MTU size, it will generate an
> > ICMP packet (type 3, code 4) and includes the next hop mtu in it
> > so that the sender can fragment the packets.
> >
> > Reported-at:
> > https://mail.openvswitch.org/pipermail/ovs-discuss/2018-July/047039.html
> > Suggested-by: Ben Pfaff <b...@ovn.org>
> > Signed-off-by: Numan Siddique <nusid...@redhat.com>
> > CC: Ben Pfaff <b...@ovn.org>
> > CC: Greg Rose <gvrose8...@gmail.com>
> > CC: Lorenzo Bianconi <lorenzo.bianc...@redhat.com>
> > ---
> >
> > v3 -> v4
> > --------
> >   * v4 only has 1 patch - datapath patch which implements the
> >   * check_pkt_len action
> >   * Addressed the review comments from Lorenzo, Ben and Greg
> >
> >
> >   include/uapi/linux/openvswitch.h |  42 ++++++++
> >   net/openvswitch/actions.c        |  49 +++++++++
> >   net/openvswitch/flow_netlink.c   | 171 +++++++++++++++++++++++++++++++
> >   3 files changed, 262 insertions(+)
> >
> > diff --git a/include/uapi/linux/openvswitch.h
> b/include/uapi/linux/openvswitch.h
> > index dbe0cbe4f1b7..05ab885c718d 100644
> > --- a/include/uapi/linux/openvswitch.h
> > +++ b/include/uapi/linux/openvswitch.h
> > @@ -798,6 +798,44 @@ struct ovs_action_push_eth {
> >       struct ovs_key_ethernet addresses;
> >   };
> >
> > +/*
> > + * enum ovs_check_pkt_len_attr - Attributes for
> %OVS_ACTION_ATTR_CHECK_PKT_LEN.
> > + *
> > + * @OVS_CHECK_PKT_LEN_ATTR_PKT_LEN: u16 Packet length to check for.
> > + * @OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER: Nested OVS_ACTION_ATTR_*
> > + * actions to apply if the packer length is greater than the specified
> > + * length in the attr - OVS_CHECK_PKT_LEN_ATTR_PKT_LEN.
> > + * @OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL - Nested
> OVS_ACTION_ATTR_*
> > + * actions to apply if the packer length is lesser or equal to the
> specified
> > + * length in the attr - OVS_CHECK_PKT_LEN_ATTR_PKT_LEN.
> > + */
> > +enum ovs_check_pkt_len_attr {
> > +     OVS_CHECK_PKT_LEN_ATTR_UNSPEC,
> > +     OVS_CHECK_PKT_LEN_ATTR_PKT_LEN,
> > +     OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER,
> > +     OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL,
> > +     __OVS_CHECK_PKT_LEN_ATTR_MAX,
> > +
> > +#ifdef __KERNEL__
> > +     OVS_CHECK_PKT_LEN_ATTR_ARG          /* struct check_pkt_len_arg  */
> > +#endif
> > +};
> > +
> > +#define OVS_CHECK_PKT_LEN_ATTR_MAX (__OVS_CHECK_PKT_LEN_ATTR_MAX - 1)
> > +
> > +#ifdef __KERNEL__
> > +struct check_pkt_len_arg {
> > +     u16 pkt_len;    /* Same value as OVS_CHECK_PKT_LEN_ATTR_PKT_LEN'.
> */
> > +     bool exec_for_greater;  /* When true, actions in IF_GREATE will
> > +                              * not change flow keys. False otherwise.
> > +                              */
> > +     bool exec_for_lesser_equal; /* When true, actions in IF_LESS_EQUAL
> > +                                  * will not change flow keys. False
> > +                                  * otherwise.
> > +                                  */
> > +};
> > +#endif
> > +
> >   /**
> >    * enum ovs_action_attr - Action types.
> >    *
> > @@ -842,6 +880,9 @@ struct ovs_action_push_eth {
> >    * packet, or modify the packet (e.g., change the DSCP field).
> >    * @OVS_ACTION_ATTR_CLONE: make a copy of the packet and execute a
> list of
> >    * actions without affecting the original packet and key.
> > + * @OVS_ACTION_ATTR_CHECK_PKT_LEN: Check the packet length and execute
> a set
> > + * of actions if greater than the specified packet length, else execute
> > + * another set of actions.
> >    *
> >    * Only a single header can be set with a single
> %OVS_ACTION_ATTR_SET.  Not all
> >    * fields within a header are modifiable, e.g. the IPv4 protocol and
> fragment
> > @@ -876,6 +917,7 @@ enum ovs_action_attr {
> >       OVS_ACTION_ATTR_POP_NSH,      /* No argument. */
> >       OVS_ACTION_ATTR_METER,        /* u32 meter ID. */
> >       OVS_ACTION_ATTR_CLONE,        /* Nested OVS_CLONE_ATTR_*.  */
> > +     OVS_ACTION_ATTR_CHECK_PKT_LEN, /* Nested OVS_CHECK_PKT_LEN_ATTR_*.
> */
> >
> >       __OVS_ACTION_ATTR_MAX,        /* Nothing past this will be accepted
> >                                      * from userspace. */
> > diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c
> > index e47ebbbe71b8..bc7b79b29469 100644
> > --- a/net/openvswitch/actions.c
> > +++ b/net/openvswitch/actions.c
> > @@ -169,6 +169,10 @@ static int clone_execute(struct datapath *dp,
> struct sk_buff *skb,
> >                        const struct nlattr *actions, int len,
> >                        bool last, bool clone_flow_key);
> >
> > +static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
> > +                           struct sw_flow_key *key,
> > +                           const struct nlattr *attr, int len);
> > +
> >   static void update_ethertype(struct sk_buff *skb, struct ethhdr *hdr,
> >                            __be16 ethertype)
> >   {
> > @@ -1213,6 +1217,41 @@ static int execute_recirc(struct datapath *dp,
> struct sk_buff *skb,
> >       return clone_execute(dp, skb, key, recirc_id, NULL, 0, last, true);
> >   }
> >
> > +static int execute_check_pkt_len(struct datapath *dp, struct sk_buff
> *skb,
> > +                              struct sw_flow_key *key,
> > +                              const struct nlattr *attr, bool last)
> > +{
> > +     const struct nlattr *actions, *cpl_arg;
> > +     const struct check_pkt_len_arg *arg;
> > +     int rem = nla_len(attr);
> > +     bool clone_flow_key;
> > +     u16 actual_pkt_len;
> > +
> > +     /* The first action is always 'OVS_CHECK_PKT_LEN_ATTR_ARG'. */
> > +     cpl_arg = nla_data(attr);
> > +     arg = nla_data(cpl_arg);
> > +
> > +     actual_pkt_len = skb->len + (skb_vlan_tag_present(skb) ? VLAN_HLEN
> : 0);
> > +
> > +     if (actual_pkt_len > arg->pkt_len) {
> > +             /* Second action is always
> > +              * 'OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER'.
> > +              */
> > +             actions = nla_next(cpl_arg, &rem);
> > +             clone_flow_key = !arg->exec_for_greater;
> > +     } else {
> > +             /* Third action is always
> > +              * 'OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL'.
> > +              */
> > +             actions = nla_next(cpl_arg, &rem);
> > +             actions = nla_next(actions, &rem);
> > +             clone_flow_key = !arg->exec_for_lesser_equal;
> > +     }
> > +
> > +     return clone_execute(dp, skb, key, 0, nla_data(actions),
> > +                          nla_len(actions), last, clone_flow_key);
> > +}
> > +
> >   /* Execute a list of actions against 'skb'. */
> >   static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
> >                             struct sw_flow_key *key,
> > @@ -1374,6 +1413,16 @@ static int do_execute_actions(struct datapath
> *dp, struct sk_buff *skb,
> >
> >                       break;
> >               }
> > +
> > +             case OVS_ACTION_ATTR_CHECK_PKT_LEN: {
> > +                     bool last = nla_is_last(a, rem);
> > +
> > +                     err = execute_check_pkt_len(dp, skb, key, a, last);
> > +                     if (last)
> > +                             return err;
> > +
> > +                     break;
> > +             }
> >               }
> >
> >               if (unlikely(err)) {
> > diff --git a/net/openvswitch/flow_netlink.c
> b/net/openvswitch/flow_netlink.c
> > index 691da853bef5..989b5092c526 100644
> > --- a/net/openvswitch/flow_netlink.c
> > +++ b/net/openvswitch/flow_netlink.c
> > @@ -91,6 +91,7 @@ static bool actions_may_change_flow(const struct
> nlattr *actions)
> >               case OVS_ACTION_ATTR_SET:
> >               case OVS_ACTION_ATTR_SET_MASKED:
> >               case OVS_ACTION_ATTR_METER:
> > +             case OVS_ACTION_ATTR_CHECK_PKT_LEN:
> >               default:
> >                       return true;
> >               }
> > @@ -2838,6 +2839,88 @@ static int validate_userspace(const struct nlattr
> *attr)
> >       return 0;
> >   }
> >
> > +static int validate_and_copy_check_pkt_len(struct net *net,
> > +                                        const struct nlattr *attr,
> > +                                        const struct sw_flow_key *key,
> > +                                        struct sw_flow_actions **sfa,
> > +                                        __be16 eth_type, __be16
> vlan_tci,
> > +                                        bool log, bool last)
> > +{
> > +     static const struct nla_policy pol[OVS_CHECK_PKT_LEN_ATTR_MAX + 1]
> = {
> > +             [OVS_CHECK_PKT_LEN_ATTR_PKT_LEN] = {.type = NLA_U16 },
> > +             [OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER] = {
> > +                     .type = NLA_NESTED },
> > +             [OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL] = {
> > +                     .type = NLA_NESTED },
> > +     };
> > +     const struct nlattr *acts_if_greater, *acts_if_lesser_eq;
> > +     struct nlattr *a[OVS_CHECK_PKT_LEN_ATTR_MAX + 1];
> > +     struct check_pkt_len_arg arg;
> > +     int nested_acts_start;
> > +     int start, err;
> > +
> > +     err = nla_parse_strict(a, OVS_CHECK_PKT_LEN_ATTR_MAX,
> nla_data(attr),
> > +                            nla_len(attr), pol, NULL);
> > +     if (err)
> > +             return err;
> > +
> > +     if (!a[OVS_CHECK_PKT_LEN_ATTR_PKT_LEN] ||
> > +         !nla_get_u16(a[OVS_CHECK_PKT_LEN_ATTR_PKT_LEN]))
> > +             return -EINVAL;
> > +
> > +     acts_if_greater = a[OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER];
> > +     acts_if_lesser_eq =
> a[OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL];
> > +
> > +     /* Both the nested action should be present. */
> > +     if (!acts_if_greater || !acts_if_lesser_eq)
> > +             return -EINVAL;
> > +
> > +     /* validation done, copy the nested actions. */
> > +     start = add_nested_action_start(sfa, OVS_ACTION_ATTR_CHECK_PKT_LEN,
> > +                                     log);
> > +     if (start < 0)
> > +             return start;
> > +
> > +     arg.pkt_len = nla_get_u16(a[OVS_CHECK_PKT_LEN_ATTR_PKT_LEN]);
> > +     arg.exec_for_greater =
> > +             last || !actions_may_change_flow(acts_if_greater);
> > +     arg.exec_for_lesser_equal =
> > +             last || !actions_may_change_flow(acts_if_lesser_eq);
> > +
> > +     err = ovs_nla_add_action(sfa, OVS_CHECK_PKT_LEN_ATTR_ARG, &arg,
> > +                              sizeof(arg), log);
> > +     if (err)
> > +             return err;
> > +
> > +     nested_acts_start = add_nested_action_start(sfa,
> > +             OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER, log);
> > +     if (nested_acts_start < 0)
> > +             return nested_acts_start;
> > +
> > +     err = __ovs_nla_copy_actions(net, acts_if_greater, key, sfa,
> > +                                  eth_type, vlan_tci, log);
> > +
> > +     if (err)
> > +             return err;
> > +
> > +     add_nested_action_end(*sfa, nested_acts_start);
> > +
> > +     nested_acts_start = add_nested_action_start(sfa,
> > +             OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL, log);
> > +     if (nested_acts_start < 0)
> > +             return nested_acts_start;
> > +
> > +     err = __ovs_nla_copy_actions(net, acts_if_lesser_eq, key, sfa,
> > +                                  eth_type, vlan_tci, log);
> > +
> > +     if (err)
> > +             return err;
> > +
> > +     add_nested_action_end(*sfa, nested_acts_start);
> > +     add_nested_action_end(*sfa, start);
> > +     return 0;
> > +}
> > +
> >   static int copy_action(const struct nlattr *from,
> >                      struct sw_flow_actions **sfa, bool log)
> >   {
> > @@ -2884,6 +2967,7 @@ static int __ovs_nla_copy_actions(struct net *net,
> const struct nlattr *attr,
> >                       [OVS_ACTION_ATTR_POP_NSH] = 0,
> >                       [OVS_ACTION_ATTR_METER] = sizeof(u32),
> >                       [OVS_ACTION_ATTR_CLONE] = (u32)-1,
> > +                     [OVS_ACTION_ATTR_CHECK_PKT_LEN] = (u32)-1,
> >               };
> >               const struct ovs_action_push_vlan *vlan;
> >               int type = nla_type(a);
> > @@ -3085,6 +3169,18 @@ static int __ovs_nla_copy_actions(struct net
> *net, const struct nlattr *attr,
> >                       break;
> >               }
> >
> > +             case OVS_ACTION_ATTR_CHECK_PKT_LEN: {
> > +                     bool last = nla_is_last(a, rem);
> > +
> > +                     err = validate_and_copy_check_pkt_len(net, a, key,
> sfa,
> > +                                                           eth_type,
> > +                                                           vlan_tci,
> log,
> > +                                                           last);
> > +                     if (err)
> > +                             return err;
> > +                     skip_copy = true;
> > +                     break;
> > +             }
> >               default:
> >                       OVS_NLERR(log, "Unknown Action type %d", type);
> >                       return -EINVAL;
> > @@ -3183,6 +3279,75 @@ static int clone_action_to_attr(const struct
> nlattr *attr,
> >       return err;
> >   }
> >
> > +static int check_pkt_len_action_to_attr(const struct nlattr *attr,
> > +                                     struct sk_buff *skb)
> > +{
> > +     struct nlattr *start, *ac_start = NULL;
> > +     const struct check_pkt_len_arg *arg;
> > +     const struct nlattr *a, *cpl_arg;
> > +     int err = 0, rem = nla_len(attr);
> > +
> > +     start = nla_nest_start(skb, OVS_ACTION_ATTR_CHECK_PKT_LEN);
> > +     if (!start)
> > +             return -EMSGSIZE;
> > +
> > +     /* The first nested action in 'attr' is always
> > +      * 'OVS_CHECK_PKT_LEN_ATTR_ARG'.
> > +      */
> > +     cpl_arg = nla_data(attr);
> > +     arg = nla_data(cpl_arg);
> > +
> > +     if (nla_put_u16(skb, OVS_CHECK_PKT_LEN_ATTR_PKT_LEN,
> arg->pkt_len)) {
> > +             err = -EMSGSIZE;
> > +             goto out;
> > +     }
> > +
> > +     /* Second action in 'attr' is always
> > +      * 'OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER'.
> > +      */
> > +     a = nla_next(cpl_arg, &rem);
> > +     ac_start =  nla_nest_start(skb,
> > +
> OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER);
> > +     if (!ac_start) {
> > +             err = -EMSGSIZE;
> > +             goto out;
> > +     }
> > +
> > +     err = ovs_nla_put_actions(nla_data(a), nla_len(a), skb);
> > +     if (err) {
> > +             nla_nest_cancel(skb, ac_start);
> > +             goto out;
> > +     } else {
> > +             nla_nest_end(skb, ac_start);
> > +     }
> > +
> > +     /* Third action in 'attr' is always
> > +      * OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL.
> > +      */
> > +     a = nla_next(a, &rem);
> > +     ac_start =  nla_nest_start(skb,
> > +             OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL);
> > +     if (!ac_start) {
> > +             err = -EMSGSIZE;
> > +             goto out;
> > +     }
> > +
> > +     err = ovs_nla_put_actions(nla_data(a), nla_len(a), skb);
> > +     if (err) {
> > +             nla_nest_cancel(skb, ac_start);
> > +             goto out;
> > +     } else {
> > +             nla_nest_end(skb, ac_start);
> > +     }
> > +
> > +     nla_nest_end(skb, start);
> > +     return 0;
> > +
> > +out:
> > +     nla_nest_cancel(skb, start);
> > +     return err;
> > +}
> > +
> >   static int set_action_to_attr(const struct nlattr *a, struct sk_buff
> *skb)
> >   {
> >       const struct nlattr *ovs_key = nla_data(a);
> > @@ -3277,6 +3442,12 @@ int ovs_nla_put_actions(const struct nlattr
> *attr, int len, struct sk_buff *skb)
> >                               return err;
> >                       break;
> >
> > +             case OVS_ACTION_ATTR_CHECK_PKT_LEN:
> > +                     err = check_pkt_len_action_to_attr(a, skb);
> > +                     if (err)
> > +                             return err;
> > +                     break;
> > +
> >               default:
> >                       if (nla_put(skb, type, nla_len(a), nla_data(a)))
> >                               return -EMSGSIZE;
>
>
_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to