datapath: Better handle vlan packets sent to userspace.
[openvswitch] / datapath / datapath.c
index c43adf9fa6d492871eedc33e8cb755822980c088..1ea5d1e9a7c2d850cf989b34c6a5f3e7b53d25ac 100644 (file)
@@ -437,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);
@@ -456,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,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. */
@@ -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));