datapath: Better handle vlan packets sent to userspace.
[openvswitch] / datapath / datapath.c
index 49d93aac5c2ff15e6e5c57b36a0e840f1c3f4f00..1ea5d1e9a7c2d850cf989b34c6a5f3e7b53d25ac 100644 (file)
@@ -1,13 +1,21 @@
 /*
- * Copyright (c) 2007, 2008, 2009, 2010, 2011 Nicira Networks.
- * Distributed under the terms of the GNU GPL version 2.
+ * Copyright (c) 2007-2011 Nicira Networks.
  *
- * Significant portions of this file may be copied from parts of the Linux
- * kernel, by Linus Torvalds and others.
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA
  */
 
-/* Functions for managing the dp interface/device. */
-
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 
 #include <linux/init.h>
@@ -87,7 +95,7 @@ static int queue_userspace_packet(int dp_ifindex, struct sk_buff *,
                                  const struct dp_upcall_info *);
 
 /* Must be called with rcu_read_lock, genl_mutex, or RTNL lock. */
-struct datapath *get_dp(int dp_ifindex)
+static struct datapath *get_dp(int dp_ifindex)
 {
        struct datapath *dp = NULL;
        struct net_device *dev;
@@ -103,7 +111,6 @@ struct datapath *get_dp(int dp_ifindex)
 
        return dp;
 }
-EXPORT_SYMBOL_GPL(get_dp);
 
 /* Must be called with genl_mutex. */
 static struct flow_table *get_table_protected(struct datapath *dp)
@@ -241,7 +248,7 @@ static void destroy_dp_rcu(struct rcu_head *rcu)
 {
        struct datapath *dp = container_of(rcu, struct datapath, rcu);
 
-       flow_tbl_destroy(dp->table);
+       flow_tbl_destroy((__force struct flow_table *)dp->table);
        free_percpu(dp->stats_percpu);
        kobject_put(&dp->ifobj);
 }
