OSDN Git Service

net: accept UFO datagrams from tuntap and packet
[uclinux-h8/linux.git] / net / openvswitch / datapath.c
index c3aec62..99cfafc 100644 (file)
@@ -55,6 +55,7 @@
 #include "flow.h"
 #include "flow_table.h"
 #include "flow_netlink.h"
+#include "meter.h"
 #include "vport-internal_dev.h"
 #include "vport-netdev.h"
 
@@ -142,35 +143,6 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *,
                                  const struct dp_upcall_info *,
                                  uint32_t cutlen);
 
-/* Must be called with rcu_read_lock. */
-static struct datapath *get_dp_rcu(struct net *net, int dp_ifindex)
-{
-       struct net_device *dev = dev_get_by_index_rcu(net, dp_ifindex);
-
-       if (dev) {
-               struct vport *vport = ovs_internal_dev_get_vport(dev);
-               if (vport)
-                       return vport->dp;
-       }
-
-       return NULL;
-}
-
-/* The caller must hold either ovs_mutex or rcu_read_lock to keep the
- * returned dp pointer valid.
- */
-static inline struct datapath *get_dp(struct net *net, int dp_ifindex)
-{
-       struct datapath *dp;
-
-       WARN_ON_ONCE(!rcu_read_lock_held() && !lockdep_ovsl_is_held());
-       rcu_read_lock();
-       dp = get_dp_rcu(net, dp_ifindex);
-       rcu_read_unlock();
-
-       return dp;
-}
-
 /* Must be called with rcu_read_lock or ovs_mutex. */
 const char *ovs_dp_name(const struct datapath *dp)
 {
@@ -203,6 +175,7 @@ static void destroy_dp_rcu(struct rcu_head *rcu)
        ovs_flow_tbl_destroy(&dp->table);
        free_percpu(dp->stats_percpu);
        kfree(dp->ports);
+       ovs_meters_exit(dp);
        kfree(dp);
 }
 
