From 2a4999f3f33467f4fa22ed6e5b06350615fb2dac Mon Sep 17 00:00:00 2001 From: Pravin B Shelar Date: Mon, 30 Jan 2012 06:56:54 -0800 Subject: [PATCH] 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 Acked-by: Jesse Gross Bug #7821 --- datapath/Modules.mk | 1 + datapath/brcompat.c | 55 ++-- datapath/compat.h | 8 + datapath/datapath.c | 244 +++++++++++------- datapath/datapath.h | 37 ++- datapath/dp_notify.c | 9 +- datapath/dp_sysfs_dp.c | 13 +- datapath/dp_sysfs_if.c | 10 +- datapath/genl_exec.c | 2 + datapath/linux/Modules.mk | 3 + datapath/linux/compat/include/net/genetlink.h | 4 + .../linux/compat/include/net/net_namespace.h | 84 +++++- .../linux/compat/include/net/netns/generic.h | 12 + datapath/linux/compat/include/net/sock.h | 15 ++ datapath/linux/compat/net_namespace.c | 108 ++++++++ datapath/tunnel.c | 52 ++-- datapath/tunnel.h | 22 +- datapath/vport-capwap.c | 124 ++++++--- datapath/vport-capwap.h | 32 +++ datapath/vport-gre.c | 14 +- datapath/vport-internal_dev.c | 9 +- datapath/vport-netdev.c | 2 +- datapath/vport-patch.c | 35 +-- datapath/vport.c | 24 +- datapath/vport.h | 11 +- 25 files changed, 689 insertions(+), 241 deletions(-) create mode 100644 datapath/linux/compat/include/net/netns/generic.h create mode 100644 datapath/linux/compat/include/net/sock.h create mode 100644 datapath/linux/compat/net_namespace.c create mode 100644 datapath/vport-capwap.h diff --git a/datapath/Modules.mk b/datapath/Modules.mk index 4d17568ed..96e7f7dfa 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 339b5dcf5..d4a0acad9 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 36d00258b..fd757f1a5 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 e9a4e1894..220c7dd42 100644 --- a/datapath/datapath.c +++ b/datapath/datapath.c @@ -50,6 +50,8 @@ #include #include #include +#include +#include #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 7f3946e63..b012a766d 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 d040d46e7..48023a9d8 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 1574a93f5..2582321c6 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//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 5b695cfc0..f564e9808 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 e57952945..66c7f9429 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 40c3927ac..7f54bde43 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 a1ff7c1c3..af7d5fd49 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 9ce9fcd20..77f0a1626 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) /* exists, go ahead and include it. */ #include_next -#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 000000000..1a0303f74 --- /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) +/* exists, go ahead and include it. */ +#include_next +#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 000000000..513489aeb --- /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 +#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 000000000..4e8a8910c --- /dev/null +++ b/datapath/linux/compat/net_namespace.c @@ -0,0 +1,108 @@ +#include +#include +#include +#include + +#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 33d2fe934..cdbf94ab9 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 #include #include @@ -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 6865ae611..33eb63c35 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 +#include +#include #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 6c1b0da9a..8fbd4a7b5 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 #include #include +#include #include #include @@ -23,6 +24,7 @@ #include #include +#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 000000000..b33cc94c3 --- /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 + +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 eb1000910..3bb55f061 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 #include +#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 c56f3b295..46b78d226 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 4cca7fe86..12a34942e 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 53b24b0f1..0056792bc 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 #include +#include #include #include +#include #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 e9ccdbde3..d81f6869c 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 #include #include #include +#include #include #include #include @@ -28,7 +28,9 @@ #include #include #include +#include +#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 44cf60333..ee9715d78 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 +#include #include #include #include #include -#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 *); -- 2.43.0