mirror of
https://github.com/openvswitch/ovs
synced 2025-10-15 14:17:18 +00:00
Signed-off-by: Joe Perches <joe@perches.com> Acked-by: Simon Horman <horms@verge.net.au> [Jesse: Added missing pr_fmt in vport-gre.c and dp_sysfs_dp.c] Signed-off-by: Jesse Gross <jesse@nicira.com>
197 lines
5.2 KiB
C
197 lines
5.2 KiB
C
/*
|
|
* Copyright (c) 2009, 2010 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
|
|
* kernel, by Linus Torvalds and others.
|
|
*/
|
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/netdevice.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/seq_file.h>
|
|
#include <net/genetlink.h>
|
|
#include "brc_procfs.h"
|
|
#include "openvswitch/brcompat-netlink.h"
|
|
|
|
/* This code implements a Generic Netlink command BRC_GENL_C_SET_PROC that can
|
|
* be used to add, modify, and delete arbitrary files in selected
|
|
* subdirectories of /proc. It's a horrible kluge prompted by the need to
|
|
* simulate certain /proc/net/vlan and /proc/net/bonding files for software
|
|
* that wants to read them, and with any luck it will go away eventually.
|
|
*
|
|
* The implementation is a kluge too. In particular, we want to release the
|
|
* strings copied into the 'data' members of proc_dir_entry when the
|
|
* proc_dir_entry structures are freed, but there doesn't appear to be a way to
|
|
* hook that, so instead we have to rely on being the only entity modifying the
|
|
* directories in question.
|
|
*/
|
|
|
|
static int brc_seq_show(struct seq_file *seq, void *unused)
|
|
{
|
|
seq_puts(seq, seq->private);
|
|
return 0;
|
|
}
|
|
|
|
static int brc_seq_open(struct inode *inode, struct file *file)
|
|
{
|
|
return single_open(file, brc_seq_show, PDE(inode)->data);
|
|
}
|
|
|
|
static struct file_operations brc_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = brc_seq_open,
|
|
.read = seq_read,
|
|
.llseek = seq_lseek,
|
|
.release = single_release,
|
|
};
|
|
|
|
static struct proc_dir_entry *proc_vlan_dir;
|
|
static struct proc_dir_entry *proc_bonding_dir;
|
|
|
|
static struct proc_dir_entry *brc_lookup_entry(struct proc_dir_entry *de, const char *name)
|
|
{
|
|
int namelen = strlen(name);
|
|
for (de = de->subdir; de; de = de->next) {
|
|
if (de->namelen != namelen)
|
|
continue;
|
|
if (!memcmp(name, de->name, de->namelen))
|
|
return de;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static struct proc_dir_entry *brc_open_dir(const char *dir_name,
|
|
struct proc_dir_entry *parent,
|
|
struct proc_dir_entry **dirp)
|
|
{
|
|
if (!*dirp) {
|
|
struct proc_dir_entry *dir;
|
|
if (brc_lookup_entry(parent, dir_name)) {
|
|
pr_warn("%s proc directory exists, can't simulate--"
|
|
"probably its real module is loaded\n",
|
|
dir_name);
|
|
return NULL;
|
|
}
|
|
dir = *dirp = proc_mkdir(dir_name, parent);
|
|
}
|
|
return *dirp;
|
|
}
|
|
|
|
/* Maximum length of the BRC_GENL_A_PROC_DIR and BRC_GENL_A_PROC_NAME strings.
|
|
* If we could depend on supporting NLA_NUL_STRING and the .len member in
|
|
* Generic Netlink policy, then we could just put this in brc_genl_policy (and
|
|
* simplify brc_genl_set_proc() below too), but upstream 2.6.18 does not have
|
|
* either. */
|
|
#define BRC_NAME_LEN_MAX 32
|
|
|
|
int brc_genl_set_proc(struct sk_buff *skb, struct genl_info *info)
|
|
{
|
|
struct proc_dir_entry *dir, *entry;
|
|
const char *dir_name, *name;
|
|
char *data;
|
|
|
|
if (!info->attrs[BRC_GENL_A_PROC_DIR] ||
|
|
VERIFY_NUL_STRING(info->attrs[BRC_GENL_A_PROC_DIR]) ||
|
|
!info->attrs[BRC_GENL_A_PROC_NAME] ||
|
|
VERIFY_NUL_STRING(info->attrs[BRC_GENL_A_PROC_NAME]) ||
|
|
(info->attrs[BRC_GENL_A_PROC_DATA] &&
|
|
VERIFY_NUL_STRING(info->attrs[BRC_GENL_A_PROC_DATA])))
|
|
return -EINVAL;
|
|
|
|
dir_name = nla_data(info->attrs[BRC_GENL_A_PROC_DIR]);
|
|
name = nla_data(info->attrs[BRC_GENL_A_PROC_NAME]);
|
|
if (strlen(dir_name) > BRC_NAME_LEN_MAX ||
|
|
strlen(name) > BRC_NAME_LEN_MAX)
|
|
return -EINVAL;
|
|
|
|
if (!strcmp(dir_name, "net/vlan"))
|
|
dir = brc_open_dir("vlan", proc_net, &proc_vlan_dir);
|
|
else if (!strcmp(dir_name, "net/bonding"))
|
|
dir = brc_open_dir("bonding", proc_net, &proc_bonding_dir);
|
|
else
|
|
return -EINVAL;
|
|
if (!dir) {
|
|
/* Probably failed because the module that really implements
|
|
* the function in question is loaded and already owns the
|
|
* directory in question.*/
|
|
return -EBUSY;
|
|
}
|
|
|
|
entry = brc_lookup_entry(dir, name);
|
|
if (!info->attrs[BRC_GENL_A_PROC_DATA]) {
|
|
if (!entry)
|
|
return -ENOENT;
|
|
|
|
data = entry->data;
|
|
remove_proc_entry(name, dir);
|
|
if (brc_lookup_entry(dir, name))
|
|
return -EBUSY; /* Shouldn't happen */
|
|
|
|
kfree(data);
|
|
} else {
|
|
data = kstrdup(nla_data(info->attrs[BRC_GENL_A_PROC_DATA]),
|
|
GFP_KERNEL);
|
|
if (!data)
|
|
return -ENOMEM;
|
|
|
|
if (entry) {
|
|
char *old_data = entry->data;
|
|
entry->data = data;
|
|
kfree(old_data);
|
|
return 0;
|
|
}
|
|
|
|
entry = create_proc_entry(name, S_IFREG|S_IRUSR|S_IWUSR, dir);
|
|
if (!entry) {
|
|
kfree(data);
|
|
return -ENOBUFS;
|
|
}
|
|
entry->proc_fops = &brc_fops;
|
|
entry->data = data;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void kill_proc_dir(const char *dir_name,
|
|
struct proc_dir_entry *parent,
|
|
struct proc_dir_entry *dir)
|
|
{
|
|
if (!dir)
|
|
return;
|
|
for (;;) {
|
|
struct proc_dir_entry *e;
|
|
char *data;
|
|
char name[BRC_NAME_LEN_MAX + 1];
|
|
|
|
e = dir->subdir;
|
|
if (!e)
|
|
break;
|
|
|
|
if (e->namelen >= sizeof name) {
|
|
/* Can't happen: we prevent adding names this long by
|
|
* limiting the BRC_GENL_A_PROC_NAME string to
|
|
* BRC_NAME_LEN_MAX bytes. */
|
|
WARN_ON(1);
|
|
break;
|
|
}
|
|
strcpy(name, e->name);
|
|
|
|
data = e->data;
|
|
e->data = NULL;
|
|
kfree(data);
|
|
|
|
remove_proc_entry(name, dir);
|
|
}
|
|
remove_proc_entry(dir_name, parent);
|
|
}
|
|
|
|
void brc_procfs_exit(void)
|
|
{
|
|
kill_proc_dir("vlan", proc_net, proc_vlan_dir);
|
|
kill_proc_dir("bonding", proc_net, proc_bonding_dir);
|
|
}
|