aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPravin B Shelar <pshelar@nicira.com>2012-01-30 06:56:54 -0800
committerPravin B Shelar <pshelar@nicira.com>2012-01-30 06:56:54 -0800
commit2a4999f3f33467f4fa22ed6e5b06350615fb2dac (patch)
tree0749f09b09a7547efffe43648456768438ba293a
parentb5d29991cc4722aec39c346c3f82291581e92aa0 (diff)
datapath: Add support for namespace.
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
-rw-r--r--datapath/Modules.mk1
-rw-r--r--datapath/brcompat.c55
-rw-r--r--datapath/compat.h8
-rw-r--r--datapath/datapath.c244
-rw-r--r--datapath/datapath.h37
-rw-r--r--datapath/dp_notify.c9
-rw-r--r--datapath/dp_sysfs_dp.c13
-rw-r--r--datapath/dp_sysfs_if.c10
-rw-r--r--datapath/genl_exec.c2
-rw-r--r--datapath/linux/Modules.mk3
-rw-r--r--datapath/linux/compat/include/net/genetlink.h4
-rw-r--r--datapath/linux/compat/include/net/net_namespace.h84
-rw-r--r--datapath/linux/compat/include/net/netns/generic.h12
-rw-r--r--datapath/linux/compat/include/net/sock.h15
-rw-r--r--datapath/linux/compat/net_namespace.c108
-rw-r--r--datapath/tunnel.c52
-rw-r--r--datapath/tunnel.h22
-rw-r--r--datapath/vport-capwap.c124
-rw-r--r--datapath/vport-capwap.h32
-rw-r--r--datapath/vport-gre.c14
-rw-r--r--datapath/vport-internal_dev.c9
-rw-r--r--datapath/vport-netdev.c2
-rw-r--r--datapath/vport-patch.c35
-rw-r--r--datapath/vport.c24
-rw-r--r--datapath/vport.h11
25 files changed, 689 insertions, 241 deletions
diff --git a/datapath/Modules.mk b/datapath/Modules.mk
index 4d17568e..96e7f7df 100644
--- a/datapath/Modules.mk
+++ b/datapath/Modules.mk
@@ -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
diff --git a/datapath/brcompat.c b/datapath/brcompat.c
index 339b5dcf..d4a0acad 100644
--- a/datapath/brcompat.c
+++ b/datapath/brcompat.c
@@ -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;
diff --git a/datapath/compat.h b/datapath/compat.h
index 36d00258..fd757f1a 100644
--- a/datapath/compat.h
+++ b/datapath/compat.h
@@ -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 */
diff --git a/datapath/datapath.c b/datapath/datapath.c
index e9a4e189..220c7dd4 100644
--- a/datapath/datapath.c
+++ b/datapath/datapath.c
@@ -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();
diff --git a/datapath/datapath.h b/datapath/datapath.h
index 7f3946e6..b012a766 100644
--- a/datapath/datapath.h
+++ b/datapath/datapath.h
@@ -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
@@ -28,11 +28,11 @@
#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);
diff --git a/datapath/dp_notify.c b/datapath/dp_notify.c
index d040d46e..48023a9d 100644
--- a/datapath/dp_notify.c
+++ b/datapath/dp_notify.c
@@ -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;
diff --git a/datapath/dp_sysfs_dp.c b/datapath/dp_sysfs_dp.c
index 1574a93f..2582321c 100644
--- a/datapath/dp_sysfs_dp.c
+++ b/datapath/dp_sysfs_dp.c
@@ -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);
diff --git a/datapath/dp_sysfs_if.c b/datapath/dp_sysfs_if.c
index 5b695cfc..f564e980 100644
--- a/datapath/dp_sysfs_if.c
+++ b/datapath/dp_sysfs_if.c
@@ -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)
diff --git a/datapath/genl_exec.c b/datapath/genl_exec.c
index e5795294..66c7f942 100644
--- a/datapath/genl_exec.c
+++ b/datapath/genl_exec.c
@@ -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) {
diff --git a/datapath/linux/Modules.mk b/datapath/linux/Modules.mk
index 40c3927a..7f54bde4 100644
--- a/datapath/linux/Modules.mk
+++ b/datapath/linux/Modules.mk
@@ -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
diff --git a/datapath/linux/compat/include/net/genetlink.h b/datapath/linux/compat/include/net/genetlink.h
index a1ff7c1c..af7d5fd4 100644
--- a/datapath/linux/compat/include/net/genetlink.h
+++ b/datapath/linux/compat/include/net/genetlink.h
@@ -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)
diff --git a/datapath/linux/compat/include/net/net_namespace.h b/datapath/linux/compat/include/net/net_namespace.h
index 9ce9fcd2..77f0a162 100644
--- a/datapath/linux/compat/include/net/net_namespace.h
+++ b/datapath/linux/compat/include/net/net_namespace.h
@@ -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
index 00000000..1a0303f7
--- /dev/null
+++ b/datapath/linux/compat/include/net/netns/generic.h
@@ -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
index 00000000..513489ae
--- /dev/null
+++ b/datapath/linux/compat/include/net/sock.h
@@ -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
index 00000000..4e8a8910
--- /dev/null
+++ b/datapath/linux/compat/net_namespace.c
@@ -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
diff --git a/datapath/tunnel.c b/datapath/tunnel.c
index 33d2fe93..cdbf94ab 100644
--- a/datapath/tunnel.c
+++ b/datapath/tunnel.c
@@ -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);
}
diff --git a/datapath/tunnel.h b/datapath/tunnel.h
index 6865ae61..33eb63c3 100644
--- a/datapath/tunnel.h
+++ b/datapath/tunnel.h
@@ -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"
@@ -58,12 +60,16 @@
/**
* 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,
diff --git a/datapath/vport-capwap.c b/datapath/vport-capwap.c
index 6c1b0da9..8fbd4a7b 100644
--- a/datapath/vport-capwap.c
+++ b/datapath/vport-capwap.c
@@ -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
index 00000000..b33cc94c
--- /dev/null
+++ b/datapath/vport-capwap.h
@@ -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 */
diff --git a/datapath/vport-gre.c b/datapath/vport-gre.c
index eb100091..3bb55f06 100644
--- a/datapath/vport-gre.c
+++ b/datapath/vport-gre.c
@@ -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)
diff --git a/datapath/vport-internal_dev.c b/datapath/vport-internal_dev.c
index c56f3b29..46b78d22 100644
--- a/datapath/vport-internal_dev.c
+++ b/datapath/vport-internal_dev.c
@@ -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;
diff --git a/datapath/vport-netdev.c b/datapath/vport-netdev.c
index 4cca7fe8..12a34942 100644
--- a/datapath/vport-netdev.c
+++ b/datapath/vport-netdev.c
@@ -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;
diff --git a/datapath/vport-patch.c b/datapath/vport-patch.c
index 53b24b0f..0056792b 100644
--- a/datapath/vport-patch.c
+++ b/datapath/vport-patch.c
@@ -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,10 +16,11 @@
* 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);
}
}
diff --git a/datapath/vport.c b/datapath/vport.c
index e9ccdbde..d81f6869 100644
--- a/datapath/vport.c
+++ b/datapath/vport.c
@@ -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,10 +16,10 @@
* 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;
}
}
diff --git a/datapath/vport.h b/datapath/vport.h
index 44cf6033..ee9715d7 100644
--- a/datapath/vport.h
+++ b/datapath/vport.h
@@ -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,16 +20,21 @@
#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 *);