datapath: Better handle vlan packets sent to userspace.
[openvswitch] / datapath / datapath.c
index 06d371060cf49401f614d97665d773eadf756b91..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>
 #include <linux/openvswitch.h>
 #include <linux/rculist.h>
 #include <linux/dmi.h>
-#include <net/inet_ecn.h>
 #include <net/genetlink.h>
 
 #include "checksum.h"
 #include "datapath.h"
-#include "actions.h"
 #include "flow.h"
 #include "vlan.h"
 #include "tunnel.h"
 #include "vport-internal_dev.h"
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) || \
-    LINUX_VERSION_CODE >= KERNEL_VERSION(3,2,0)
-#error Kernels before 2.6.18 or after 3.1 are not supported by this version of Open vSwitch.
+    LINUX_VERSION_CODE > KERNEL_VERSION(3,2,0)
+#error Kernels before 2.6.18 or after 3.2 are not supported by this version of Open vSwitch.
 #endif
 
 int (*dp_ioctl_hook)(struct net_device *dev, struct ifreq *rq, int cmd);
@@ -89,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;
@@ -105,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)
@@ -122,7 +127,8 @@ static struct vport *get_vport_protected(struct datapath *dp, u16 port_no)
 /* Must be called with rcu_read_lock or RTNL lock. */
 const char *dp_name(const struct datapath *dp)
 {
-       return vport_get_name(rcu_dereference_rtnl(dp->ports[OVSP_LOCAL]));
+       struct vport *vport = rcu_dereference_rtnl(dp->ports[OVSP_LOCAL]);
+       return vport->ops->get_name(vport);
 }
 
 static int get_dpifindex(struct datapath *dp)
@@ -134,7 +140,7 @@ static int get_dpifindex(struct datapath *dp)
 
        local = get_vport_protected(dp, OVSP_LOCAL);
        if (local)
-               ifindex = vport_get_ifindex(local);
+               ifindex = local->ops->get_ifindex(local);
        else
                ifindex = 0;
 
@@ -159,12 +165,11 @@ static int dp_fill_ifinfo(struct sk_buff *skb,
                          int event, unsigned int flags)
 {
        struct datapath *dp = port->dp;
-       int ifindex = vport_get_ifindex(port);
        struct ifinfomsg *hdr;
        struct nlmsghdr *nlh;
 
-       if (ifindex < 0)
-               return ifindex;
+       if (!port->ops->get_ifindex)
+               return -ENODEV;
 
        nlh = nlmsg_put(skb, 0, 0, event, sizeof(*hdr), flags);
        if (nlh == NULL)
@@ -174,21 +179,21 @@ static int dp_fill_ifinfo(struct sk_buff *skb,
        hdr->ifi_family = AF_BRIDGE;
        hdr->__ifi_pad = 0;
        hdr->ifi_type = ARPHRD_ETHER;
-       hdr->ifi_index = ifindex;
-       hdr->ifi_flags = vport_get_flags(port);
+       hdr->ifi_index = port->ops->get_ifindex(port);
+       hdr->ifi_flags = port->ops->get_dev_flags(port);
        hdr->ifi_change = 0;
 
-       NLA_PUT_STRING(skb, IFLA_IFNAME, vport_get_name(port));
+       NLA_PUT_STRING(skb, IFLA_IFNAME, port->ops->get_name(port));
        NLA_PUT_U32(skb, IFLA_MASTER, get_dpifindex(dp));
-       NLA_PUT_U32(skb, IFLA_MTU, vport_get_mtu(port));
+       NLA_PUT_U32(skb, IFLA_MTU, port->ops->get_mtu(port));
 #ifdef IFLA_OPERSTATE
        NLA_PUT_U8(skb, IFLA_OPERSTATE,
-                  vport_is_running(port)
-                       ? vport_get_operstate(port)
+                  port->ops->is_running(port)
+                       ? port->ops->get_operstate(port)
                        : IF_OPER_DOWN);
 #endif
 
-       NLA_PUT(skb, IFLA_ADDRESS, ETH_ALEN, vport_get_addr(port));
+       NLA_PUT(skb, IFLA_ADDRESS, ETH_ALEN, port->ops->get_addr(port));
 
        return nlmsg_end(skb, nlh);
 
@@ -201,24 +206,32 @@ nla_put_failure:
 static void dp_ifinfo_notify(int event, struct vport *port)
 {
        struct sk_buff *skb;
-       int err = -ENOBUFS;
+       int err;
 
        skb = nlmsg_new(br_nlmsg_size(), GFP_KERNEL);
-       if (skb == NULL)
-               goto errout;
+       if (!skb) {
+               err = -ENOBUFS;
+               goto err;
+       }
 
        err = dp_fill_ifinfo(skb, port, event, 0);
        if (err < 0) {
-               /* -EMSGSIZE implies BUG in br_nlmsg_size() */
-               WARN_ON(err == -EMSGSIZE);
-               kfree_skb(skb);
-               goto errout;
+               if (err == -ENODEV) {
+                       goto out;
+               } else {
+                       /* -EMSGSIZE implies BUG in br_nlmsg_size() */
+                       WARN_ON(err == -EMSGSIZE);
+                       goto err;
+               }
        }
+
        rtnl_notify(skb, &init_net, 0, RTNLGRP_LINK, NULL, GFP_KERNEL);
+
        return;
-errout:
-       if (err < 0)
-               rtnl_set_sk_err(&init_net, RTNLGRP_LINK, err);
+err:
+       rtnl_set_sk_err(&init_net, RTNLGRP_LINK, err);
+out:
+       kfree_skb(skb);
 }
 
 static void release_dp(struct kobject *kobj)
@@ -235,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);
 }