@@ -335,6 +308,8 @@ static int queue_gso_packets(struct datapath *dp, struct sk_buff *skb,
                             const struct dp_upcall_info *upcall_info,
                                 uint32_t cutlen)
 {
+       unsigned short gso_type = skb_shinfo(skb)->gso_type;
+       struct sw_flow_key later_key;
        struct sk_buff *segs, *nskb;
        int err;
 
@@ -345,9 +320,21 @@ static int queue_gso_packets(struct datapath *dp, struct sk_buff *skb,
        if (segs == NULL)
                return -EINVAL;
 
+       if (gso_type & SKB_GSO_UDP) {
+               /* The initial flow key extracted by ovs_flow_key_extract()
+                * in this case is for a first fragment, so we need to
+                * properly mark later fragments.
+                */
+               later_key = *key;
+               later_key.ip.frag = OVS_FRAG_TYPE_LATER;
+       }
+
        /* Queue all of the segments. */
        skb = segs;
        do {
+               if (gso_type & SKB_GSO_UDP && skb != segs)
+                       key = &later_key;
+
                err = queue_userspace_packet(dp, skb, key, upcall_info, cutlen);
                if (err)
                        break;
@@ -1601,6 +1588,10 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
        for (i = 0; i < DP_VPORT_HASH_BUCKETS; i++)
                INIT_HLIST_HEAD(&dp->ports[i]);
 
+       err = ovs_meters_init(dp);
+       if (err)
+               goto err_destroy_ports_array;
+
        /* Set up our datapath device. */
        parms.name = nla_data(a[OVS_DP_ATTR_NAME]);
        parms.type = OVS_VPORT_TYPE_INTERNAL;
@@ -1629,7 +1620,7 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
                                ovs_dp_reset_user_features(skb, info);
                }
 
-               goto err_destroy_ports_array;
+               goto err_destroy_meters;
        }
 
        err = ovs_dp_cmd_fill_info(dp, reply, info->snd_portid,
@@ -1644,8 +1635,10 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
        ovs_notify(&dp_datapath_genl_family, reply, info);
        return 0;
 
-err_destroy_ports_array:
+err_destroy_meters:
        ovs_unlock();
+       ovs_meters_exit(dp);
+err_destroy_ports_array:
        kfree(dp->ports);
 err_destroy_percpu:
        free_percpu(dp->stats_percpu);
@@ -1848,7 +1841,8 @@ static struct genl_family dp_datapath_genl_family __ro_after_init = {
 
 /* Called with ovs_mutex or RCU read lock. */
 static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb,
-                                  u32 portid, u32 seq, u32 flags, u8 cmd)
+                                  struct net *net, u32 portid, u32 seq,
+                                  u32 flags, u8 cmd)
 {
        struct ovs_header *ovs_header;
        struct ovs_vport_stats vport_stats;
@@ -1864,9 +1858,17 @@ static int ovs_vport_cmd_fill_info(struct vport *vport, struct sk_buff *skb,
        if (nla_put_u32(skb, OVS_VPORT_ATTR_PORT_NO, vport->port_no) ||
            nla_put_u32(skb, OVS_VPORT_ATTR_TYPE, vport->ops->type) ||
            nla_put_string(skb, OVS_VPORT_ATTR_NAME,
-                          ovs_vport_name(vport)))
+                          ovs_vport_name(vport)) ||
+           nla_put_u32(skb, OVS_VPORT_ATTR_IFINDEX, vport->dev->ifindex))
                goto nla_put_failure;
 
+       if (!net_eq(net, dev_net(vport->dev))) {
+               int id = peernet2id_alloc(net, dev_net(vport->dev));
+
+               if (nla_put_s32(skb, OVS_VPORT_ATTR_NETNSID, id))
+                       goto nla_put_failure;
+       }
+
        ovs_vport_get_stats(vport, &vport_stats);
        if (nla_put_64bit(skb, OVS_VPORT_ATTR_STATS,
                          sizeof(struct ovs_vport_stats), &vport_stats,
@@ -1896,8 +1898,8 @@ static struct sk_buff *ovs_vport_cmd_alloc_info(void)
 }
 
 /* Called with ovs_mutex, only via ovs_dp_notify_wq(). */
-struct sk_buff *ovs_vport_cmd_build_info(struct vport *vport, u32 portid,
-                                        u32 seq, u8 cmd)
+struct sk_buff *ovs_vport_cmd_build_info(struct vport *vport, struct net *net,
+                                        u32 portid, u32 seq, u8 cmd)
 {
        struct sk_buff *skb;
        int retval;
@@ -1906,7 +1908,7 @@ struct sk_buff *ovs_vport_cmd_build_info(struct vport *vport, u32 portid,
        if (!skb)
                return ERR_PTR(-ENOMEM);
 
-       retval = ovs_vport_cmd_fill_info(vport, skb, portid, seq, 0, cmd);
+       retval = ovs_vport_cmd_fill_info(vport, skb, net, portid, seq, 0, cmd);
        BUG_ON(retval < 0);
 
        return skb;
@@ -1920,6 +1922,8 @@ static struct vport *lookup_vport(struct net *net,
        struct datapath *dp;
        struct vport *vport;
 
+       if (a[OVS_VPORT_ATTR_IFINDEX])
+               return ERR_PTR(-EOPNOTSUPP);
        if (a[OVS_VPORT_ATTR_NAME]) {
                vport = ovs_vport_locate(net, nla_data(a[OVS_VPORT_ATTR_NAME]));
                if (!vport)
@@ -1944,6 +1948,7 @@ static struct vport *lookup_vport(struct net *net,
                return vport;
        } else
                return ERR_PTR(-EINVAL);
+
 }
 
 /* Called with ovs_mutex */
@@ -1983,6 +1988,8 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info)
        if (!a[OVS_VPORT_ATTR_NAME] || !a[OVS_VPORT_ATTR_TYPE] ||
            !a[OVS_VPORT_ATTR_UPCALL_PID])
                return -EINVAL;
+       if (a[OVS_VPORT_ATTR_IFINDEX])
+               return -EOPNOTSUPP;
 
        port_no = a[OVS_VPORT_ATTR_PORT_NO]
                ? nla_get_u32(a[OVS_VPORT_ATTR_PORT_NO]) : 0;
@@ -2032,8 +2039,9 @@ restart:
                goto exit_unlock_free;
        }
 
-       err = ovs_vport_cmd_fill_info(vport, reply, info->snd_portid,
-                                     info->snd_seq, 0, OVS_VPORT_CMD_NEW);
+       err = ovs_vport_cmd_fill_info(vport, reply, genl_info_net(info),
+                                     info->snd_portid, info->snd_seq, 0,
+                                     OVS_VPORT_CMD_NEW);
 
        if (netdev_get_fwd_headroom(vport->dev) > dp->max_headroom)
                update_headroom(dp);
@@ -2090,8 +2098,9 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info)
                        goto exit_unlock_free;
        }
 
-       err = ovs_vport_cmd_fill_info(vport, reply, info->snd_portid,
-                                     info->snd_seq, 0, OVS_VPORT_CMD_NEW);
+       err = ovs_vport_cmd_fill_info(vport, reply, genl_info_net(info),
+                                     info->snd_portid, info->snd_seq, 0,
+                                     OVS_VPORT_CMD_NEW);
        BUG_ON(err < 0);
 
        ovs_unlock();
@@ -2128,8 +2137,9 @@ static int ovs_vport_cmd_del(struct sk_buff *skb, struct genl_info *info)
                goto exit_unlock_free;
        }
 
