datapath: Fix kernel panic on ovs_flow_free
[cascardo/ovs.git] / datapath / datapath.c
index eb07a89..db89a39 100644 (file)
 #include <net/net_namespace.h>
 #include <net/netns/generic.h>
 
-#include "checksum.h"
 #include "datapath.h"
 #include "flow.h"
 #include "vlan.h"
-#include "tunnel.h"
 #include "vport-internal_dev.h"
 #include "vport-netdev.h"
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) || \
-    LINUX_VERSION_CODE >= KERNEL_VERSION(3,9,0)
-#error Kernels before 2.6.18 or after 3.8 are not supported by this version of Open vSwitch.
-#endif
-
 #define REHASH_FLOW_INTERVAL (10 * 60 * HZ)
 static void rehash_flow_table(struct work_struct *work);
 static DECLARE_DELAYED_WORK(rehash_flow_wq, rehash_flow_table);
@@ -173,7 +166,6 @@ static void destroy_dp_rcu(struct rcu_head *rcu)
 {
        struct datapath *dp = container_of(rcu, struct datapath, rcu);
 
-       ovs_flow_tbl_destroy((__force struct flow_table *)dp->table, false);
        free_percpu(dp->stats_percpu);
        release_net(ovs_dp_get_net(dp));
        kfree(dp->ports);
@@ -279,7 +271,8 @@ static struct genl_family dp_packet_genl_family = {
        .name = OVS_PACKET_FAMILY,
        .version = OVS_PACKET_VERSION,
        .maxattr = OVS_PACKET_ATTR_MAX,
-        SET_NETNSOK
+       .netnsok = true,
+        SET_PARALLEL_OPS
 };
 
 int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb,
@@ -300,8 +293,6 @@ int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb,
                goto err;
        }
 
-       forward_ip_summed(skb, true);
-
        if (!skb_is_gso(skb))
                err = queue_userspace_packet(ovs_dp_get_net(dp), dp_ifindex, skb, upcall_info);
        else
@@ -419,10 +410,12 @@ static int queue_userspace_packet(struct net *net, int dp_ifindex,
                nskb = skb_clone(skb, GFP_ATOMIC);
                if (!nskb)
                        return -ENOMEM;
-               
-               err = vlan_deaccel_tag(nskb);
-               if (err)
-                       return err;
+
+               nskb = __vlan_put_tag(nskb, nskb->vlan_proto, vlan_tx_tag_get(nskb));
+               if (!nskb)
+                       return -ENOMEM;
+
+               vlan_set_tci(nskb, 0);
 
                skb = nskb;
        }
@@ -624,7 +617,7 @@ static int validate_and_copy_set_tun(const struct nlattr *attr,
        int err, start;
 
        ovs_match_init(&match, &key, NULL);
-       err = ipv4_tun_from_nlattr(nla_data(attr), &match, false);
+       err = ovs_ipv4_tun_from_nlattr(nla_data(attr), &match, false);
        if (err)
                return err;
 
@@ -662,14 +655,8 @@ static int validate_set(const struct nlattr *a,
        int err;
 
        case OVS_KEY_ATTR_PRIORITY:
-       case OVS_KEY_ATTR_ETHERNET:
-               break;
-
        case OVS_KEY_ATTR_SKB_MARK:
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) && !defined(CONFIG_NETFILTER)
-               if (nla_get_u32(ovs_key) != 0)
-                       return -EINVAL;
-#endif
+       case OVS_KEY_ATTR_ETHERNET:
                break;
 
        case OVS_KEY_ATTR_TUNNEL:
@@ -726,6 +713,12 @@ static int validate_set(const struct nlattr *a,
 
                return validate_tp_port(flow_key);
 
+       case OVS_KEY_ATTR_SCTP:
+               if (flow_key->ip.proto != IPPROTO_SCTP)
+                       return -EINVAL;
+
+               return validate_tp_port(flow_key);
+
        default:
                return -EINVAL;
        }
@@ -926,7 +919,7 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
        OVS_CB(packet)->flow = flow;
        OVS_CB(packet)->pkt_key = &flow->key;
        packet->priority = flow->key.phy.priority;
-       skb_set_mark(packet, flow->key.phy.skb_mark);
+       packet->mark = flow->key.phy.skb_mark;
 
        rcu_read_lock();
        dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
@@ -953,11 +946,7 @@ err:
 }
 
 static const struct nla_policy packet_policy[OVS_PACKET_ATTR_MAX + 1] = {
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,18)
        [OVS_PACKET_ATTR_PACKET] = { .len = ETH_HLEN },
