datapath: Add support for namespace.
authorPravin B Shelar <pshelar@nicira.com>
Mon, 30 Jan 2012 14:56:54 +0000 (06:56 -0800)
committerPravin B Shelar <pshelar@nicira.com>
Mon, 30 Jan 2012 14:56:54 +0000 (06:56 -0800)
Following patch adds support for Linux net-namespace. Now we can
have independent OVS instance in each net-ns.
Namespace support requires 2.6.32 or newer kernel as per-net-ns
genl-sock is not available in earlier kernel.

Signed-off-by: Pravin B Shelar <pshelar@nicira.com>
Acked-by: Jesse Gross <jesse@nicira.com>
Bug #7821

25 files changed:
datapath/Modules.mk
datapath/brcompat.c
datapath/compat.h
datapath/datapath.c
datapath/datapath.h
datapath/dp_notify.c
datapath/dp_sysfs_dp.c
datapath/dp_sysfs_if.c
datapath/genl_exec.c
datapath/linux/Modules.mk
datapath/linux/compat/include/net/genetlink.h
datapath/linux/compat/include/net/net_namespace.h
datapath/linux/compat/include/net/netns/generic.h [new file with mode: 0644]
datapath/linux/compat/include/net/sock.h [new file with mode: 0644]
datapath/linux/compat/net_namespace.c [new file with mode: 0644]
datapath/tunnel.c
datapath/tunnel.h
datapath/vport-capwap.c
datapath/vport-capwap.h [new file with mode: 0644]
datapath/vport-gre.c
datapath/vport-internal_dev.c
datapath/vport-netdev.c
datapath/vport-patch.c
datapath/vport.c
datapath/vport.h

index 4d17568..96e7f7d 100644 (file)
@@ -38,6 +38,7 @@ openvswitch_headers = \
        tunnel.h \
        vlan.h \
        vport.h \
+       vport-capwap.h \
        vport-generic.h \
        vport-internal_dev.h \
        vport-netdev.h
index 339b5dc..d4a0aca 100644 (file)
@@ -50,9 +50,10 @@ static DECLARE_COMPLETION(brc_done); /* Userspace signaled operation done? */
 static struct sk_buff *brc_reply;    /* Reply from userspace. */
 static u32 brc_seq;                 /* Sequence number for current op. */
 
-static struct sk_buff *brc_send_command(struct sk_buff *,
+static struct sk_buff *brc_send_command(struct net *,
+                                       struct sk_buff *,
                                        struct nlattr **attrs);
-static int brc_send_simple_command(struct sk_buff *);
+static int brc_send_simple_command(struct net *, struct sk_buff *);
 
 static struct sk_buff *brc_make_request(int op, const char *bridge,
                                        const char *port)
@@ -74,13 +75,13 @@ error:
        return NULL;
 }
 
-static int brc_send_simple_command(struct sk_buff *request)
+static int brc_send_simple_command(struct net *net, struct sk_buff *request)
 {
        struct nlattr *attrs[BRC_GENL_A_MAX + 1];
        struct sk_buff *reply;
        int error;
 
-       reply = brc_send_command(request, attrs);
+       reply = brc_send_command(net, request, attrs);
        if (IS_ERR(reply))
                return PTR_ERR(reply);
 
@@ -89,7 +90,7 @@ static int brc_send_simple_command(struct sk_buff *request)
        return -error;
 }
 
-static int brc_add_del_bridge(char __user *uname, int add)
+static int brc_add_del_bridge(struct net *net, char __user *uname, int add)
 {
        struct sk_buff *request;
        char name[IFNAMSIZ];
@@ -106,10 +107,11 @@ static int brc_add_del_bridge(char __user *uname, int add)
        if (!request)
                return -ENOMEM;
 
-       return brc_send_simple_command(request);
+       return brc_send_simple_command(net, request);
 }
 