-       err = ovs_vport_cmd_fill_info(vport, reply, info->snd_portid,
-                                     info->snd_seq, 0, OVS_VPORT_CMD_DEL);
+       err = ovs_vport_cmd_fill_info(vport, reply, genl_info_net(info),
+                                     info->snd_portid, info->snd_seq, 0,
+                                     OVS_VPORT_CMD_DEL);
        BUG_ON(err < 0);
 
        /* the vport deletion may trigger dp headroom update */
@@ -2169,8 +2179,9 @@ static int ovs_vport_cmd_get(struct sk_buff *skb, struct genl_info *info)
        err = PTR_ERR(vport);
        if (IS_ERR(vport))
                goto exit_unlock_free;
-       err = ovs_vport_cmd_fill_info(vport, reply, info->snd_portid,
-                                     info->snd_seq, 0, OVS_VPORT_CMD_NEW);
+       err = ovs_vport_cmd_fill_info(vport, reply, genl_info_net(info),
+                                     info->snd_portid, info->snd_seq, 0,
+                                     OVS_VPORT_CMD_NEW);
        BUG_ON(err < 0);
        rcu_read_unlock();
 
@@ -2202,6 +2213,7 @@ static int ovs_vport_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
                hlist_for_each_entry_rcu(vport, &dp->ports[i], dp_hash_node) {
                        if (j >= skip &&
                            ovs_vport_cmd_fill_info(vport, skb,
+                                                   sock_net(skb->sk),
                                                    NETLINK_CB(cb->skb).portid,
                                                    cb->nlh->nlmsg_seq,
                                                    NLM_F_MULTI,
@@ -2228,6 +2240,8 @@ static const struct nla_policy vport_policy[OVS_VPORT_ATTR_MAX + 1] = {
        [OVS_VPORT_ATTR_TYPE] = { .type = NLA_U32 },
        [OVS_VPORT_ATTR_UPCALL_PID] = { .type = NLA_U32 },
        [OVS_VPORT_ATTR_OPTIONS] = { .type = NLA_NESTED },
+       [OVS_VPORT_ATTR_IFINDEX] = { .type = NLA_U32 },
+       [OVS_VPORT_ATTR_NETNSID] = { .type = NLA_S32 },
 };
 
 static const struct genl_ops dp_vport_genl_ops[] = {
@@ -2273,6 +2287,7 @@ static struct genl_family * const dp_genl_families[] = {
        &dp_vport_genl_family,
        &dp_flow_genl_family,
        &dp_packet_genl_family,
+       &dp_meter_genl_family,
 };
 
 static void dp_unregister_genl(int n_families)
@@ -2453,3 +2468,4 @@ MODULE_ALIAS_GENL_FAMILY(OVS_DATAPATH_FAMILY);
 MODULE_ALIAS_GENL_FAMILY(OVS_VPORT_FAMILY);
 MODULE_ALIAS_GENL_FAMILY(OVS_FLOW_FAMILY);
 MODULE_ALIAS_GENL_FAMILY(OVS_PACKET_FAMILY);
+MODULE_ALIAS_GENL_FAMILY(OVS_METER_FAMILY);