-#else
-       [OVS_PACKET_ATTR_PACKET] = { .minlen = ETH_HLEN },
-#endif
        [OVS_PACKET_ATTR_KEY] = { .type = NLA_NESTED },
        [OVS_PACKET_ATTR_ACTIONS] = { .type = NLA_NESTED },
 };
@@ -1009,7 +998,8 @@ static struct genl_family dp_flow_genl_family = {
        .name = OVS_FLOW_FAMILY,
        .version = OVS_FLOW_VERSION,
        .maxattr = OVS_FLOW_ATTR_MAX,
-        SET_NETNSOK
+       .netnsok = true,
+        SET_PARALLEL_OPS
 };
 
 static struct genl_multicast_group ovs_dp_flow_multicast_group = {
@@ -1065,8 +1055,8 @@ static int set_action_to_attr(const struct nlattr *a, struct sk_buff *skb)
                if (!start)
                        return -EMSGSIZE;
 
-               err = ipv4_tun_to_nlattr(skb,
-                               nla_data(ovs_key), nla_data(ovs_key));
+               err = ovs_ipv4_tun_to_nlattr(skb, nla_data(ovs_key),
+                                            nla_data(ovs_key));
                if (err)
                        return err;
                nla_nest_end(skb, start);
@@ -1127,7 +1117,6 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
                                  u32 seq, u32 flags, u8 cmd)
 {
        const int skb_orig_len = skb->len;
-       struct sw_flow_mask *mask;
        struct nlattr *start;
        struct ovs_flow_stats stats;
        struct ovs_header *ovs_header;
@@ -1157,8 +1146,7 @@ static int ovs_flow_cmd_fill_info(struct sw_flow *flow, struct datapath *dp,
        if (!nla)
                goto nla_put_failure;
 
-       mask = rcu_dereference_check(flow->mask, lockdep_ovsl_is_held());
-       err = ovs_flow_to_nlattrs(&flow->key, &mask->key, skb);
+       err = ovs_flow_to_nlattrs(&flow->key, &flow->mask->key, skb);
        if (err)
                goto error;
 
@@ -1251,7 +1239,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
 {
        struct nlattr **a = info->attrs;
        struct ovs_header *ovs_header = info->userhdr;
-       struct sw_flow_key key;
+       struct sw_flow_key key, masked_key;
        struct sw_flow *flow = NULL;
        struct sw_flow_mask mask;
        struct sk_buff *reply;
@@ -1279,9 +1267,13 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
                if (IS_ERR(acts))
                        goto error;
 
-               error = validate_and_copy_actions(a[OVS_FLOW_ATTR_ACTIONS], &key,  0, &acts);
-               if (error)
+               ovs_flow_key_mask(&masked_key, &key, &mask);
+               error = validate_and_copy_actions(a[OVS_FLOW_ATTR_ACTIONS],
+                                                 &masked_key, 0, &acts);
+               if (error) {
+                       OVS_NLERR("Flow actions may not be safe on all matching packets.\n");
                        goto err_kfree;
+               }
        } else if (info->genlhdr->cmd == OVS_FLOW_CMD_NEW) {
                error = -EINVAL;
                goto error;
@@ -1324,6 +1316,9 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
                }
                clear_stats(flow);
 
+               flow->key = masked_key;
+               flow->unmasked_key = key;
+
                /* Make sure mask is unique in the system */
                mask_p = ovs_sw_flow_mask_find(table, &mask);
                if (!mask_p) {
@@ -1337,11 +1332,11 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
                }
 
                ovs_sw_flow_mask_add_ref(mask_p);
-               rcu_assign_pointer(flow->mask, mask_p);
+               flow->mask = mask_p;
                rcu_assign_pointer(flow->sf_acts, acts);
 
                /* Put flow in bucket. */
-               ovs_flow_insert(table, flow, &key, match.range.end);
+               ovs_flow_insert(table, flow);
 
                reply = ovs_flow_cmd_build_info(flow, dp, info->snd_portid,
                                                info->snd_seq, OVS_FLOW_CMD_NEW);
@@ -1387,7 +1382,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
        if (!IS_ERR(reply))
                ovs_notify(reply, info, &ovs_dp_flow_multicast_group);
        else
-               netlink_set_err(GENL_SOCK(sock_net(skb->sk)), 0,
+               netlink_set_err(sock_net(skb->sk)->genl_sock, 0,
                                ovs_dp_flow_multicast_group.id, PTR_ERR(reply));
        return 0;
 
@@ -1571,9 +1566,7 @@ static struct genl_ops dp_flow_genl_ops[] = {
 };
 
 static const struct nla_policy datapath_policy[OVS_DP_ATTR_MAX + 1] = {
-#ifdef HAVE_NLA_NUL_STRING
        [OVS_DP_ATTR_NAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 },
-#endif
        [OVS_DP_ATTR_UPCALL_PID] = { .type = NLA_U32 },
 };
 
@@ -1583,7 +1576,8 @@ static struct genl_family dp_datapath_genl_family = {
        .name = OVS_DATAPATH_FAMILY,
        .version = OVS_DATAPATH_VERSION,
        .maxattr = OVS_DP_ATTR_MAX,
-        SET_NETNSOK
+       .netnsok = true,
+        SET_PARALLEL_OPS
 };
 
 static struct genl_multicast_group ovs_dp_datapath_multicast_group = {
@@ -1650,11 +1644,6 @@ static struct sk_buff *ovs_dp_cmd_build_info(struct datapath *dp, u32 portid,
        return skb;
 }
 
-static int ovs_dp_cmd_validate(struct nlattr *a[OVS_DP_ATTR_MAX + 1])
-{
-       return CHECK_NUL_STRING(a[OVS_DP_ATTR_NAME], IFNAMSIZ - 1);
-}
-
 /* Called with ovs_mutex. */
 static struct datapath *lookup_datapath(struct net *net,
                                        struct ovs_header *ovs_header,
@@ -1689,10 +1678,6 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
        if (!a[OVS_DP_ATTR_NAME] || !a[OVS_DP_ATTR_UPCALL_PID])
                goto err;
 
-       err = ovs_dp_cmd_validate(a);
-       if (err)
-               goto err;
-
        ovs_lock();
 
        err = -ENOMEM;
@@ -1793,6 +1778,8 @@ static void __dp_destroy(struct datapath *dp)
         */
        ovs_dp_detach_port(ovs_vport_ovsl(dp, OVSP_LOCAL));
 
+       ovs_flow_tbl_destroy((__force struct flow_table *)dp->table, true);
+
        call_rcu(&dp->rcu, destroy_dp_rcu);
 }
 
@@ -1802,10 +1789,6 @@ static int ovs_dp_cmd_del(struct sk_buff *skb, struct genl_info *info)
        struct datapath *dp;
        int err;
 
-       err = ovs_dp_cmd_validate(info->attrs);
-       if (err)
-               return err;
-
        ovs_lock();
        dp = lookup_datapath(sock_net(skb->sk), info->userhdr, info->attrs);
        err = PTR_ERR(dp);
@@ -1835,10 +1818,6 @@ static int ovs_dp_cmd_set(struct sk_buff *skb, struct genl_info *info)
        struct datapath *dp;
        int err;
 
-       err = ovs_dp_cmd_validate(info->attrs);
-       if (err)
-               return err;
-
        ovs_lock();
        dp = lookup_datapath(sock_net(skb->sk), info->userhdr, info->attrs);
        err = PTR_ERR(dp);
@@ -1849,7 +1828,7 @@ static int ovs_dp_cmd_set(struct sk_buff *skb, struct genl_info *info)
                                      info->snd_seq, OVS_DP_CMD_NEW);
        if (IS_ERR(reply)) {
                err = PTR_ERR(reply);
-               netlink_set_err(GENL_SOCK(sock_net(skb->sk)), 0,
+               netlink_set_err(sock_net(skb->sk)->genl_sock, 0,
                                ovs_dp_datapath_multicast_group.id, err);
                err = 0;
                goto unlock;
@@ -1870,10 +1849,6 @@ static int ovs_dp_cmd_get(struct sk_buff *skb, struct genl_info *info)
        struct datapath *dp;
        int err;
 
-       err = ovs_dp_cmd_validate(info->attrs);
-       if (err)
-               return err;
-
        ovs_lock();
        dp = lookup_datapath(sock_net(skb->sk), info->userhdr, info->attrs);
        if (IS_ERR(dp)) {
@@ -1944,12 +1919,8 @@ static struct genl_ops dp_datapath_genl_ops[] = {
 };
 
 static const struct nla_policy vport_policy[OVS_VPORT_ATTR_MAX + 1] = {
-#ifdef HAVE_NLA_NUL_STRING
        [OVS_VPORT_ATTR_NAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 },
        [OVS_VPORT_ATTR_STATS] = { .len = sizeof(struct ovs_vport_stats) },
-#else
-       [OVS_VPORT_ATTR_STATS] = { .minlen = sizeof(struct ovs_vport_stats) },
-#endif
        [OVS_VPORT_ATTR_PORT_NO] = { .type = NLA_U32 },
        [OVS_VPORT_ATTR_TYPE] = { .type = NLA_U32 },
        [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NLA_U32 },
@@ -1962,7 +1933,8 @@ static struct genl_family dp_vport_genl_family = {
        .name = OVS_VPORT_FAMILY,
        .version = OVS_VPORT_VERSION,
        .maxattr = OVS_VPORT_ATTR_MAX,
-        SET_NETNSOK
+       .netnsok = true,
+        SET_PARALLEL_OPS
 };
 
 struct genl_multicast_group ovs_dp_vport_multicast_group = {
@@ -2025,11 +1997,6 @@ struct sk_buff *ovs_vport_cmd_build_info(struct vport *vport, u32 portid,
        return skb;
 }
 
-static int ovs_vport_cmd_validate(struct nlattr *a[OVS_VPORT_ATTR_MAX + 1])
-{
-       return CHECK_NUL_STRING(a[OVS_VPORT_ATTR_NAME], IFNAMSIZ - 1);
-}
-
 /* Called with ovs_mutex or RCU read lock. */
 static struct vport *lookup_vport(struct net *net,
                                  struct ovs_header *ovs_header,
@@ -2080,10 +2047,6 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info)
            !a[OVS_VPORT_ATTR_UPCALL_PID])
                goto exit;
 
-       err = ovs_vport_cmd_validate(a);
-       if (err)
-               goto exit;
-
        ovs_lock();
        dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
        err = -ENODEV;
@@ -2152,10 +2115,6 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info)
        struct vport *vport;
        int err;
 
-       err = ovs_vport_cmd_validate(a);
-       if (err)
-               goto exit;
-
        ovs_lock();
        vport = lookup_vport(sock_net(skb->sk), info->userhdr, a);
        err = PTR_ERR(vport);
@@ -2198,7 +2157,6 @@ exit_free:
        kfree_skb(reply);
 exit_unlock:
        ovs_unlock();
-exit:
        return err;
 }
 
@@ -2209,10 +2167,6 @@ static int ovs_vport_cmd_del(struct sk_buff *skb, struct genl_info *info)
        struct vport *vport;
        int err;
 
-       err = ovs_vport_cmd_validate(a);
-       if (err)
-               goto exit;
-
        ovs_lock();
        vport = lookup_vport(sock_net(skb->sk), info->userhdr, a);
        err = PTR_ERR(vport);
@@ -2237,7 +2191,6 @@ static int ovs_vport_cmd_del(struct sk_buff *skb, struct genl_info *info)
 
 exit_unlock:
        ovs_unlock();
-exit:
        return err;
 }
 
@@ -2249,10 +2202,6 @@ static int ovs_vport_cmd_get(struct sk_buff *skb, struct genl_info *info)
        struct vport *vport;
        int err;
 
-       err = ovs_vport_cmd_validate(a);
-       if (err)
-               goto exit;
-
        rcu_read_lock();
        vport = lookup_vport(sock_net(skb->sk), ovs_header, a);
        err = PTR_ERR(vport);
@@ -2271,7 +2220,6 @@ static int ovs_vport_cmd_get(struct sk_buff *skb, struct genl_info *info)
 
 exit_unlock:
        rcu_read_unlock();
-exit:
        return err;
 }
 
@@ -2452,6 +2400,8 @@ static struct pernet_operations ovs_net_ops = {
        .size = sizeof(struct ovs_net),
 };
 
+DEFINE_COMPAT_PNET_REG_FUNC(device);
+
 static int __init dp_init(void)
 {
        int err;