-static int brc_get_indices(int op, const char *br_name,
+static int brc_get_indices(struct net *net,
+                          int op, const char *br_name,
                           int __user *uindices, int n)
 {
        struct nlattr *attrs[BRC_GENL_A_MAX + 1];
@@ -127,7 +129,7 @@ static int brc_get_indices(int op, const char *br_name,
        if (!request)
                return -ENOMEM;
 
-       reply = brc_send_command(request, attrs);
+       reply = brc_send_command(net, request, attrs);
        ret = PTR_ERR(reply);
        if (IS_ERR(reply))
                goto exit;
@@ -155,13 +157,13 @@ exit:
 }
 
 /* Called with br_ioctl_mutex. */
-static int brc_get_bridges(int __user *uindices, int n)
+static int brc_get_bridges(struct net *net, int __user *uindices, int n)
 {
-       return brc_get_indices(BRC_GENL_C_GET_BRIDGES, NULL, uindices, n);
+       return brc_get_indices(net, BRC_GENL_C_GET_BRIDGES, NULL, uindices, n);
 }
 
 /* Legacy deviceless bridge ioctl's.  Called with br_ioctl_mutex. */
-static int old_deviceless(void __user *uarg)
+static int old_deviceless(struct net *net, void __user *uarg)
 {
        unsigned long args[3];
 
@@ -170,12 +172,12 @@ static int old_deviceless(void __user *uarg)
 
        switch (args[0]) {
        case BRCTL_GET_BRIDGES:
-               return brc_get_bridges((int __user *)args[1], args[2]);
+               return brc_get_bridges(net, (int __user *)args[1], args[2]);
 
        case BRCTL_ADD_BRIDGE:
-               return brc_add_del_bridge((void __user *)args[1], 1);
+               return brc_add_del_bridge(net, (void __user *)args[1], 1);
        case BRCTL_DEL_BRIDGE:
-               return brc_add_del_bridge((void __user *)args[1], 0);
+               return brc_add_del_bridge(net, (void __user *)args[1], 0);
        }
 
        return -EOPNOTSUPP;
@@ -185,19 +187,21 @@ static int old_deviceless(void __user *uarg)
 static int
 #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23)
 brc_ioctl_deviceless_stub(unsigned int cmd, void __user *uarg)
+{
+       struct net *net = NULL;
 #else
 brc_ioctl_deviceless_stub(struct net *net, unsigned int cmd, void __user *uarg)
-#endif
 {
+#endif
        switch (cmd) {
        case SIOCGIFBR:
        case SIOCSIFBR:
-               return old_deviceless(uarg);
+               return old_deviceless(net, uarg);
 
        case SIOCBRADDBR:
-               return brc_add_del_bridge(uarg, 1);
+               return brc_add_del_bridge(net, uarg, 1);
        case SIOCBRDELBR:
-               return brc_add_del_bridge(uarg, 0);
+               return brc_add_del_bridge(net, uarg, 0);
        }
 
        return -EOPNOTSUPP;
@@ -212,7 +216,7 @@ static int brc_add_del_port(struct net_device *dev, int port_ifindex, int add)
        if (!capable(CAP_NET_ADMIN))
                return -EPERM;
 
-       port = __dev_get_by_index(&init_net, port_ifindex);
+       port = __dev_get_by_index(dev_net(dev), port_ifindex);
        if (!port)
                return -EINVAL;
 
@@ -224,7 +228,7 @@ static int brc_add_del_port(struct net_device *dev, int port_ifindex, int add)
                return -ENOMEM;
 
        rtnl_unlock();
-       err = brc_send_simple_command(request);
+       err = brc_send_simple_command(dev_net(dev), request);
        rtnl_lock();
 
        return err;
@@ -255,7 +259,7 @@ static int brc_get_port_list(struct net_device *dev, int __user *uindices,
        int retval;
 
        rtnl_unlock();
-       retval = brc_get_indices(BRC_GENL_C_GET_PORTS, dev->name,
+       retval = brc_get_indices(dev_net(dev), BRC_GENL_C_GET_PORTS, dev->name,
                                 uindices, num);
        rtnl_lock();
 
@@ -288,7 +292,7 @@ static int brc_get_fdb_entries(struct net_device *dev, void __user *userbuf,
        NLA_PUT_U64(request, BRC_GENL_A_FDB_SKIP, offset);
 
        rtnl_unlock();
-       reply = brc_send_command(request, attrs);
+       reply = brc_send_command(dev_net(dev), request, attrs);
        retval = PTR_ERR(reply);
        if (IS_ERR(reply))
                goto exit;
@@ -378,6 +382,7 @@ static struct genl_family brc_genl_family = {
        .name = BRC_GENL_FAMILY_NAME,
        .version = 1,
        .maxattr = BRC_GENL_A_MAX,
+        SET_NETNSOK
 };
 
 static int brc_genl_query(struct sk_buff *skb, struct genl_info *info)
@@ -456,7 +461,8 @@ static struct genl_ops brc_genl_ops[] = {
        },
 };
 
-static struct sk_buff *brc_send_command(struct sk_buff *request,
+static struct sk_buff *brc_send_command(struct net *net,
+                                       struct sk_buff *request,
                                        struct nlattr **attrs)
 {
        unsigned long int flags;
@@ -475,7 +481,8 @@ static struct sk_buff *brc_send_command(struct sk_buff *request,
        nlmsg_end(request, nlmsg_hdr(request));
 
        /* Send message. */
-       error = genlmsg_multicast(request, 0, brc_mc_group.id, GFP_KERNEL);
+       error = genlmsg_multicast_netns(net, request, 0,
+                                       brc_mc_group.id, GFP_KERNEL);
        if (error < 0)
                goto error;
 
index 36d0025..fd757f1 100644 (file)
@@ -73,4 +73,12 @@ static inline void skb_clear_rxhash(struct sk_buff *skb)
        typeof(br_should_route_hook) br_should_route_hook;      \
        EXPORT_SYMBOL(br_should_route_hook)
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)
+#define GENL_SOCK(net) (genl_sock)
+#define SET_NETNSOK
+#else
+#define GENL_SOCK(net) ((net)->genl_sock)
+#define SET_NETNSOK    .netnsok = true,
+#endif
+
 #endif /* compat.h */
index e9a4e18..220c7dd 100644 (file)
@@ -50,6 +50,8 @@
 #include <linux/rculist.h>
 #include <linux/dmi.h>
 #include <net/genetlink.h>
+#include <net/net_namespace.h>
+#include <net/netns/generic.h>
 
 #include "checksum.h"
 #include "datapath.h"
@@ -68,6 +70,8 @@
 static void rehash_flow_table(struct work_struct *work);
 static DECLARE_DELAYED_WORK(rehash_flow_wq, rehash_flow_table);
 
+int ovs_net_id __read_mostly;
+
 int (*ovs_dp_ioctl_hook)(struct net_device *dev, struct ifreq *rq, int cmd);
 EXPORT_SYMBOL(ovs_dp_ioctl_hook);
 
@@ -88,25 +92,21 @@ EXPORT_SYMBOL(ovs_dp_ioctl_hook);
  * each other.
  */
 
-/* Global list of datapaths to enable dumping them all out.
- * Protected by genl_mutex.
- */
-static LIST_HEAD(dps);
-
 static struct vport *new_vport(const struct vport_parms *);
-static int queue_gso_packets(int dp_ifindex, struct sk_buff *,
+static int queue_gso_packets(struct net *, int dp_ifindex, struct sk_buff *,
                             const struct dp_upcall_info *);
-static int queue_userspace_packet(int dp_ifindex, struct sk_buff *,
+static int queue_userspace_packet(struct net *, int dp_ifindex,
+                                 struct sk_buff *,
                                  const struct dp_upcall_info *);
 
 /* Must be called with rcu_read_lock, genl_mutex, or RTNL lock. */
-static struct datapath *get_dp(int dp_ifindex)
+static struct datapath *get_dp(struct net *net, int dp_ifindex)
 {
        struct datapath *dp = NULL;
        struct net_device *dev;
 
        rcu_read_lock();
-       dev = dev_get_by_index_rcu(&init_net, dp_ifindex);
+       dev = dev_get_by_index_rcu(net, dp_ifindex);
        if (dev) {
                struct vport *vport = ovs_internal_dev_get_vport(dev);
                if (vport)
@@ -218,11 +218,11 @@ static void dp_ifinfo_notify(int event, struct vport *port)
                }
        }
 
-       rtnl_notify(skb, &init_net, 0, RTNLGRP_LINK, NULL, GFP_KERNEL);
+       rtnl_notify(skb, ovs_dp_get_net(port->dp), 0, RTNLGRP_LINK, NULL, GFP_KERNEL);
 
        return;
 err:
-       rtnl_set_sk_err(&init_net, RTNLGRP_LINK, err);
+       rtnl_set_sk_err(ovs_dp_get_net(port->dp), RTNLGRP_LINK, err);
 out:
        kfree_skb(skb);
 }
@@ -243,6 +243,7 @@ static void destroy_dp_rcu(struct rcu_head *rcu)
 
        ovs_flow_tbl_destroy((__force struct flow_table *)dp->table);
        free_percpu(dp->stats_percpu);
+       release_net(ovs_dp_get_net(dp));
        kobject_put(&dp->ifobj);
 }
 
@@ -338,7 +339,8 @@ static struct genl_family dp_packet_genl_family = {
        .hdrsize = sizeof(struct ovs_header),
        .name = OVS_PACKET_FAMILY,
        .version = OVS_PACKET_VERSION,
-       .maxattr = OVS_PACKET_ATTR_MAX
+       .maxattr = OVS_PACKET_ATTR_MAX,
+        SET_NETNSOK
 };
 
 int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb,
@@ -362,9 +364,9 @@ int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb,
        forward_ip_summed(skb, true);
 
        if (!skb_is_gso(skb))
-               err = queue_userspace_packet(dp_ifindex, skb, upcall_info);
+               err = queue_userspace_packet(ovs_dp_get_net(dp), dp_ifindex, skb, upcall_info);
        else
-               err = queue_gso_packets(dp_ifindex, skb, upcall_info);
+               err = queue_gso_packets(ovs_dp_get_net(dp), dp_ifindex, skb, upcall_info);
        if (err)
                goto err;
 
@@ -380,7 +382,8 @@ err:
        return err;
 }
 
-static int queue_gso_packets(int dp_ifindex, struct sk_buff *skb,
+static int queue_gso_packets(struct net *net, int dp_ifindex,
+                            struct sk_buff *skb,
                             const struct dp_upcall_info *upcall_info)
 {
        struct dp_upcall_info later_info;
@@ -395,7 +398,7 @@ static int queue_gso_packets(int dp_ifindex, struct sk_buff *skb,
        /* Queue all of the segments. */
        skb = segs;
        do {
-               err = queue_userspace_packet(dp_ifindex, skb, upcall_info);
+               err = queue_userspace_packet(net, dp_ifindex, skb, upcall_info);
                if (err)
                        break;
 
@@ -425,7 +428,8 @@ static int queue_gso_packets(int dp_ifindex, struct sk_buff *skb,
        return err;
 }
 
-static int queue_userspace_packet(int dp_ifindex, struct sk_buff *skb,
+static int queue_userspace_packet(struct net *net, int dp_ifindex,
+                                 struct sk_buff *skb,
                                  const struct dp_upcall_info *upcall_info)
 {
        struct ovs_header *upcall;
@@ -480,7 +484,7 @@ static int queue_userspace_packet(int dp_ifindex, struct sk_buff *skb,
 
        skb_copy_and_csum_dev(skb, nla_data(nla));
 
-       err = genlmsg_unicast(&init_net, user_skb, upcall_info->pid);
+       err = genlmsg_unicast(net, user_skb, upcall_info->pid);
 
 out:
        kfree_skb(nskb);
@@ -488,15 +492,10 @@ out:
 }
 
 /* Called with genl_mutex. */
-static int flush_flows(int dp_ifindex)
+static int flush_flows(struct datapath *dp)
 {
        struct flow_table *old_table;
        struct flow_table *new_table;
-       struct datapath *dp;
-
-       dp = get_dp(dp_ifindex);
-       if (!dp)
-               return -ENODEV;
 
        old_table = genl_dereference(dp->table);
        new_table = ovs_flow_tbl_alloc(TBL_MIN_BUCKETS);
@@ -780,7 +779,7 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
        packet->priority = flow->key.phy.priority;
 
        rcu_read_lock();
-       dp = get_dp(ovs_header->dp_ifindex);
+       dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
        err = -ENODEV;
        if (!dp)
                goto err_unlock;
@@ -854,7 +853,8 @@ static struct genl_family dp_flow_genl_family = {
        .hdrsize = sizeof(struct ovs_header),
        .name = OVS_FLOW_FAMILY,
        .version = OVS_FLOW_VERSION,
-       .maxattr = OVS_FLOW_ATTR_MAX
+       .maxattr = OVS_FLOW_ATTR_MAX,
+        SET_NETNSOK
 };
 
 static struct genl_multicast_group ovs_dp_flow_multicast_group = {
@@ -1003,7 +1003,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
                goto error;
        }
 
-       dp = get_dp(ovs_header->dp_ifindex);
+       dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
        error = -ENODEV;
        if (!dp)
                goto error;
@@ -1104,9 +1104,8 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info)
                           ovs_dp_flow_multicast_group.id, info->nlhdr,
                           GFP_KERNEL);
        else
-               netlink_set_err(INIT_NET_GENL_SOCK, 0,
-                               ovs_dp_flow_multicast_group.id,
-                               PTR_ERR(reply));
+               netlink_set_err(GENL_SOCK(sock_net(skb->sk)), 0,
+                               ovs_dp_flow_multicast_group.id, PTR_ERR(reply));
        return 0;
 
 error_free_flow:
@@ -1133,7 +1132,7 @@ static int ovs_flow_cmd_get(struct sk_buff *skb, struct genl_info *info)
        if (err)
                return err;
 
-       dp = get_dp(ovs_header->dp_ifindex);
+       dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
        if (!dp)
                return -ENODEV;
 
@@ -1162,16 +1161,17 @@ static int ovs_flow_cmd_del(struct sk_buff *skb, struct genl_info *info)
        int err;
        int key_len;
 
+       dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
+       if (!dp)
+               return -ENODEV;
+
        if (!a[OVS_FLOW_ATTR_KEY])
-               return flush_flows(ovs_header->dp_ifindex);
+               return flush_flows(dp);
+
        err = ovs_flow_from_nlattrs(&key, &key_len, a[OVS_FLOW_ATTR_KEY]);
        if (err)
                return err;
 
-       dp = get_dp(ovs_header->dp_ifindex);
-       if (!dp)
-               return -ENODEV;
-
        table = genl_dereference(dp->table);
        flow = ovs_flow_tbl_lookup(table, &key, key_len);
        if (!flow)
@@ -1200,7 +1200,7 @@ static int ovs_flow_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
        struct datapath *dp;
        struct flow_table *table;
 
-       dp = get_dp(ovs_header->dp_ifindex);
+       dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
        if (!dp)
                return -ENODEV;
 
@@ -1264,7 +1264,8 @@ static struct genl_family dp_datapath_genl_family = {
        .hdrsize = sizeof(struct ovs_header),
        .name = OVS_DATAPATH_FAMILY,
        .version = OVS_DATAPATH_VERSION,
-       .maxattr = OVS_DP_ATTR_MAX
+       .maxattr = OVS_DP_ATTR_MAX,
+        SET_NETNSOK
 };
 
 static struct genl_multicast_group ovs_dp_datapath_multicast_group = {
@@ -1326,18 +1327,19 @@ static int ovs_dp_cmd_validate(struct nlattr *a[OVS_DP_ATTR_MAX + 1])
 }
 
 /* Called with genl_mutex and optionally with RTNL lock also. */
-static struct datapath *lookup_datapath(struct ovs_header *ovs_header,
+static struct datapath *lookup_datapath(struct net *net,
+                                       struct ovs_header *ovs_header,
                                        struct nlattr *a[OVS_DP_ATTR_MAX + 1])
 {
        struct datapath *dp;
 
        if (!a[OVS_DP_ATTR_NAME])
-               dp = get_dp(ovs_header->dp_ifindex);
+               dp = get_dp(net, ovs_header->dp_ifindex);
        else {
                struct vport *vport;
 
                rcu_read_lock();
-               vport = ovs_vport_locate(nla_data(a[OVS_DP_ATTR_NAME]));
+               vport = ovs_vport_locate(net, nla_data(a[OVS_DP_ATTR_NAME]));
                dp = vport && vport->port_no == OVSP_LOCAL ? vport->dp : NULL;
                rcu_read_unlock();
        }
@@ -1351,6 +1353,7 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
        struct sk_buff *reply;
        struct datapath *dp;
        struct vport *vport;
+       struct ovs_net *ovs_net;
        int err;
 
        err = -EINVAL;
@@ -1362,14 +1365,12 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
                goto err;
 
        rtnl_lock();
-       err = -ENODEV;
-       if (!try_module_get(THIS_MODULE))
-               goto err_unlock_rtnl;
 
        err = -ENOMEM;
        dp = kzalloc(sizeof(*dp), GFP_KERNEL);
        if (dp == NULL)
-               goto err_put_module;
+               goto err_unlock_rtnl;
+
        INIT_LIST_HEAD(&dp->port_list);
 
        /* Initialize kobject for bridge.  This will be added as
@@ -1388,6 +1389,7 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
                err = -ENOMEM;
                goto err_destroy_table;
        }
+       ovs_dp_set_net(dp, hold_net(sock_net(skb->sk)));
 
        /* Set up our datapath device. */
        parms.name = nla_data(a[OVS_DP_ATTR_NAME]);
@@ -1412,7 +1414,8 @@ static int ovs_dp_cmd_new(struct sk_buff *skb, struct genl_info *info)
        if (IS_ERR(reply))
                goto err_destroy_local_port;
 
-       list_add_tail(&dp->list_node, &dps);
+       ovs_net = net_generic(ovs_dp_get_net(dp), ovs_net_id);
+       list_add_tail(&dp->list_node, &ovs_net->dps);
        ovs_dp_sysfs_add_dp(dp);
 
        rtnl_unlock();
@@ -1430,37 +1433,18 @@ err_destroy_table:
        ovs_flow_tbl_destroy(genl_dereference(dp->table));
 err_free_dp:
        kfree(dp);
-err_put_module:
-       module_put(THIS_MODULE);
 err_unlock_rtnl:
        rtnl_unlock();
 err:
        return err;
 }
 
-static int ovs_dp_cmd_del(struct sk_buff *skb, struct genl_info *info)
+/* Called with genl_mutex. */
+static void __dp_destroy(struct datapath *dp)
 {
        struct vport *vport, *next_vport;
-       struct sk_buff *reply;
-       struct datapath *dp;
-       int err;
-
-       err = ovs_dp_cmd_validate(info->attrs);
-       if (err)
-               goto exit;
 
        rtnl_lock();
-       dp = lookup_datapath(info->userhdr, info->attrs);
-       err = PTR_ERR(dp);
-       if (IS_ERR(dp))
-               goto exit_unlock;
-
-       reply = ovs_dp_cmd_build_info(dp, info->snd_pid,
-                                     info->snd_seq, OVS_DP_CMD_DEL);
-       err = PTR_ERR(reply);
-       if (IS_ERR(reply))
-               goto exit_unlock;
-
        list_for_each_entry_safe(vport, next_vport, &dp->port_list, node)
                if (vport->port_no != OVSP_LOCAL)
                        ovs_dp_detach_port(vport);
@@ -1477,18 +1461,36 @@ static int ovs_dp_cmd_del(struct sk_buff *skb, struct genl_info *info)
        rtnl_unlock();
 
        call_rcu(&dp->rcu, destroy_dp_rcu);
-       module_put(THIS_MODULE);
+}
+
+static int ovs_dp_cmd_del(struct sk_buff *skb, struct genl_info *info)
+{
+       struct sk_buff *reply;
+       struct datapath *dp;
+       int err;
+
+       err = ovs_dp_cmd_validate(info->attrs);
+       if (err)
+               return err;
+
+       dp = lookup_datapath(sock_net(skb->sk), info->userhdr, info->attrs);
+       err = PTR_ERR(dp);
+       if (IS_ERR(dp))
+               return err;
+
+       reply = ovs_dp_cmd_build_info(dp, info->snd_pid,
+                                     info->snd_seq, OVS_DP_CMD_DEL);
+       err = PTR_ERR(reply);
+       if (IS_ERR(reply))
+               return err;
+
+       __dp_destroy(dp);
 
        genl_notify(reply, genl_info_net(info), info->snd_pid,
                    ovs_dp_datapath_multicast_group.id, info->nlhdr,
                    GFP_KERNEL);
 
        return 0;
-
-exit_unlock:
-       rtnl_unlock();
-exit:
-       return err;
 }
 
 static int ovs_dp_cmd_set(struct sk_buff *skb, struct genl_info *info)
@@ -1501,7 +1503,7 @@ static int ovs_dp_cmd_set(struct sk_buff *skb, struct genl_info *info)
        if (err)
                return err;
 
-       dp = lookup_datapath(info->userhdr, info->attrs);
+       dp = lookup_datapath(sock_net(skb->sk), info->userhdr, info->attrs);
        if (IS_ERR(dp))
                return PTR_ERR(dp);
 
@@ -1509,7 +1511,7 @@ static int ovs_dp_cmd_set(struct sk_buff *skb, struct genl_info *info)
                                      info->snd_seq, OVS_DP_CMD_NEW);
        if (IS_ERR(reply)) {
                err = PTR_ERR(reply);
-               netlink_set_err(INIT_NET_GENL_SOCK, 0,
+               netlink_set_err(GENL_SOCK(sock_net(skb->sk)), 0,
                                ovs_dp_datapath_multicast_group.id, err);
                return 0;
        }
@@ -1531,7 +1533,7 @@ static int ovs_dp_cmd_get(struct sk_buff *skb, struct genl_info *info)
        if (err)
                return err;
 
-       dp = lookup_datapath(info->userhdr, info->attrs);
+       dp = lookup_datapath(sock_net(skb->sk), info->userhdr, info->attrs);
        if (IS_ERR(dp))
                return PTR_ERR(dp);
 
@@ -1545,11 +1547,12 @@ static int ovs_dp_cmd_get(struct sk_buff *skb, struct genl_info *info)
 
 static int ovs_dp_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
 {
+       struct ovs_net *ovs_net = net_generic(sock_net(skb->sk), ovs_net_id);
        struct datapath *dp;
        int skip = cb->args[0];
        int i = 0;
 
-       list_for_each_entry(dp, &dps, list_node) {
+       list_for_each_entry(dp, &ovs_net->dps, list_node) {
                if (i >= skip &&
                    ovs_dp_cmd_fill_info(dp, skb, NETLINK_CB(cb->skb).pid,
                                         cb->nlh->nlmsg_seq, NLM_F_MULTI,
@@ -1607,7 +1610,8 @@ static struct genl_family dp_vport_genl_family = {
        .hdrsize = sizeof(struct ovs_header),
        .name = OVS_VPORT_FAMILY,
        .version = OVS_VPORT_VERSION,
-       .maxattr = OVS_VPORT_ATTR_MAX
+       .maxattr = OVS_VPORT_ATTR_MAX,
+        SET_NETNSOK
 };
 
 struct genl_multicast_group ovs_dp_vport_multicast_group = {
@@ -1679,14 +1683,15 @@ static int ovs_vport_cmd_validate(struct nlattr *a[OVS_VPORT_ATTR_MAX + 1])
 }
 
 /* Called with RTNL lock or RCU read lock. */
-static struct vport *lookup_vport(struct ovs_header *ovs_header,
+static struct vport *lookup_vport(struct net *net,
+                                 struct ovs_header *ovs_header,
                                  struct nlattr *a[OVS_VPORT_ATTR_MAX + 1])
 {
        struct datapath *dp;
        struct vport *vport;
 
        if (a[OVS_VPORT_ATTR_NAME]) {
-               vport = ovs_vport_locate(nla_data(a[OVS_VPORT_ATTR_NAME]));
+               vport = ovs_vport_locate(net, nla_data(a[OVS_VPORT_ATTR_NAME]));
                if (!vport)
                        return ERR_PTR(-ENODEV);
                return vport;
@@ -1696,7 +1701,7 @@ static struct vport *lookup_vport(struct ovs_header *ovs_header,
                if (port_no >= DP_MAX_PORTS)
                        return ERR_PTR(-EFBIG);
 
-               dp = get_dp(ovs_header->dp_ifindex);
+               dp = get_dp(net, ovs_header->dp_ifindex);
                if (!dp)
                        return ERR_PTR(-ENODEV);
 
@@ -1744,7 +1749,7 @@ static int ovs_vport_cmd_new(struct sk_buff *skb, struct genl_info *info)
                goto exit;
 
        rtnl_lock();
-       dp = get_dp(ovs_header->dp_ifindex);
+       dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
        err = -ENODEV;
        if (!dp)
                goto exit_unlock;
@@ -1819,7 +1824,7 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info)
                goto exit;
 
        rtnl_lock();
-       vport = lookup_vport(info->userhdr, a);
+       vport = lookup_vport(sock_net(skb->sk), info->userhdr, a);
        err = PTR_ERR(vport);
        if (IS_ERR(vport))
                goto exit_unlock;
@@ -1840,7 +1845,7 @@ static int ovs_vport_cmd_set(struct sk_buff *skb, struct genl_info *info)
                                         OVS_VPORT_CMD_NEW);
        if (IS_ERR(reply)) {
                err = PTR_ERR(reply);
-               netlink_set_err(INIT_NET_GENL_SOCK, 0,
+               netlink_set_err(GENL_SOCK(sock_net(skb->sk)), 0,
                                ovs_dp_vport_multicast_group.id, err);
                return 0;
        }
@@ -1866,7 +1871,7 @@ static int ovs_vport_cmd_del(struct sk_buff *skb, struct genl_info *info)
                goto exit;
 
        rtnl_lock();
-       vport = lookup_vport(info->userhdr, a);
+       vport = lookup_vport(sock_net(skb->sk), info->userhdr, a);
        err = PTR_ERR(vport);
        if (IS_ERR(vport))
                goto exit_unlock;
@@ -1906,7 +1911,7 @@ static int ovs_vport_cmd_get(struct sk_buff *skb, struct genl_info *info)
                goto exit;
 
        rcu_read_lock();
-       vport = lookup_vport(ovs_header, a);
+       vport = lookup_vport(sock_net(skb->sk), ovs_header, a);
        err = PTR_ERR(vport);
        if (IS_ERR(vport))
                goto exit_unlock;
@@ -1934,7 +1939,7 @@ static int ovs_vport_cmd_dump(struct sk_buff *skb, struct netlink_callback *cb)
        u32 port_no;
        int retval;
 
-       dp = get_dp(ovs_header->dp_ifindex);
+       dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex);
        if (!dp)
                return -ENODEV;
 
@@ -2046,17 +2051,24 @@ error:
 static int __rehash_flow_table(void *dummy)
 {
        struct datapath *dp;
+       struct net *net;
+
+       rtnl_lock();
+       for_each_net(net) {
+               struct ovs_net *ovs_net = net_generic(net, ovs_net_id);
 
-       list_for_each_entry(dp, &dps, list_node) {
-               struct flow_table *old_table = genl_dereference(dp->table);
-               struct flow_table *new_table;
+               list_for_each_entry(dp, &ovs_net->dps, list_node) {
+                       struct flow_table *old_table = genl_dereference(dp->table);
+                       struct flow_table *new_table;
 
-               new_table = ovs_flow_tbl_rehash(old_table);
-               if (!IS_ERR(new_table)) {
-                       rcu_assign_pointer(dp->table, new_table);
-                       ovs_flow_tbl_deferred_destroy(old_table);
+                       new_table = ovs_flow_tbl_rehash(old_table);
+                       if (!IS_ERR(new_table)) {
+                               rcu_assign_pointer(dp->table, new_table);
+                               ovs_flow_tbl_deferred_destroy(old_table);
+                       }
                }
        }
+       rtnl_unlock();
        return 0;
 }
 
@@ -2066,6 +2078,39 @@ static void rehash_flow_table(struct work_struct *work)
        schedule_delayed_work(&rehash_flow_wq, REHASH_FLOW_INTERVAL);
 }
 
+static int dp_destroy_all(void *data)
+{
+       struct datapath *dp, *dp_next;
+       struct ovs_net *ovs_net = data;
+
+       list_for_each_entry_safe(dp, dp_next, &ovs_net->dps, list_node)
+               __dp_destroy(dp);
+
+       return 0;
+}
+
+static int __net_init ovs_init_net(struct net *net)
+{
+       struct ovs_net *ovs_net = net_generic(net, ovs_net_id);
+
+       INIT_LIST_HEAD(&ovs_net->dps);
+       return 0;
+}
+
+static void __net_exit ovs_exit_net(struct net *net)
+{
+       struct ovs_net *ovs_net = net_generic(net, ovs_net_id);
+
+       genl_exec(dp_destroy_all, ovs_net);
+}
+
+static struct pernet_operations ovs_net_ops = {
+       .init = ovs_init_net,
+       .exit = ovs_exit_net,
+       .id   = &ovs_net_id,
+       .size = sizeof(struct ovs_net),
+};
+
 static int __init dp_init(void)
 {
        struct sk_buff *dummy_skb;
@@ -2096,10 +2141,14 @@ static int __init dp_init(void)
        if (err)
                goto error_flow_exit;
 
-       err = register_netdevice_notifier(&ovs_dp_device_notifier);
+       err = register_pernet_device(&ovs_net_ops);
        if (err)
                goto error_vport_exit;
 
+       err = register_netdevice_notifier(&ovs_dp_device_notifier);
+       if (err)
+               goto error_netns_exit;
+
        err = dp_register_genl();
        if (err < 0)
                goto error_unreg_notifier;
@@ -2110,6 +2159,8 @@ static int __init dp_init(void)
 
 error_unreg_notifier:
        unregister_netdevice_notifier(&ovs_dp_device_notifier);
+error_netns_exit:
+       unregister_pernet_device(&ovs_net_ops);
 error_vport_exit:
        ovs_vport_exit();
 error_flow_exit:
@@ -2127,9 +2178,10 @@ error:
 static void dp_cleanup(void)
 {
        cancel_delayed_work_sync(&rehash_flow_wq);
-       rcu_barrier();
        dp_unregister_genl(ARRAY_SIZE(dp_genl_families));
        unregister_netdevice_notifier(&ovs_dp_device_notifier);
+       unregister_pernet_device(&ovs_net_ops);
+       rcu_barrier();
        ovs_vport_exit();
        ovs_flow_exit();
        ovs_tnl_exit();
index 7f3946e..b012a76 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2011 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira Networks.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
 
 #include "checksum.h"
 #include "compat.h"
-#include "flow.h"
 #include "dp_sysfs.h"
+#include "flow.h"
+#include "tunnel.h"
 #include "vlan.h"
-
-struct vport;
+#include "vport.h"
 
 #define DP_MAX_PORTS 1024
 #define SAMPLE_ACTION_DEPTH 3
@@ -68,6 +68,7 @@ struct dp_stats_percpu {
  * @port_list: List of all ports in @ports in arbitrary order.  RTNL required
  * to iterate or modify.
  * @stats_percpu: Per-CPU datapath statistics.
+ * @net: Reference to net namespace.
  *
  * Context: See the comment on locking at the top of datapath.c for additional
  * locking information.
@@ -86,6 +87,11 @@ struct datapath {
 
        /* Stats. */
        struct dp_stats_percpu __percpu *stats_percpu;
+
+#ifdef CONFIG_NET_NS
+       /* Network namespace ref. */
+       struct net *net;
+#endif
 };
 
 /**
@@ -130,6 +136,29 @@ struct dp_upcall_info {
        u32 pid;
 };
 
+/**
+ * struct ovs_net - Per net-namespace data for ovs.
+ * @dps: List of datapaths to enable dumping them all out.
+ * Protected by genl_mutex.
+ * @vport_net: Per network namespace data for vport.
+ */
+struct ovs_net {
+       struct list_head dps;
+       struct vport_net vport_net;
+};
+
+extern int ovs_net_id;
+
+static inline struct net *ovs_dp_get_net(struct datapath *dp)
+{
+       return read_pnet(&dp->net);
+}
+
+static inline void ovs_dp_set_net(struct datapath *dp, struct net *net)
+{
+       write_pnet(&dp->net, net);
+}
+
 extern struct notifier_block ovs_dp_device_notifier;
 extern struct genl_multicast_group ovs_dp_vport_multicast_group;
 extern int (*ovs_dp_ioctl_hook)(struct net_device *dev, struct ifreq *rq, int cmd);
index d040d46..48023a9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2011 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira Networks.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
@@ -46,14 +46,15 @@ static int dp_device_event(struct notifier_block *unused, unsigned long event,
                                                          OVS_VPORT_CMD_DEL);
                        ovs_dp_detach_port(vport);
                        if (IS_ERR(notify)) {
-                               netlink_set_err(INIT_NET_GENL_SOCK, 0,
+                               netlink_set_err(GENL_SOCK(ovs_dp_get_net(vport->dp)), 0,
                                                ovs_dp_vport_multicast_group.id,
                                                PTR_ERR(notify));
                                break;
                        }
 
-                       genlmsg_multicast(notify, 0, ovs_dp_vport_multicast_group.id,
-                                         GFP_KERNEL);
+                       genlmsg_multicast_netns(ovs_dp_get_net(vport->dp), notify, 0,
+                                               ovs_dp_vport_multicast_group.id,
+                                               GFP_KERNEL);
                }
                break;
 
index 1574a93..2582321 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2011 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira Networks.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
@@ -366,6 +366,12 @@ int ovs_dp_sysfs_add_dp(struct datapath *dp)
        struct kobject *kobj = vport->ops->get_kobj(vport);
        int err;
 
+#ifdef CONFIG_NET_NS
+       /* Due to bug in 2.6.32 kernel, sysfs_create_group() could panic
+        * in other namespace than init_net. Following check is to avoid it. */
+       if (!kobj->sd)
+               return -ENOENT;
+#endif
        /* Create /sys/class/net/<devname>/bridge directory. */
        err = sysfs_create_group(kobj, &bridge_group);
        if (err) {
@@ -395,6 +401,11 @@ int ovs_dp_sysfs_del_dp(struct datapath *dp)
        struct vport *vport = rtnl_dereference(dp->ports[OVSP_LOCAL]);
        struct kobject *kobj = vport->ops->get_kobj(vport);
 
+#ifdef CONFIG_NET_NS
+       if (!kobj->sd)
+               return 0;
+#endif
+
        kobject_del(&dp->ifobj);
        sysfs_remove_group(kobj, &bridge_group);
 
index 5b695cf..f564e98 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2011 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira Networks.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
@@ -217,6 +217,14 @@ int ovs_dp_sysfs_add_if(struct vport *p)
        if (!p->ops->get_kobj)
                return -ENOENT;
 
+#ifdef CONFIG_NET_NS
+       /* Due to bug in 2.6.32 kernel, sysfs_create_group() could panic
+        * in other namespace than init_net. Following check is to avoid it. */
+
+       if (!p->kobj.sd)
+               return -ENOENT;
+#endif
+
        err = kobject_add(&p->kobj, p->ops->get_kobj(p),
                          SYSFS_BRIDGE_PORT_ATTR);
        if (err)
index e579529..66c7f94 100644 (file)
@@ -100,6 +100,8 @@ int genl_exec(genl_exec_func_t func, void *data)
 
        genl_exec_function = func;
        genl_exec_data = data;
+
+       /* There is no need to send msg to current namespace. */
        ret = genlmsg_unicast(&init_net, genlmsg_skb, 0);
 
        if (!ret) {
index 40c3927..7f54bde 100644 (file)
@@ -7,6 +7,7 @@ openvswitch_sources += \
        linux/compat/ip_output-openvswitch.c \
        linux/compat/kmemdup.c \
        linux/compat/netdevice.c \
+       linux/compat/net_namespace.c \
        linux/compat/reciprocal_div.c \
        linux/compat/skbuff-openvswitch.c \
        linux/compat/time.c     \
@@ -62,6 +63,8 @@ openvswitch_headers += \
        linux/compat/include/net/netlink.h \
        linux/compat/include/net/protocol.h \
        linux/compat/include/net/route.h \
+       linux/compat/include/net/sock.h \
+       linux/compat/include/net/netns/generic.h \
        linux/compat/genetlink.inc
 
 both_modules += brcompat
index a1ff7c1..af7d5fd 100644 (file)
@@ -101,6 +101,10 @@ static inline int genlmsg_multicast_flags(struct sk_buff *skb, u32 pid,
 }
 #endif /* linux kernel < 2.6.19 */
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)
+#define genlmsg_multicast_netns(net, skb, pid, grp, flags) \
+               genlmsg_multicast(skb, pid, grp, flags)
+#endif
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
 
index 9ce9fcd..77f0a16 100644 (file)
@@ -4,12 +4,86 @@
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
 /* <net/net_namespace.h> exists, go ahead and include it. */
 #include_next <net/net_namespace.h>
-#endif
+#else
+/* No network namespace support. */
+struct net;
+
+static inline struct net *hold_net(struct net *net)
+{
+       return net;
+}
+
+static inline void release_net(struct net *net)
+{
+}
+
+#define __net_init      __init
+#define __net_exit      __exit
+#endif /* 2.6.24 */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)
+#ifdef CONFIG_NET_NS
+static inline
+int net_eq(const struct net *net1, const struct net *net2)
+{
+       return net1 == net2;
+}
+#else
+static inline
+int net_eq(const struct net *net1, const struct net *net2)
+{
+       return 1;
+}
+#endif /* CONFIG_NET_NS */
+#endif /* 2.6.26 */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)
+#ifdef CONFIG_NET_NS
+
+static inline void write_pnet(struct net **pnet, struct net *net)
+{
+       *pnet = net;
+}
+
+static inline struct net *read_pnet(struct net * const *pnet)
+{
+       return *pnet;
+}
+
+#else
+
+#define write_pnet(pnet, net)   do { (void)(net); } while (0)
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
-#define INIT_NET_GENL_SOCK init_net.genl_sock
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24)
+#define read_pnet(pnet)         (&init_net)
 #else
-#define INIT_NET_GENL_SOCK genl_sock
-#endif
+#define read_pnet(pnet)         (NULL)
+#endif /* 2.6.24 */
+
+#endif /* CONFIG_NET_NS */
+#endif /* 2.6.29 */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
+#define pernet_operations rpl_pernet_operations
+struct pernet_operations {
+       int (*init)(struct net *net);
+       void (*exit)(struct net *net);
+       int *id;
+       size_t size;
+};
+
+extern int rpl_register_pernet_gen_device(struct rpl_pernet_operations *ops);
+extern void rpl_unregister_pernet_gen_device(struct rpl_pernet_operations *ops);
+
+#define register_pernet_device rpl_register_pernet_gen_device
+#define unregister_pernet_device rpl_unregister_pernet_gen_device
+
+#endif /* 2.6.33 */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)
+#undef for_each_net
+#define for_each_net(net)   { net = NULL; }
+
+#endif /* 2.6.32 */
 
 #endif /* net/net_namespace.h wrapper */
diff --git a/datapath/linux/compat/include/net/netns/generic.h b/datapath/linux/compat/include/net/netns/generic.h
new file mode 100644 (file)
index 0000000..1a0303f
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef __NET_NET_NETNS_GENERIC_WRAPPER_H
+#define __NET_NET_NETNS_GENERIC_WRAPPER_H 1
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
+/* <net/netns/generic.h> exists, go ahead and include it. */
+#include_next <net/netns/generic.h>
+#else
+#define net_generic rpl_net_generic
+void *net_generic(const struct net *net, int id);
+#endif
+
+#endif /* net/netns/generic.h wrapper */
diff --git a/datapath/linux/compat/include/net/sock.h b/datapath/linux/compat/include/net/sock.h
new file mode 100644 (file)
index 0000000..513489a
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef __NET_SOCK_WRAPPER_H
+#define __NET_SOCK_WRAPPER_H 1
+
+#include_next <net/sock.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
+struct net;
+
+static inline struct net *sock_net(const struct sock *sk)
+{
+       return NULL;
+}
+
+#endif
+
+#endif /* net/sock.h wrapper */
diff --git a/datapath/linux/compat/net_namespace.c b/datapath/linux/compat/net_namespace.c
new file mode 100644 (file)
index 0000000..4e8a891
--- /dev/null
@@ -0,0 +1,108 @@
+#include <linux/if_vlan.h>
+#include <linux/netdevice.h>
+#include <net/net_namespace.h>
+#include <net/netns/generic.h>
+
+#undef pernet_operations
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,32)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)
+static int net_assign_generic(struct net *net, int id, void *data);
+#endif
+
+static int __net_init compat_init_net(struct net *net, struct rpl_pernet_operations *pnet)
+{
+       int err;
+       void *ovs_net = kzalloc(pnet->size, GFP_KERNEL);
+
+       if (!ovs_net)
+               return -ENOMEM;
+
+       err = net_assign_generic(net, *pnet->id, ovs_net);
+       if (err)
+               goto err;
+
+       if (pnet->init) {
+               err = pnet->init(net);
+               if (err)
+                       goto err;
+       }
+
+       return 0;
+err:
+       kfree(ovs_net);
+       return err;
+}
+
+static void __net_exit compat_exit_net(struct net *net, struct rpl_pernet_operations *pnet)
+{
+       void *ovs_net = net_generic(net, *pnet->id);
+
+       if (pnet->exit)
+               pnet->exit(net);
+       kfree(ovs_net);
+}
+#endif
+
+#if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,32)
+#define DEFINE_PNET_REG_FUNC(PNET_TYPE)                                        \
+       static struct rpl_pernet_operations *pnet_##PNET_TYPE;          \
+static int __net_init compat_init_net_##PNET_TYPE(struct net *net)     \
+{                                                                      \
+       return compat_init_net(net, pnet_##PNET_TYPE);                  \
+}                                                                      \
+                                                                       \
+static void __net_exit compat_exit_net_##PNET_TYPE(struct net *net)    \
+{                                                                      \
+       compat_exit_net(net, pnet_##PNET_TYPE);                         \
+}                                                                      \
+                                                                       \
+static struct pernet_operations pnet_compat_##PNET_TYPE = {            \
+       .init = compat_init_net_##PNET_TYPE,                            \
+       .exit = compat_exit_net_##PNET_TYPE,                            \
+};                                                                     \
+                                                                       \
+int rpl_register_pernet_##PNET_TYPE(struct rpl_pernet_operations *rpl_pnet)    \
+{                                                                              \
+       pnet_##PNET_TYPE = rpl_pnet;                                            \
+       return register_pernet_##PNET_TYPE(pnet_##PNET_TYPE->id, &pnet_compat_##PNET_TYPE); \
+}                                                                                      \
+                                                                                       \
+void rpl_unregister_pernet_##PNET_TYPE(struct rpl_pernet_operations *pnet)             \
+{                                                                                      \
+       unregister_pernet_##PNET_TYPE(*pnet->id, &pnet_compat_##PNET_TYPE);             \
+}
+
+DEFINE_PNET_REG_FUNC(gen_device);
+
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,6,32)
+#define MAX_DATA_COUNT 1
+static struct net *net;
+
+static void *__ovs_net_data[MAX_DATA_COUNT];
+static int count;
+
+static int net_assign_generic(struct net *net, int id, void *data)
+{
+       BUG_ON(id >= MAX_DATA_COUNT);
+       __ovs_net_data[id] = data;
+       return 0;
+}
+
+void *net_generic(const struct net *net, int id)
+{
+       return __ovs_net_data[id];
+}
+
+int rpl_register_pernet_gen_device(struct rpl_pernet_operations *rpl_pnet)
+{
+       *rpl_pnet->id = count++;
+       return compat_init_net(net, rpl_pnet);
+}
+
+void rpl_unregister_pernet_gen_device(struct rpl_pernet_operations *rpl_pnet)
+{
+       compat_exit_net(net, rpl_pnet);
+}
+
+#endif
index 33d2fe9..cdbf94a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2011 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira Networks.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
@@ -16,6 +16,8 @@
  * 02110-1301, USA
  */
 
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/if_arp.h>
 #include <linux/if_ether.h>
 #include <linux/ip.h>
@@ -169,7 +171,7 @@ static void free_mutable_rtnl(struct tnl_mutable_config *mutable)
        ASSERT_RTNL();
        if (ipv4_is_multicast(mutable->key.daddr) && mutable->mlink) {
                struct in_device *in_dev;
-               in_dev = inetdev_by_index(&init_net, mutable->mlink);
+               in_dev = inetdev_by_index(port_key_get_net(&mutable->key), mutable->mlink);
                if (in_dev)
                        ip_mc_dec_group(in_dev, mutable->key.daddr);
        }
@@ -299,14 +301,15 @@ static struct vport *port_table_lookup(struct port_lookup_key *key,
        return NULL;
 }
 
-struct vport *ovs_tnl_find_port(__be32 saddr, __be32 daddr, __be64 key,
-                               int tunnel_type,
+struct vport *ovs_tnl_find_port(struct net *net, __be32 saddr, __be32 daddr,
+                               __be64 key, int tunnel_type,
                                const struct tnl_mutable_config **mutable)
 {
        struct port_lookup_key lookup;
        struct vport *vport;
        bool is_multicast = ipv4_is_multicast(saddr);
 
+       port_key_set_net(&lookup, net);
        lookup.saddr = saddr;
        lookup.daddr = daddr;
 
@@ -811,6 +814,13 @@ static void *get_cached_header(const struct tnl_cache *cache)
        return (void *)cache + ALIGN(sizeof(struct tnl_cache), CACHE_DATA_ALIGN);
 }
 
+#ifdef HAVE_RT_GENID
+static inline int rt_genid(struct net *net)
+{
+       return atomic_read(&net->ipv4.rt_genid);
+}
+#endif
+
 static bool check_cache_valid(const struct tnl_cache *cache,
                              const struct tnl_mutable_config *mutable)
 {
@@ -825,7 +835,7 @@ static bool check_cache_valid(const struct tnl_cache *cache,
                time_before(jiffies, cache->expiration) &&
 #endif
 #ifdef HAVE_RT_GENID
-               atomic_read(&init_net.ipv4.rt_genid) == cache->rt->rt_genid &&
+               rt_genid(dev_net(rt_dst(cache->rt).dev)) == cache->rt->rt_genid &&
 #endif
 #ifdef HAVE_HH_SEQ
                hh->hh_lock.sequence == cache->hh_seq &&
@@ -858,7 +868,7 @@ static void cache_cleaner(struct work_struct *work)
        for (i = 0; i < PORT_TABLE_SIZE; i++) {
                struct hlist_node *n;
                struct hlist_head *bucket;
-               struct tnl_vport  *tnl_vport;
+               struct tnl_vport *tnl_vport;
 
                bucket = &port_table[i];
                hlist_for_each_entry_rcu(tnl_vport, n, bucket, hash_node)
@@ -1000,7 +1010,7 @@ static struct rtable *__find_route(const struct tnl_mutable_config *mutable,
                            .proto = ipproto };
        struct rtable *rt;
 
-       if (unlikely(ip_route_output_key(&init_net, &rt, &fl)))
+       if (unlikely(ip_route_output_key(port_key_get_net(&mutable->key), &rt, &fl)))
                return ERR_PTR(-EADDRNOTAVAIL);
 
        return rt;
@@ -1010,7 +1020,7 @@ static struct rtable *__find_route(const struct tnl_mutable_config *mutable,
                             .flowi4_tos = tos,
                             .flowi4_proto = ipproto };
 
-       return ip_route_output_key(&init_net, &fl);
+       return ip_route_output_key(port_key_get_net(&mutable->key), &fl);
 #endif
 }
 
@@ -1360,7 +1370,8 @@ static const struct nla_policy tnl_policy[OVS_TUNNEL_ATTR_MAX + 1] = {
 
 /* Sets OVS_TUNNEL_ATTR_* fields in 'mutable', which must initially be
  * zeroed. */
-static int tnl_set_config(struct nlattr *options, const struct tnl_ops *tnl_ops,
+static int tnl_set_config(struct net *net, struct nlattr *options,
+                         const struct tnl_ops *tnl_ops,
                          const struct vport *cur_vport,
                          struct tnl_mutable_config *mutable)
 {
@@ -1381,6 +1392,7 @@ static int tnl_set_config(struct nlattr *options, const struct tnl_ops *tnl_ops,
 
        mutable->flags = nla_get_u32(a[OVS_TUNNEL_ATTR_FLAGS]) & TNL_F_PUBLIC;
 
+       port_key_set_net(&mutable->key, net);
        mutable->key.daddr = nla_get_be32(a[OVS_TUNNEL_ATTR_DST_IPV4]);
        if (a[OVS_TUNNEL_ATTR_SRC_IPV4]) {
                if (ipv4_is_multicast(mutable->key.daddr))
@@ -1472,7 +1484,8 @@ struct vport *ovs_tnl_create(const struct vport_parms *parms,
        get_random_bytes(&initial_frag_id, sizeof(int));
        atomic_set(&tnl_vport->frag_id, initial_frag_id);
 
-       err = tnl_set_config(parms->options, tnl_ops, NULL, mutable);
+       err = tnl_set_config(ovs_dp_get_net(parms->dp), parms->options, tnl_ops,
+                            NULL, mutable);
        if (err)
                goto error_free_mutable;
 
@@ -1516,7 +1529,8 @@ int ovs_tnl_set_options(struct vport *vport, struct nlattr *options)
        memcpy(mutable->eth_addr, old_mutable->eth_addr, ETH_ALEN);
 
        /* Parse the others configured by userspace. */
-       err = tnl_set_config(options, tnl_vport->tnl_ops, vport, mutable);
+       err = tnl_set_config(ovs_dp_get_net(vport->dp), options, tnl_vport->tnl_ops,
+                            vport, mutable);
        if (err)
                goto error_free;
 
@@ -1624,7 +1638,7 @@ int ovs_tnl_init(void)
        int i;
 
        port_table = kmalloc(PORT_TABLE_SIZE * sizeof(struct hlist_head *),
-                       GFP_KERNEL);
+                            GFP_KERNEL);
        if (!port_table)
                return -ENOMEM;
 
@@ -1636,19 +1650,5 @@ int ovs_tnl_init(void)
 
 void ovs_tnl_exit(void)
 {
-       int i;
-
-       for (i = 0; i < PORT_TABLE_SIZE; i++) {
-               struct tnl_vport *tnl_vport;
-               struct hlist_head *hash_head;
-               struct hlist_node *n;
-
-               hash_head = &port_table[i];
-               hlist_for_each_entry(tnl_vport, n, hash_head, hash_node) {
-                       BUG();
-                       goto out;
-               }
-       }
-out:
        kfree(port_table);
 }
index 6865ae6..33eb63c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2011 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira Networks.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
@@ -20,6 +20,8 @@
 #define TUNNEL_H 1
 
 #include <linux/version.h>
+#include <net/net_namespace.h>
+#include <net/netns/generic.h>
 
 #include "flow.h"
 #include "openvswitch/tunnel.h"
 /**
  * struct port_lookup_key - Tunnel port key, used as hash table key.
  * @in_key: Key to match on input, 0 for wildcard.
+ * @net: Network namespace of the port.
  * @saddr: IPv4 source address to match, 0 to accept any source address.
  * @daddr: IPv4 destination of tunnel.
  * @tunnel_type: Set of TNL_T_* flags that define lookup.
  */
 struct port_lookup_key {
        __be64 in_key;
+#ifdef CONFIG_NET_NS
+       struct net *net;
+#endif
        __be32 saddr;
        __be32 daddr;
        u32    tunnel_type;
@@ -72,6 +78,16 @@ struct port_lookup_key {
 #define PORT_KEY_LEN   (offsetof(struct port_lookup_key, tunnel_type) + \
                         FIELD_SIZEOF(struct port_lookup_key, tunnel_type))
 
+static inline struct net *port_key_get_net(const struct port_lookup_key *key)
+{
+       return read_pnet(&key->net);
+}
+
+static inline void port_key_set_net(struct port_lookup_key *key, struct net *net)
+{
+       write_pnet(&key->net, net);
+}
+
 /**
  * struct tnl_mutable_config - modifiable configuration for a tunnel.
  * @key: Used as key for tunnel port.  Configured via OVS_TUNNEL_ATTR_*
@@ -255,8 +271,8 @@ const unsigned char *ovs_tnl_get_addr(const struct vport *vport);
 int ovs_tnl_send(struct vport *vport, struct sk_buff *skb);
 void ovs_tnl_rcv(struct vport *vport, struct sk_buff *skb, u8 tos);
 
-struct vport *ovs_tnl_find_port(__be32 saddr, __be32 daddr, __be64 key,
-                               int tunnel_type,
+struct vport *ovs_tnl_find_port(struct net *net, __be32 saddr, __be32 daddr,
+                               __be64 key, int tunnel_type,
                                const struct tnl_mutable_config **mutable);
 bool ovs_tnl_frag_needed(struct vport *vport,
                         const struct tnl_mutable_config *mutable,
index 6c1b0da..8fbd4a7 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2011 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira Networks.
  * Distributed under the terms of the GNU GPL version 2.
  *
  * Significant portions of this file may be copied from parts of the Linux
@@ -16,6 +16,7 @@
 #include <linux/ip.h>
 #include <linux/list.h>
 #include <linux/net.h>
+#include <net/net_namespace.h>
 
 #include <net/icmp.h>
 #include <net/inet_frag.h>
@@ -23,6 +24,7 @@
 #include <net/protocol.h>
 #include <net/udp.h>
 
+#include "datapath.h"
 #include "tunnel.h"
 #include "vport.h"
 #include "vport-generic.h"
@@ -137,8 +139,6 @@ struct frag_skb_cb {
 
 static struct sk_buff *fragment(struct sk_buff *, const struct vport *,
                                struct dst_entry *dst, unsigned int hlen);
-static void defrag_init(void);
-static void defrag_exit(void);
 static struct sk_buff *defrag(struct sk_buff *, bool frag_last);
 
 static void capwap_frag_init(struct inet_frag_queue *, void *match);
@@ -154,13 +154,6 @@ static struct inet_frags frag_state = {
        .frag_expire    = capwap_frag_expire,
        .secret_interval = CAPWAP_FRAG_SECRET_INTERVAL,
 };
-static struct netns_frags frag_netns_state = {
-       .timeout        = CAPWAP_FRAG_TIMEOUT,
-       .high_thresh    = CAPWAP_FRAG_MAX_MEM,
-       .low_thresh     = CAPWAP_FRAG_PRUNE_MEM,
-};
-
-static struct socket *capwap_rcv_socket;
 
 static int capwap_hdr_len(const struct tnl_mutable_config *mutable)
 {
@@ -333,8 +326,8 @@ static int capwap_rcv(struct sock *sk, struct sk_buff *skb)
                goto out;
 
        iph = ip_hdr(skb);
-       vport = ovs_tnl_find_port(iph->daddr, iph->saddr, key, TNL_T_PROTO_CAPWAP,
-                                 &mutable);
+       vport = ovs_tnl_find_port(sock_net(sk), iph->daddr, iph->saddr, key,
+                                 TNL_T_PROTO_CAPWAP, &mutable);
        if (unlikely(!vport)) {
                icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
                goto error;
@@ -362,49 +355,105 @@ static const struct tnl_ops capwap_tnl_ops = {
        .update_header  = capwap_update_header,
 };
 
-static struct vport *capwap_create(const struct vport_parms *parms)
+static inline struct capwap_net *ovs_get_capwap_net(struct net *net)
 {
-       return ovs_tnl_create(parms, &ovs_capwap_vport_ops, &capwap_tnl_ops);
+       struct ovs_net *ovs_net = net_generic(net, ovs_net_id);
+       return &ovs_net->vport_net.capwap;
 }
 
 /* Random value.  Irrelevant as long as it's not 0 since we set the handler. */
 #define UDP_ENCAP_CAPWAP 10
-static int capwap_init(void)
+static int init_socket(struct net *net)
 {
        int err;
+       struct capwap_net *capwap_net = ovs_get_capwap_net(net);
        struct sockaddr_in sin;
 
-       err = sock_create(AF_INET, SOCK_DGRAM, 0, &capwap_rcv_socket);
+       if (capwap_net->n_tunnels) {
+               capwap_net->n_tunnels++;
+               return 0;
+       }
+
+       err = sock_create_kern(AF_INET, SOCK_DGRAM, 0,
+                              &capwap_net->capwap_rcv_socket);
        if (err)
                goto error;
 
+       /* release net ref. */
+       sk_change_net(capwap_net->capwap_rcv_socket->sk, net);
+
        sin.sin_family = AF_INET;
        sin.sin_addr.s_addr = htonl(INADDR_ANY);
        sin.sin_port = htons(CAPWAP_DST_PORT);
 
-       err = kernel_bind(capwap_rcv_socket, (struct sockaddr *)&sin,
+       err = kernel_bind(capwap_net->capwap_rcv_socket,
+                         (struct sockaddr *)&sin,
                          sizeof(struct sockaddr_in));
        if (err)
                goto error_sock;
 
-       udp_sk(capwap_rcv_socket->sk)->encap_type = UDP_ENCAP_CAPWAP;
-       udp_sk(capwap_rcv_socket->sk)->encap_rcv = capwap_rcv;
+       udp_sk(capwap_net->capwap_rcv_socket->sk)->encap_type = UDP_ENCAP_CAPWAP;
+       udp_sk(capwap_net->capwap_rcv_socket->sk)->encap_rcv = capwap_rcv;
+
+       capwap_net->frag_state.timeout          = CAPWAP_FRAG_TIMEOUT;
+       capwap_net->frag_state.high_thresh      = CAPWAP_FRAG_MAX_MEM;
+       capwap_net->frag_state.low_thresh       = CAPWAP_FRAG_PRUNE_MEM;
 
-       defrag_init();
+       inet_frags_init_net(&capwap_net->frag_state);
 
+       capwap_net->n_tunnels++;
        return 0;
 
 error_sock:
-       sock_release(capwap_rcv_socket);
+       sk_release_kernel(capwap_net->capwap_rcv_socket->sk);
 error:
-       pr_warn("cannot register capwap protocol handler\n");
+       pr_warn("cannot register capwap protocol handler : %d\n", err);
        return err;
 }
 
+static void release_socket(struct net *net)
+{
+       struct capwap_net *capwap_net = ovs_get_capwap_net(net);
+
+       capwap_net->n_tunnels--;
+       if (capwap_net->n_tunnels)
+               return;
+
+       inet_frags_exit_net(&capwap_net->frag_state, &frag_state);
+       sk_release_kernel(capwap_net->capwap_rcv_socket->sk);
+}
+
+static struct vport *capwap_create(const struct vport_parms *parms)
+{
+       struct vport *vport;
+       int err;
+
+       err = init_socket(ovs_dp_get_net(parms->dp));
+       if (err)
+               return ERR_PTR(err);
+
+       vport = ovs_tnl_create(parms, &ovs_capwap_vport_ops, &capwap_tnl_ops);
+       if (IS_ERR(vport))
+               release_socket(ovs_dp_get_net(parms->dp));
+
+       return vport;
+}
+
+static void capwap_destroy(struct vport *vport)
+{
+       ovs_tnl_destroy(vport);
+       release_socket(ovs_dp_get_net(vport->dp));
+}
+
+static int capwap_init(void)
+{
+       inet_frags_init(&frag_state);
+       return 0;
+}
+
 static void capwap_exit(void)
 {
-       defrag_exit();
-       sock_release(capwap_rcv_socket);
+       inet_frags_fini(&frag_state);
 }
 
 static void copy_skb_metadata(struct sk_buff *from, struct sk_buff *to)
@@ -529,13 +578,14 @@ static u32 frag_hash(struct frag_match *match)
                            frag_state.rnd) & (INETFRAGS_HASHSZ - 1);
 }
 
-static struct frag_queue *queue_find(struct frag_match *match)
+static struct frag_queue *queue_find(struct netns_frags *ns_frag_state,
+                                    struct frag_match *match)
 {
        struct inet_frag_queue *ifq;
 
        read_lock(&frag_state.lock);
 
-       ifq = inet_frag_find(&frag_netns_state, &frag_state, match, frag_hash(match));
+       ifq = inet_frag_find(ns_frag_state, &frag_state, match, frag_hash(match));
        if (!ifq)
                return NULL;
 
@@ -710,19 +760,21 @@ static struct sk_buff *defrag(struct sk_buff *skb, bool frag_last)
 {
        struct iphdr *iph = ip_hdr(skb);
        struct capwaphdr *cwh = capwap_hdr(skb);
+       struct capwap_net *capwap_net = ovs_get_capwap_net(dev_net(skb->dev));
+       struct netns_frags *ns_frag_state = &capwap_net->frag_state;
        struct frag_match match;
        u16 frag_off;
        struct frag_queue *fq;
 
-       if (atomic_read(&frag_netns_state.mem) > frag_netns_state.high_thresh)
-               inet_frag_evictor(&frag_netns_state, &frag_state);
+       if (atomic_read(&ns_frag_state->mem) > ns_frag_state->high_thresh)
+               inet_frag_evictor(ns_frag_state, &frag_state);
 
        match.daddr = iph->daddr;
        match.saddr = iph->saddr;
        match.id = cwh->frag_id;
        frag_off = ntohs(cwh->frag_off) & FRAG_OFF_MASK;
 
-       fq = queue_find(&match);
+       fq = queue_find(ns_frag_state, &match);
        if (fq) {
                spin_lock(&fq->ifq.lock);
                skb = frag_queue(fq, skb, frag_off, frag_last);
@@ -737,18 +789,6 @@ static struct sk_buff *defrag(struct sk_buff *skb, bool frag_last)
        return NULL;
 }
 
-static void defrag_init(void)
-{
-       inet_frags_init(&frag_state);
-       inet_frags_init_net(&frag_netns_state);
-}
-
-static void defrag_exit(void)
-{
-       inet_frags_exit_net(&frag_netns_state, &frag_state);
-       inet_frags_fini(&frag_state);
-}
-
 static void capwap_frag_init(struct inet_frag_queue *ifq, void *match_)
 {
        struct frag_match *match = match_;
@@ -791,7 +831,7 @@ const struct vport_ops ovs_capwap_vport_ops = {
        .init           = capwap_init,
        .exit           = capwap_exit,
        .create         = capwap_create,
-       .destroy        = ovs_tnl_destroy,
+       .destroy        = capwap_destroy,
        .set_addr       = ovs_tnl_set_addr,
        .get_name       = ovs_tnl_get_name,
        .get_addr       = ovs_tnl_get_addr,
diff --git a/datapath/vport-capwap.h b/datapath/vport-capwap.h
new file mode 100644 (file)
index 0000000..b33cc94
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2012 Nicira Networks.
+ *
+ * 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
+ */
+
+#ifndef VPORT_CAPWAP_H
+#define VPORT_CAPWAP_H 1
+
+#include <linux/net.h>
+
+struct capwap_net {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
+       struct socket *capwap_rcv_socket;
+       struct netns_frags frag_state;
+       int n_tunnels;
+#endif
+};
+
+#endif /* vport-capwap.h */
index eb10009..3bb55f0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2011 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira Networks.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
@@ -29,6 +29,7 @@
 #include <net/ip.h>
 #include <net/protocol.h>
 
+#include "datapath.h"
 #include "tunnel.h"
 #include "vport.h"
 #include "vport-generic.h"
@@ -206,8 +207,8 @@ static void gre_err(struct sk_buff *skb, u32 info)
        if (tunnel_hdr_len < 0)
                return;
 
-       vport = ovs_tnl_find_port(iph->saddr, iph->daddr, key, TNL_T_PROTO_GRE,
-                                 &mutable);
+       vport = ovs_tnl_find_port(dev_net(skb->dev), iph->saddr, iph->daddr, key,
+                                 TNL_T_PROTO_GRE, &mutable);
        if (!vport)
                return;
 
@@ -343,8 +344,8 @@ static int gre_rcv(struct sk_buff *skb)
                goto error;
 
        iph = ip_hdr(skb);
-       vport = ovs_tnl_find_port(iph->daddr, iph->saddr, key, TNL_T_PROTO_GRE,
-                                 &mutable);
+       vport = ovs_tnl_find_port(dev_net(skb->dev), iph->daddr, iph->saddr, key,
+                                 TNL_T_PROTO_GRE, &mutable);
        if (unlikely(!vport)) {
                icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
                goto error;
@@ -382,6 +383,9 @@ static struct vport *gre_create(const struct vport_parms *parms)
 static const struct net_protocol gre_protocol_handlers = {
        .handler        =       gre_rcv,
        .err_handler    =       gre_err,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
+       .netns_ok       =       1,
+#endif
 };
 
 static int gre_init(void)
index c56f3b2..46b78d2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2011 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira Networks.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
@@ -203,7 +203,7 @@ static void do_setup(struct net_device *netdev)
        netdev->tx_queue_len = 0;
 
        netdev->features = NETIF_F_LLTX | NETIF_F_SG | NETIF_F_FRAGLIST |
-                               NETIF_F_HIGHDMA | NETIF_F_HW_CSUM | NETIF_F_TSO;
+                          NETIF_F_HIGHDMA | NETIF_F_HW_CSUM | NETIF_F_TSO;
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
        netdev->vlan_features = netdev->features;
@@ -239,9 +239,14 @@ static struct vport *internal_dev_create(const struct vport_parms *parms)
                goto error_free_vport;
        }
 
+       dev_net_set(netdev_vport->dev, ovs_dp_get_net(vport->dp));
        internal_dev = internal_dev_priv(netdev_vport->dev);
        internal_dev->vport = vport;
 
+       /* Restrict bridge port to current netns. */
+       if (vport->port_no == OVSP_LOCAL)
+               netdev_vport->dev->features |= NETIF_F_NETNS_LOCAL;
+
        err = register_netdevice(netdev_vport->dev);
        if (err)
                goto error_free_netdev;
index 4cca7fe..12a3494 100644 (file)
@@ -139,7 +139,7 @@ static struct vport *netdev_create(const struct vport_parms *parms)
 
        netdev_vport = netdev_vport_priv(vport);
 
-       netdev_vport->dev = dev_get_by_name(&init_net, parms->name);
+       netdev_vport->dev = dev_get_by_name(ovs_dp_get_net(vport->dp), parms->name);
        if (!netdev_vport->dev) {
                err = -ENODEV;
                goto error_free_vport;
index 53b24b0..0056792 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2011 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira Networks.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
  * 02110-1301, USA
  */
 
-#include <linux/dcache.h>
 #include <linux/kernel.h>
+#include <linux/jhash.h>
 #include <linux/list.h>
 #include <linux/rtnetlink.h>
+#include <net/net_namespace.h>
 
 #include "compat.h"
 #include "datapath.h"
@@ -49,7 +50,7 @@ struct patch_vport {
 static struct hlist_head *peer_table;
 #define PEER_HASH_BUCKETS 256
 
-static void update_peers(const char *name, struct vport *);
+static void update_peers(struct net *, const char *name, struct vport *);
 
 static struct patch_vport *patch_vport_priv(const struct vport *vport)
 {
@@ -74,9 +75,9 @@ static void assign_config_rcu(struct vport *vport,
        call_rcu(&old_config->rcu, free_config);
 }
 
-static struct hlist_head *hash_bucket(const char *name)
+static struct hlist_head *hash_bucket(struct net *net, const char *name)
 {
-       unsigned int hash = full_name_hash(name, strlen(name));
+       unsigned int hash = jhash(name, strlen(name), (unsigned long) net);
        return &peer_table[hash & (PEER_HASH_BUCKETS - 1)];
 }
 
@@ -135,6 +136,7 @@ static struct vport *patch_create(const struct vport_parms *parms)
        struct patch_vport *patch_vport;
        const char *peer_name;
        struct patch_config *patchconf;
+       struct net *net = ovs_dp_get_net(parms->dp);
        int err;
 
        vport = ovs_vport_alloc(sizeof(struct patch_vport),
@@ -163,9 +165,9 @@ static struct vport *patch_create(const struct vport_parms *parms)
        rcu_assign_pointer(patch_vport->patchconf, patchconf);
 
        peer_name = patchconf->peer_name;
-       hlist_add_head(&patch_vport->hash_node, hash_bucket(peer_name));
-       rcu_assign_pointer(patch_vport->peer, ovs_vport_locate(peer_name));
-       update_peers(patch_vport->name, vport);
+       hlist_add_head(&patch_vport->hash_node, hash_bucket(net, peer_name));
+       rcu_assign_pointer(patch_vport->peer, ovs_vport_locate(net, peer_name));
+       update_peers(net, patch_vport->name, vport);
 
        return vport;
 
@@ -190,7 +192,7 @@ static void patch_destroy(struct vport *vport)
 {
        struct patch_vport *patch_vport = patch_vport_priv(vport);
 
-       update_peers(patch_vport->name, NULL);
+       update_peers(ovs_dp_get_net(vport->dp), patch_vport->name, NULL);
        hlist_del(&patch_vport->hash_node);
        call_rcu(&patch_vport->rcu, free_port_rcu);
 }
@@ -216,28 +218,31 @@ static int patch_set_options(struct vport *vport, struct nlattr *options)
 
        hlist_del(&patch_vport->hash_node);
 
-       rcu_assign_pointer(patch_vport->peer, ovs_vport_locate(patchconf->peer_name));
-       hlist_add_head(&patch_vport->hash_node, hash_bucket(patchconf->peer_name));
+       rcu_assign_pointer(patch_vport->peer,
+               ovs_vport_locate(ovs_dp_get_net(vport->dp), patchconf->peer_name));
 
-       return 0;
+       hlist_add_head(&patch_vport->hash_node,
+                      hash_bucket(ovs_dp_get_net(vport->dp), patchconf->peer_name));
 
+       return 0;
 error_free:
        kfree(patchconf);
 error:
        return err;
 }
 
-static void update_peers(const char *name, struct vport *vport)
+static void update_peers(struct net *net, const char *name, struct vport *vport)
 {
-       struct hlist_head *bucket = hash_bucket(name);
+       struct hlist_head *bucket = hash_bucket(ovs_dp_get_net(vport->dp), name);
        struct patch_vport *peer_vport;
        struct hlist_node *node;
 
        hlist_for_each_entry(peer_vport, node, bucket, hash_node) {
+               struct vport *curr_vport = vport_from_priv(peer_vport);
                const char *peer_name;
 
                peer_name = rtnl_dereference(peer_vport->patchconf)->peer_name;
-               if (!strcmp(peer_name, name))
+               if (!strcmp(peer_name, name) && net_eq(ovs_dp_get_net(curr_vport->dp), net))
                        rcu_assign_pointer(peer_vport->peer, vport);
        }
 }
index e9ccdbd..d81f686 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2011 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira Networks.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
  * 02110-1301, USA
  */
 
-#include <linux/dcache.h>
 #include <linux/etherdevice.h>
 #include <linux/if.h>
 #include <linux/if_vlan.h>
+#include <linux/jhash.h>
 #include <linux/kernel.h>
 #include <linux/list.h>
 #include <linux/mutex.h>
@@ -28,7 +28,9 @@
 #include <linux/rtnetlink.h>
 #include <linux/compat.h>
 #include <linux/version.h>
+#include <net/net_namespace.h>
 
+#include "datapath.h"
 #include "vport.h"
 #include "vport-internal_dev.h"
 
@@ -119,9 +121,9 @@ void ovs_vport_exit(void)
        kfree(dev_table);
 }
 
-static struct hlist_head *hash_bucket(const char *name)
+static struct hlist_head *hash_bucket(struct net *net, const char *name)
 {
-       unsigned int hash = full_name_hash(name, strlen(name));
+       unsigned int hash = jhash(name, strlen(name), (unsigned long) net);
        return &dev_table[hash & (VPORT_HASH_BUCKETS - 1)];
 }
 
@@ -132,14 +134,15 @@ static struct hlist_head *hash_bucket(const char *name)
  *
  * Must be called with RTNL or RCU read lock.
  */
-struct vport *ovs_vport_locate(const char *name)
+struct vport *ovs_vport_locate(struct net *net, const char *name)
 {
-       struct hlist_head *bucket = hash_bucket(name);
+       struct hlist_head *bucket = hash_bucket(net, name);
        struct vport *vport;
        struct hlist_node *node;
 
        hlist_for_each_entry_rcu(vport, node, bucket, hash_node)
-               if (!strcmp(name, vport->ops->get_name(vport)))
+               if (!strcmp(name, vport->ops->get_name(vport)) &&
+                   net_eq(ovs_dp_get_net(vport->dp), net))
                        return vport;
 
        return NULL;
@@ -241,14 +244,17 @@ struct vport *ovs_vport_add(const struct vport_parms *parms)
 
        for (i = 0; i < n_vport_types; i++) {
                if (vport_ops_list[i]->type == parms->type) {
+                       struct hlist_head *bucket;
+
                        vport = vport_ops_list[i]->create(parms);
                        if (IS_ERR(vport)) {
                                err = PTR_ERR(vport);
                                goto out;
                        }
 
-                       hlist_add_head_rcu(&vport->hash_node,
-                                          hash_bucket(vport->ops->get_name(vport)));
+                       bucket = hash_bucket(ovs_dp_get_net(vport->dp),
+                                            vport->ops->get_name(vport));
+                       hlist_add_head_rcu(&vport->hash_node, bucket);
                        return vport;
                }
        }
index 44cf603..ee9715d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007-2011 Nicira Networks.
+ * Copyright (c) 2007-2012 Nicira Networks.
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of version 2 of the GNU General Public
 #define VPORT_H 1
 
 #include <linux/list.h>
+#include <linux/netlink.h>
 #include <linux/openvswitch.h>
 #include <linux/skbuff.h>
 #include <linux/spinlock.h>
 #include <linux/u64_stats_sync.h>
 
-#include "datapath.h"
+#include "vport-capwap.h"
 
 struct vport;
 struct vport_parms;
 
+struct vport_net {
+       struct capwap_net capwap;
+};
+
 /* The following definitions are for users of the vport subsytem: */
 
 int ovs_vport_init(void);
@@ -38,7 +43,7 @@ void ovs_vport_exit(void);
 struct vport *ovs_vport_add(const struct vport_parms *);
 void ovs_vport_del(struct vport *);
 
-struct vport *ovs_vport_locate(const char *name);
+struct vport *ovs_vport_locate(struct net *net, const char *name);
 
 int ovs_vport_set_addr(struct vport *, const unsigned char *);
 void ovs_vport_set_stats(struct vport *, struct ovs_vport_stats *);