@@ -328,21 +341,6 @@ out:
        write_seqcount_end(&stats->seqlock);
 }
 
-static void copy_and_csum_skb(struct sk_buff *skb, void *to)
-{
-       u16 csum_start, csum_offset;
-       __wsum csum;
-
-       get_skb_csum_pointers(skb, &csum_start, &csum_offset);
-       csum_start -= skb_headroom(skb);
-
-       skb_copy_bits(skb, 0, to, csum_start);
-
-       csum = skb_copy_and_csum_bits(skb, csum_start, to + csum_start,
-                                     skb->len - csum_start, 0);
-       *(__sum16 *)(to + csum_start + csum_offset) = csum_fold(csum);
-}
-
 static struct genl_family dp_packet_genl_family = {
        .id = GENL_ID_GENERATE,
        .hdrsize = sizeof(struct ovs_header),
@@ -415,8 +413,7 @@ static int queue_gso_packets(int dp_ifindex, struct sk_buff *skb,
                         * properly mark later fragments.
                         */
                        later_key = *upcall_info->key;
-                       later_key.ip.tos_frag &= ~OVS_FRAG_TYPE_MASK;
-                       later_key.ip.tos_frag |= OVS_FRAG_TYPE_LATER;
+                       later_key.ip.frag = OVS_FRAG_TYPE_LATER;
 
                        later_info = *upcall_info;
                        later_info.key = &later_key;
@@ -440,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);
@@ -459,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);
@@ -475,12 +485,14 @@ static int queue_userspace_packet(int dp_ifindex, struct sk_buff *skb,
                            nla_get_u64(upcall_info->userdata));
 
        nla = __nla_reserve(user_skb, OVS_PACKET_ATTR_PACKET, skb->len);
-       if (skb->ip_summed == CHECKSUM_PARTIAL)
-               copy_and_csum_skb(skb, nla_data(nla));
-       else
-               skb_copy_bits(skb, 0, nla_data(nla), skb->len);
 
-       return genlmsg_unicast(&init_net, user_skb, upcall_info->pid);
+       skb_copy_and_csum_dev(skb, nla_data(nla));
+
+       err = genlmsg_unicast(&init_net, user_skb, upcall_info->pid);
+
+out:
+       kfree_skb(nskb);
+       return err;
 }
 
 /* Called with genl_mutex. */
@@ -536,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);
 
@@ -551,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):
-               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;
+       case OVS_KEY_ATTR_PRIORITY:
+       case OVS_KEY_ATTR_TUN_ID:
+       case OVS_KEY_ATTR_ETHERNET:
                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;
 
@@ -582,16 +581,12 @@ static int validate_action_key(const struct nlattr *a,
                if (ipv4_key->ipv4_proto != flow_key->ip.proto)
                        return -EINVAL;
 
-               if (ipv4_key->ipv4_tos & INET_ECN_MASK)
-                       return -EINVAL;
-
-               if (ipv4_key->ipv4_frag !=
-                   (flow_key->ip.tos_frag & OVS_FRAG_TYPE_MASK))
+               if (ipv4_key->ipv4_frag != flow_key->ip.frag)
                        return -EINVAL;
 
                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;
 
@@ -600,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;
 
@@ -611,7 +606,7 @@ static int validate_action_key(const struct nlattr *a,
        default:
                return -EINVAL;
        }
-#undef ACTION
+
        return 0;
 }
 
@@ -648,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 ||
@@ -678,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;
@@ -1277,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,
@@ -1293,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);
 
@@ -1620,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,
@@ -1631,18 +1630,16 @@ static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb,
        ovs_header->dp_ifindex = get_dpifindex(vport->dp);
 
        NLA_PUT_U32(skb, OVS_VPORT_ATTR_PORT_NO, vport->port_no);
-       NLA_PUT_U32(skb, OVS_VPORT_ATTR_TYPE, vport_get_type(vport));
-       NLA_PUT_STRING(skb, OVS_VPORT_ATTR_NAME, vport_get_name(vport));
+       NLA_PUT_U32(skb, OVS_VPORT_ATTR_TYPE, vport->ops->type);
+       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_get_addr(vport));
+       NLA_PUT(skb, OVS_VPORT_ATTR_ADDRESS, ETH_ALEN,
+               vport->ops->get_addr(vport));
 
        err = vport_get_options(vport, skb);
        if (err == -EMSGSIZE)
@@ -1830,7 +1827,7 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info)
 
        err = 0;
        if (a[OVS_VPORT_ATTR_TYPE] &&
-           nla_get_u32(a[OVS_VPORT_ATTR_TYPE]) != vport_get_type(vport))
+           nla_get_u32(a[OVS_VPORT_ATTR_TYPE]) != vport->ops->type)
                err = -EINVAL;
 
        if (!err && a[OVS_VPORT_ATTR_OPTIONS])
@@ -2054,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();