datapath: Better handle vlan packets sent to userspace.
[openvswitch] / datapath / datapath.c
index 317c404a908dbd131cff38db928763638e5c5164..1ea5d1e9a7c2d850cf989b34c6a5f3e7b53d25ac 100644 (file)
@@ -95,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;
@@ -111,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)
@@ -249,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);
 }
@@ -438,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;
 
-       if (nla_attr_size(skb->len) > USHRT_MAX)
-               return -EFBIG;
+               skb = nskb;
+       }
+
+       if (nla_attr_size(skb->len) > USHRT_MAX) {
+               err = -EFBIG;
+               goto out;
+       }
 
        len = sizeof(struct ovs_header);
        len += nla_total_size(skb->len);
@@ -457,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);
@@ -476,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. */
@@ -1262,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,
@@ -1278,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);
 
@@ -1605,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,
@@ -1620,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));
@@ -2040,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();