X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=datapath%2Fbrcompat.c;h=d4a0acad926868011c81a25ad3333b90d9c4a4ef;hb=c1ce8fbffb04e006a1698139132439ef0d7e57b2;hp=44b2c9923e4f8dd1a313a07060abd1ed4f5bc751;hpb=f2d9c30bdf455d8ce6dbf2188c1330e4b33d7d1f;p=openvswitch diff --git a/datapath/brcompat.c b/datapath/brcompat.c index 44b2c992..d4a0acad 100644 --- a/datapath/brcompat.c +++ b/datapath/brcompat.c @@ -1,13 +1,26 @@ /* - * Copyright (c) 2009 Nicira Networks. - * Distributed under the terms of the GNU GPL version 2. + * Copyright (c) 2007-2012 Nicira Networks. * - * Significant portions of this file may be copied from parts of the Linux - * kernel, by Linus Torvalds and others. + * 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 */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include #include -#include +#include #include #include #include @@ -15,9 +28,7 @@ #include #include -#include "compat.h" #include "openvswitch/brcompat-netlink.h" -#include "brc_procfs.h" #include "datapath.h" static struct genl_family brc_genl_family; @@ -39,8 +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 *, struct nlattr **attrs); -static int brc_send_simple_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 net *, struct sk_buff *); static struct sk_buff *brc_make_request(int op, const char *bridge, const char *port) @@ -62,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); @@ -77,11 +90,14 @@ 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]; + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (copy_from_user(name, uname, IFNAMSIZ)) return -EFAULT; @@ -91,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]; @@ -112,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; @@ -140,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]; @@ -155,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; @@ -170,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; @@ -194,7 +213,10 @@ static int brc_add_del_port(struct net_device *dev, int port_ifindex, int add) struct net_device *port; int err; - port = __dev_get_by_index(&init_net, port_ifindex); + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + port = __dev_get_by_index(dev_net(dev), port_ifindex); if (!port) return -EINVAL; @@ -206,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; @@ -216,14 +238,13 @@ static int brc_get_bridge_info(struct net_device *dev, struct __bridge_info __user *ub) { struct __bridge_info b; - u64 id = 0; - int i; memset(&b, 0, sizeof(struct __bridge_info)); - for (i=0; idev_addr[i] << (8*(ETH_ALEN-1 - i)); - b.bridge_id = cpu_to_be64(id); + /* First two bytes are the priority, which we should skip. This comes + * from struct bridge_id in br_private.h, which is unavailable to us. + */ + memcpy((u8 *)&b.bridge_id + 2, dev->dev_addr, ETH_ALEN); b.stp_enabled = 0; if (copy_to_user(ub, &b, sizeof(struct __bridge_info))) @@ -238,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(); @@ -252,7 +273,7 @@ static int brc_get_port_list(struct net_device *dev, int __user *uindices, * (limited to a page for sanity) * offset -- number of records to skip */ -static int brc_get_fdb_entries(struct net_device *dev, void __user *userbuf, +static int brc_get_fdb_entries(struct net_device *dev, void __user *userbuf, unsigned long maxnum, unsigned long offset) { struct nlattr *attrs[BRC_GENL_A_MAX + 1]; @@ -271,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; @@ -337,18 +358,18 @@ static int brc_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) int err; switch (cmd) { - case SIOCDEVPRIVATE: - err = old_dev_ioctl(dev, rq, cmd); - break; - - case SIOCBRADDIF: - return brc_add_del_port(dev, rq->ifr_ifindex, 1); - case SIOCBRDELIF: - return brc_add_del_port(dev, rq->ifr_ifindex, 0); - - default: - err = -EOPNOTSUPP; - break; + case SIOCDEVPRIVATE: + err = old_dev_ioctl(dev, rq, cmd); + break; + + case SIOCBRADDIF: + return brc_add_del_port(dev, rq->ifr_ifindex, 1); + case SIOCBRDELIF: + return brc_add_del_port(dev, rq->ifr_ifindex, 0); + + default: + err = -EOPNOTSUPP; + break; } return err; @@ -361,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) @@ -370,7 +392,7 @@ static int brc_genl_query(struct sk_buff *skb, struct genl_info *info) void *data; ans_skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!ans_skb) + if (!ans_skb) return -ENOMEM; data = genlmsg_put_reply(ans_skb, info, &brc_genl_family, @@ -390,22 +412,9 @@ nla_put_failure: return err; } -static struct genl_ops brc_genl_ops_query_dp = { - .cmd = BRC_GENL_C_QUERY_MC, - .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privelege. */ - .policy = NULL, - .doit = brc_genl_query, - .dumpit = NULL -}; - /* Attribute policy: what each attribute may contain. */ static struct nla_policy brc_genl_policy[BRC_GENL_A_MAX + 1] = { [BRC_GENL_A_ERR_CODE] = { .type = NLA_U32 }, - - [BRC_GENL_A_PROC_DIR] = { .type = NLA_NUL_STRING }, - [BRC_GENL_A_PROC_NAME] = { .type = NLA_NUL_STRING }, - [BRC_GENL_A_PROC_DATA] = { .type = NLA_NUL_STRING }, - [BRC_GENL_A_FDB_DATA] = { .type = NLA_UNSPEC }, }; @@ -439,23 +448,21 @@ static int brc_genl_dp_result(struct sk_buff *skb, struct genl_info *info) return err; } -static struct genl_ops brc_genl_ops_dp_result = { - .cmd = BRC_GENL_C_DP_RESULT, - .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privelege. */ - .policy = brc_genl_policy, - .doit = brc_genl_dp_result, - .dumpit = NULL +static struct genl_ops brc_genl_ops[] = { + { .cmd = BRC_GENL_C_QUERY_MC, + .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privelege. */ + .policy = NULL, + .doit = brc_genl_query, + }, + { .cmd = BRC_GENL_C_DP_RESULT, + .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privelege. */ + .policy = brc_genl_policy, + .doit = brc_genl_dp_result, + }, }; -static struct genl_ops brc_genl_ops_set_proc = { - .cmd = BRC_GENL_C_SET_PROC, - .flags = GENL_ADMIN_PERM, /* Requires CAP_NET_ADMIN privelege. */ - .policy = brc_genl_policy, - .doit = brc_genl_set_proc, - .dumpit = NULL -}; - -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; @@ -474,16 +481,17 @@ 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; /* Wait for reply. */ error = -ETIMEDOUT; if (!wait_for_completion_timeout(&brc_done, BRC_TIMEOUT)) { - printk(KERN_WARNING "brcompat: timed out waiting for userspace\n"); + pr_warn("timed out waiting for userspace\n"); goto error; - } + } /* Grab reply. */ spin_lock_irqsave(&brc_lock, flags); @@ -510,13 +518,13 @@ static int __init brc_init(void) { int err; - printk("Open vSwitch Bridge Compatibility, built "__DATE__" "__TIME__"\n"); + pr_info("Open vSwitch Bridge Compatibility, built "__DATE__" "__TIME__"\n"); /* Set the bridge ioctl handler */ brioctl_set(brc_ioctl_deviceless_stub); /* Set the openvswitch_mod device ioctl handler */ - dp_ioctl_hook = brc_dev_ioctl; + ovs_dp_ioctl_hook = brc_dev_ioctl; /* Randomize the initial sequence number. This is not a security * feature; it only helps avoid crossed wires between userspace and @@ -525,22 +533,11 @@ static int __init brc_init(void) /* Register generic netlink family to communicate changes to * userspace. */ - err = genl_register_family(&brc_genl_family); + err = genl_register_family_with_ops(&brc_genl_family, + brc_genl_ops, ARRAY_SIZE(brc_genl_ops)); if (err) goto error; - err = genl_register_ops(&brc_genl_family, &brc_genl_ops_query_dp); - if (err != 0) - goto err_unregister; - - err = genl_register_ops(&brc_genl_family, &brc_genl_ops_dp_result); - if (err != 0) - goto err_unregister; - - err = genl_register_ops(&brc_genl_family, &brc_genl_ops_set_proc); - if (err != 0) - goto err_unregister; - strcpy(brc_mc_group.name, "brcompat"); err = genl_register_mc_group(&brc_genl_family, &brc_mc_group); if (err < 0) @@ -551,18 +548,17 @@ static int __init brc_init(void) err_unregister: genl_unregister_family(&brc_genl_family); error: - printk(KERN_EMERG "brcompat: failed to install!"); + pr_emerg("failed to install!\n"); return err; } static void brc_cleanup(void) { /* Unregister ioctl hooks */ - dp_ioctl_hook = NULL; + ovs_dp_ioctl_hook = NULL; brioctl_set(NULL); genl_unregister_family(&brc_genl_family); - brc_procfs_exit(); } module_init(brc_init); @@ -571,3 +567,12 @@ module_exit(brc_cleanup); MODULE_DESCRIPTION("Open vSwitch bridge compatibility"); MODULE_AUTHOR("Nicira Networks"); MODULE_LICENSE("GPL"); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) +/* + * In kernels 2.6.36 and later, Open vSwitch can safely coexist with + * the Linux bridge module, but it does not make sense to load both bridge and + * brcompat_mod, so this prevents it. + */ +BRIDGE_MUTUAL_EXCLUSION; +#endif