Merge tag 'afs-fixes-b-20190516' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-block.git] / net / openvswitch / flow_netlink.c
index 4bdf5e3ac2087a67e715ebd720159205697668a8..54eb80dd2dc6f099bd903aefe58bf97ba875be4c 100644 (file)
@@ -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;
                }
@@ -403,6 +404,7 @@ static const struct ovs_len_tbl ovs_tunnel_key_lens[OVS_TUNNEL_KEY_ATTR_MAX + 1]
        [OVS_TUNNEL_KEY_ATTR_IPV6_SRC]      = { .len = sizeof(struct in6_addr) },
        [OVS_TUNNEL_KEY_ATTR_IPV6_DST]      = { .len = sizeof(struct in6_addr) },
        [OVS_TUNNEL_KEY_ATTR_ERSPAN_OPTS]   = { .len = OVS_ATTR_VARIABLE },
+       [OVS_TUNNEL_KEY_ATTR_IPV4_INFO_BRIDGE]   = { .len = 0 },
 };
 
 static const struct ovs_len_tbl
@@ -666,6 +668,7 @@ static int ip_tun_from_nlattr(const struct nlattr *attr,
                              bool log)
 {
        bool ttl = false, ipv4 = false, ipv6 = false;
+       bool info_bridge_mode = false;
        __be16 tun_flags = 0;
        int opts_type = 0;
        struct nlattr *a;
@@ -782,6 +785,10 @@ static int ip_tun_from_nlattr(const struct nlattr *attr,
                        tun_flags |= TUNNEL_ERSPAN_OPT;
                        opts_type = type;
                        break;
+               case OVS_TUNNEL_KEY_ATTR_IPV4_INFO_BRIDGE:
+                       info_bridge_mode = true;
+                       ipv4 = true;
+                       break;
                default:
                        OVS_NLERR(log, "Unknown IP tunnel attribute %d",
                                  type);
@@ -812,16 +819,29 @@ static int ip_tun_from_nlattr(const struct nlattr *attr,
                        OVS_NLERR(log, "IP tunnel dst address not specified");
                        return -EINVAL;
                }
-               if (ipv4 && !match->key->tun_key.u.ipv4.dst) {
-                       OVS_NLERR(log, "IPv4 tunnel dst address is zero");
-                       return -EINVAL;
+               if (ipv4) {
+                       if (info_bridge_mode) {
+                               if (match->key->tun_key.u.ipv4.src ||
+                                   match->key->tun_key.u.ipv4.dst ||
+                                   match->key->tun_key.tp_src ||
+                                   match->key->tun_key.tp_dst ||
+                                   match->key->tun_key.ttl ||
+                                   match->key->tun_key.tos ||
+                                   tun_flags & ~TUNNEL_KEY) {
+                                       OVS_NLERR(log, "IPv4 tun info is not correct");
+                                       return -EINVAL;
+                               }
+                       } else if (!match->key->tun_key.u.ipv4.dst) {
+                               OVS_NLERR(log, "IPv4 tunnel dst address is zero");
+                               return -EINVAL;
+                       }
                }
                if (ipv6 && ipv6_addr_any(&match->key->tun_key.u.ipv6.dst)) {
                        OVS_NLERR(log, "IPv6 tunnel dst address is zero");
                        return -EINVAL;
                }
 
-               if (!ttl) {
+               if (!ttl && !info_bridge_mode) {
                        OVS_NLERR(log, "IP tunnel TTL not specified.");
                        return -EINVAL;
                }
@@ -836,7 +856,7 @@ static int vxlan_opt_to_nlattr(struct sk_buff *skb,
        const struct vxlan_metadata *opts = tun_opts;
        struct nlattr *nla;
 
-       nla = nla_nest_start(skb, OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS);
+       nla = nla_nest_start_noflag(skb, OVS_TUNNEL_KEY_ATTR_VXLAN_OPTS);
        if (!nla)
                return -EMSGSIZE;
 
@@ -850,12 +870,17 @@ static int vxlan_opt_to_nlattr(struct sk_buff *skb,
 static int __ip_tun_to_nlattr(struct sk_buff *skb,
                              const struct ip_tunnel_key *output,
                              const void *tun_opts, int swkey_tun_opts_len,
-                             unsigned short tun_proto)
+                             unsigned short tun_proto, u8 mode)
 {
        if (output->tun_flags & TUNNEL_KEY &&
            nla_put_be64(skb, OVS_TUNNEL_KEY_ATTR_ID, output->tun_id,
                         OVS_TUNNEL_KEY_ATTR_PAD))
                return -EMSGSIZE;
+
+       if (mode & IP_TUNNEL_INFO_BRIDGE)
+               return nla_put_flag(skb, OVS_TUNNEL_KEY_ATTR_IPV4_INFO_BRIDGE)
+                      ? -EMSGSIZE : 0;
+
        switch (tun_proto) {
        case AF_INET:
                if (output->u.ipv4.src &&
@@ -918,17 +943,17 @@ static int __ip_tun_to_nlattr(struct sk_buff *skb,
 static int ip_tun_to_nlattr(struct sk_buff *skb,
                            const struct ip_tunnel_key *output,
                            const void *tun_opts, int swkey_tun_opts_len,
-                           unsigned short tun_proto)
+                           unsigned short tun_proto, u8 mode)
 {
        struct nlattr *nla;
        int err;
 
-       nla = nla_nest_start(skb, OVS_KEY_ATTR_TUNNEL);
+       nla = nla_nest_start_noflag(skb, OVS_KEY_ATTR_TUNNEL);
        if (!nla)
                return -EMSGSIZE;
 
        err = __ip_tun_to_nlattr(skb, output, tun_opts, swkey_tun_opts_len,
-                                tun_proto);
+                                tun_proto, mode);
        if (err)
                return err;
 
@@ -942,7 +967,7 @@ int ovs_nla_put_tunnel_info(struct sk_buff *skb,
        return __ip_tun_to_nlattr(skb, &tun_info->key,
                                  ip_tunnel_info_opts(tun_info),
                                  tun_info->options_len,
-                                 ip_tunnel_info_af(tun_info));
+                                 ip_tunnel_info_af(tun_info), tun_info->mode);
 }
 
 static int encode_vlan_from_nlattrs(struct sw_flow_match *match,
@@ -1932,7 +1957,7 @@ static int nsh_key_to_nlattr(const struct ovs_key_nsh *nsh, bool is_mask,
 {
        struct nlattr *start;
 
-       start = nla_nest_start(skb, OVS_KEY_ATTR_NSH);
+       start = nla_nest_start_noflag(skb, OVS_KEY_ATTR_NSH);
        if (!start)
                return -EMSGSIZE;
 
@@ -1980,7 +2005,7 @@ static int __ovs_nla_put_key(const struct sw_flow_key *swkey,
                        opts = TUN_METADATA_OPTS(output, swkey->tun_opts_len);
 
                if (ip_tun_to_nlattr(skb, &output->tun_key, opts,
-                                    swkey->tun_opts_len, swkey->tun_proto))
+                                    swkey->tun_opts_len, swkey->tun_proto, 0))
                        goto nla_put_failure;
        }
 
@@ -2015,14 +2040,15 @@ static int __ovs_nla_put_key(const struct sw_flow_key *swkey,
                if (swkey->eth.vlan.tci || eth_type_vlan(swkey->eth.type)) {
                        if (ovs_nla_put_vlan(skb, &output->eth.vlan, is_mask))
                                goto nla_put_failure;
-                       encap = nla_nest_start(skb, OVS_KEY_ATTR_ENCAP);
+                       encap = nla_nest_start_noflag(skb, OVS_KEY_ATTR_ENCAP);
                        if (!swkey->eth.vlan.tci)
                                goto unencap;
 
                        if (swkey->eth.cvlan.tci || eth_type_vlan(swkey->eth.type)) {
                                if (ovs_nla_put_vlan(skb, &output->eth.cvlan, is_mask))
                                        goto nla_put_failure;
-                               in_encap = nla_nest_start(skb, OVS_KEY_ATTR_ENCAP);
+                               in_encap = nla_nest_start_noflag(skb,
+                                                                OVS_KEY_ATTR_ENCAP);
                                if (!swkey->eth.cvlan.tci)
                                        goto unencap;
                        }
@@ -2201,7 +2227,7 @@ int ovs_nla_put_key(const struct sw_flow_key *swkey,
        int err;
        struct nlattr *nla;
 
-       nla = nla_nest_start(skb, attr);
+       nla = nla_nest_start_noflag(skb, attr);
        if (!nla)
                return -EMSGSIZE;
        err = __ovs_nla_put_key(swkey, output, is_mask, skb);
@@ -2605,6 +2631,8 @@ static int validate_and_copy_set_tun(const struct nlattr *attr,
        tun_info->mode = IP_TUNNEL_INFO_TX;
        if (key.tun_proto == AF_INET6)
                tun_info->mode |= IP_TUNNEL_INFO_IPV6;
+       else if (key.tun_proto == AF_INET && key.tun_key.u.ipv4.dst == 0)
+               tun_info->mode |= IP_TUNNEL_INFO_BRIDGE;
        tun_info->key = key.tun_key;
 
        /* We need to store the options in the action itself since
@@ -2826,8 +2854,8 @@ static int validate_userspace(const struct nlattr *attr)
        struct nlattr *a[OVS_USERSPACE_ATTR_MAX + 1];
        int error;
 
-       error = nla_parse_nested(a, OVS_USERSPACE_ATTR_MAX, attr,
-                                userspace_policy, NULL);
+       error = nla_parse_nested_deprecated(a, OVS_USERSPACE_ATTR_MAX, attr,
+                                           userspace_policy, NULL);
        if (error)
                return error;
 
@@ -2838,6 +2866,88 @@ static int validate_userspace(const struct nlattr *attr)
        return 0;
 }
 
+static const struct nla_policy cpl_policy[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 },
+};
+
+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)
+{
+       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_deprecated_strict(a, OVS_CHECK_PKT_LEN_ATTR_MAX,
+                                         nla_data(attr), nla_len(attr),
+                                         cpl_policy, 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_lesser_eq = a[OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL];
+       acts_if_greater = a[OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER];
+
+       /* 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_lesser_equal =
+               last || !actions_may_change_flow(acts_if_lesser_eq);
+       arg.exec_for_greater =
+               last || !actions_may_change_flow(acts_if_greater);
+
+       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_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);
+
+       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);
+       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 +2994,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 +3196,19 @@ 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;
@@ -3130,7 +3254,7 @@ static int sample_action_to_attr(const struct nlattr *attr,
        const struct sample_arg *arg;
        struct nlattr *actions;
 
-       start = nla_nest_start(skb, OVS_ACTION_ATTR_SAMPLE);
+       start = nla_nest_start_noflag(skb, OVS_ACTION_ATTR_SAMPLE);
        if (!start)
                return -EMSGSIZE;
 
@@ -3143,7 +3267,7 @@ static int sample_action_to_attr(const struct nlattr *attr,
                goto out;
        }
 
-       ac_start = nla_nest_start(skb, OVS_SAMPLE_ATTR_ACTIONS);
+       ac_start = nla_nest_start_noflag(skb, OVS_SAMPLE_ATTR_ACTIONS);
        if (!ac_start) {
                err = -EMSGSIZE;
                goto out;
@@ -3169,7 +3293,7 @@ static int clone_action_to_attr(const struct nlattr *attr,
        struct nlattr *start;
        int err = 0, rem = nla_len(attr);
 
-       start = nla_nest_start(skb, OVS_ACTION_ATTR_CLONE);
+       start = nla_nest_start_noflag(skb, OVS_ACTION_ATTR_CLONE);
        if (!start)
                return -EMSGSIZE;
 
@@ -3183,6 +3307,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_noflag(skb, OVS_ACTION_ATTR_CHECK_PKT_LEN);
+       if (!start)
+               return -EMSGSIZE;
+
+       /* The first nested attribute 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 nested attribute in 'attr' is always
+        * 'OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL'.
+        */
+       a = nla_next(cpl_arg, &rem);
+       ac_start =  nla_nest_start_noflag(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);
+       }
+
+       /* Third nested attribute in 'attr' is always
+        * OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER.
+        */
+       a = nla_next(a, &rem);
+       ac_start =  nla_nest_start_noflag(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);
+       }
+
+       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);
@@ -3195,14 +3388,14 @@ static int set_action_to_attr(const struct nlattr *a, struct sk_buff *skb)
                struct ovs_tunnel_info *ovs_tun = nla_data(ovs_key);
                struct ip_tunnel_info *tun_info = &ovs_tun->tun_dst->u.tun_info;
 
-               start = nla_nest_start(skb, OVS_ACTION_ATTR_SET);
+               start = nla_nest_start_noflag(skb, OVS_ACTION_ATTR_SET);
                if (!start)
                        return -EMSGSIZE;
 
                err =  ip_tun_to_nlattr(skb, &tun_info->key,
                                        ip_tunnel_info_opts(tun_info),
                                        tun_info->options_len,
-                                       ip_tunnel_info_af(tun_info));
+                                       ip_tunnel_info_af(tun_info), tun_info->mode);
                if (err)
                        return err;
                nla_nest_end(skb, start);
@@ -3227,7 +3420,7 @@ static int masked_set_action_to_set_action_attr(const struct nlattr *a,
        /* Revert the conversion we did from a non-masked set action to
         * masked set action.
         */
-       nla = nla_nest_start(skb, OVS_ACTION_ATTR_SET);
+       nla = nla_nest_start_noflag(skb, OVS_ACTION_ATTR_SET);
        if (!nla)
                return -EMSGSIZE;
 
@@ -3277,6 +3470,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;