@@ -430,17 +437,28 @@ static int queue_userspace_packet(int dp_ifindex, struct sk_buff *skb,
                                  const struct dp_upcall_info *upcall_info)
 {
        struct ovs_header *upcall;
+       struct sk_buff *nskb = NULL;
        struct sk_buff *user_skb; /* to be queued to userspace */
        struct nlattr *nla;
        unsigned int len;
        int err;
 
-       err = vlan_deaccel_tag(skb);
-       if (unlikely(err))
-               return err;
+       if (vlan_tx_tag_present(skb)) {
+               nskb = skb_clone(skb, GFP_ATOMIC);
+               if (!nskb)
+                       return -ENOMEM;
+               
+               err = vlan_deaccel_tag(nskb);
+               if (err)
+                       return err;
+
+               skb = nskb;
+       }
 
-       if (nla_attr_size(skb->len) > USHRT_MAX)
-               return -EFBIG;
+       if (nla_attr_size(skb->len) > USHRT_MAX) {
+               err = -EFBIG;
+               goto out;
+       }
 
        len = sizeof(struct ovs_header);
        len += nla_total_size(skb->len);
@@ -449,8 +467,10 @@ static int queue_userspace_packet(int dp_ifindex, struct sk_buff *skb,
                len += nla_total_size(8);
 
        user_skb = genlmsg_new(len, GFP_ATOMIC);
-       if (!user_skb)
-               return -ENOMEM;
+       if (!user_skb) {
+               err = -ENOMEM;
+               goto out;
+       }
 
        upcall = genlmsg_put(user_skb, 0, 0, &dp_packet_genl_family,
                             0, upcall_info->cmd);
@@ -468,7 +488,11 @@ static int queue_userspace_packet(int dp_ifindex, struct sk_buff *skb,
 
        skb_copy_and_csum_dev(skb, nla_data(nla));
 
-       return genlmsg_unicast(&init_net, user_skb, upcall_info->pid);
+       err = genlmsg_unicast(&init_net, user_skb, upcall_info->pid);
+
+out:
+       kfree_skb(nskb);
+       return err;
 }
 
 /* Called with genl_mutex. */
@@ -524,10 +548,9 @@ static int validate_sample(const struct nlattr *attr,
        return validate_actions(actions, key, depth + 1);
 }
 
-static int validate_action_key(const struct nlattr *a,
-                               const struct sw_flow_key *flow_key)
+static int validate_set(const struct nlattr *a,
+                       const struct sw_flow_key *flow_key)
 {
-       int act_type = nla_type(a);
        const struct nlattr *ovs_key = nla_data(a);
        int key_type = nla_type(ovs_key);
 
@@ -539,27 +562,15 @@ static int validate_action_key(const struct nlattr *a,
            nla_len(ovs_key) != ovs_key_lens[key_type])
                return -EINVAL;
 
-#define ACTION(act, key)       (((act) << 8) | (key))
-
-       switch (ACTION(act_type, key_type)) {
+       switch (key_type) {
        const struct ovs_key_ipv4 *ipv4_key;
-       const struct ovs_key_8021q *q_key;
 
-       case ACTION(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_PRIORITY):
-       case ACTION(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_TUN_ID):
-       case ACTION(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_ETHERNET):
+       case OVS_KEY_ATTR_PRIORITY:
+       case OVS_KEY_ATTR_TUN_ID:
+       case OVS_KEY_ATTR_ETHERNET:
                break;
 
-       case ACTION(OVS_ACTION_ATTR_PUSH, OVS_KEY_ATTR_8021Q):
-               q_key = nla_data(ovs_key);
-               if (q_key->q_tpid != htons(ETH_P_8021Q))
-                       return -EINVAL;
-
-               if (q_key->q_tci & htons(VLAN_TAG_PRESENT))
-                       return -EINVAL;
-               break;
-
-       case ACTION(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_IPV4):
+       case OVS_KEY_ATTR_IPV4:
                if (flow_key->eth.type != htons(ETH_P_IP))
                        return -EINVAL;
 
@@ -575,7 +586,7 @@ static int validate_action_key(const struct nlattr *a,
 
                break;
 
-       case ACTION(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_TCP):
+       case OVS_KEY_ATTR_TCP:
                if (flow_key->ip.proto != IPPROTO_TCP)
                        return -EINVAL;
 
@@ -584,7 +595,7 @@ static int validate_action_key(const struct nlattr *a,
 
                break;
 
-       case ACTION(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_UDP):
+       case OVS_KEY_ATTR_UDP:
                if (flow_key->ip.proto != IPPROTO_UDP)
                        return -EINVAL;
 
@@ -595,7 +606,7 @@ static int validate_action_key(const struct nlattr *a,
        default:
                return -EINVAL;
        }
-#undef ACTION
+
        return 0;
 }
 
@@ -632,13 +643,14 @@ static int validate_actions(const struct nlattr *attr,
        nla_for_each_nested(a, attr, rem) {
                /* Expected argument lengths, (u32)-1 for variable length. */
                static const u32 action_lens[OVS_ACTION_ATTR_MAX + 1] = {
-                       [OVS_ACTION_ATTR_OUTPUT] = 4,
+                       [OVS_ACTION_ATTR_OUTPUT] = sizeof(u32),
                        [OVS_ACTION_ATTR_USERSPACE] = (u32)-1,
-                       [OVS_ACTION_ATTR_PUSH] = (u32)-1,
-                       [OVS_ACTION_ATTR_POP] = 2,
+                       [OVS_ACTION_ATTR_PUSH_VLAN] = sizeof(struct ovs_action_push_vlan),
+                       [OVS_ACTION_ATTR_POP_VLAN] = 0,
                        [OVS_ACTION_ATTR_SET] = (u32)-1,
                        [OVS_ACTION_ATTR_SAMPLE] = (u32)-1
                };
+               const struct ovs_action_push_vlan *vlan;
                int type = nla_type(a);
 
                if (type > OVS_ACTION_ATTR_MAX ||
@@ -662,14 +674,19 @@ static int validate_actions(const struct nlattr *attr,
                        break;
 
 
-               case OVS_ACTION_ATTR_POP:
-                       if (nla_get_u16(a) != OVS_KEY_ATTR_8021Q)
+               case OVS_ACTION_ATTR_POP_VLAN:
+                       break;
+
+               case OVS_ACTION_ATTR_PUSH_VLAN:
+                       vlan = nla_data(a);
+                       if (vlan->vlan_tpid != htons(ETH_P_8021Q))
+                               return -EINVAL;
+                       if (!(vlan->vlan_tci & htons(VLAN_TAG_PRESENT)))
                                return -EINVAL;
                        break;
 
                case OVS_ACTION_ATTR_SET:
-               case OVS_ACTION_ATTR_PUSH:
-                       err = validate_action_key(a, key);
+                       err = validate_set(a, key);
                        if (err)
                                return err;
                        break;
@@ -1261,7 +1278,7 @@ static int ovs_dp_cmd_fill_info(struct datapath *dp, struct sk_buff *skb,
                                u32 pid, u32 seq, u32 flags, u8 cmd)
 {
        struct ovs_header *ovs_header;
-       struct nlattr *nla;
+       struct ovs_dp_stats dp_stats;
        int err;
 
        ovs_header = genlmsg_put(skb, pid, seq, &dp_datapath_genl_family,
@@ -1277,10 +1294,8 @@ static int ovs_dp_cmd_fill_info(struct datapath *dp, struct sk_buff *skb,
        if (err)
                goto nla_put_failure;
 
-       nla = nla_reserve(skb, OVS_DP_ATTR_STATS, sizeof(struct ovs_dp_stats));
-       if (!nla)
-               goto nla_put_failure;
-       get_dp_stats(dp, nla_data(nla));
+       get_dp_stats(dp, &dp_stats);
+       NLA_PUT(skb, OVS_DP_ATTR_STATS, sizeof(struct ovs_dp_stats), &dp_stats);
 
        return genlmsg_end(skb, ovs_header);
 
@@ -1604,7 +1619,7 @@ static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb,
                                   u32 pid, u32 seq, u32 flags, u8 cmd)
 {
        struct ovs_header *ovs_header;
-       struct nlattr *nla;
+       struct ovs_vport_stats vport_stats;
        int err;
 
        ovs_header = genlmsg_put(skb, pid, seq, &dp_vport_genl_family,
@@ -1619,12 +1634,9 @@ static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb,
        NLA_PUT_STRING(skb, OVS_VPORT_ATTR_NAME, vport->ops->get_name(vport));
        NLA_PUT_U32(skb, OVS_VPORT_ATTR_UPCALL_PID, vport->upcall_pid);
 
-       nla = nla_reserve(skb, OVS_VPORT_ATTR_STATS,
-                         sizeof(struct ovs_vport_stats));
-       if (!nla)
-               goto nla_put_failure;
-
-       vport_get_stats(vport, nla_data(nla));
+       vport_get_stats(vport, &vport_stats);
+       NLA_PUT(skb, OVS_VPORT_ATTR_STATS, sizeof(struct ovs_vport_stats),
+               &vport_stats);
 
        NLA_PUT(skb, OVS_VPORT_ATTR_ADDRESS, ETH_ALEN,
                vport->ops->get_addr(vport));
@@ -2039,7 +2051,7 @@ static int __init dp_init(void)
 
        BUILD_BUG_ON(sizeof(struct ovs_skb_cb) > sizeof(dummy_skb->cb));
 
-       pr_info("Open vSwitch %s, built "__DATE__" "__TIME__"\n",
+       pr_info("Open vSwitch switching datapath %s, built "__DATE__" "__TIME__"\n",
                VERSION BUILDNR);
 
        err = tnl_init();