From 064af42167bf4fc9aaea2702d80ce08074b889c0 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Wed, 8 Jul 2009 13:19:16 -0700 Subject: Import from old repository commit 61ef2b42a9c4ba8e1600f15bb0236765edc2ad45. --- vswitchd/proc-net-compat.c | 344 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 344 insertions(+) create mode 100644 vswitchd/proc-net-compat.c (limited to 'vswitchd/proc-net-compat.c') diff --git a/vswitchd/proc-net-compat.c b/vswitchd/proc-net-compat.c new file mode 100644 index 00000000..3f5cf44a --- /dev/null +++ b/vswitchd/proc-net-compat.c @@ -0,0 +1,344 @@ +/* Copyright (c) 2009 Nicira Networks + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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, see . + * + */ + +#include +#include "proc-net-compat.h" +#include +#include +#include +#include +#include +#include "dynamic-string.h" +#include "hash.h" +#include "netlink-protocol.h" +#include "netlink.h" +#include "ofpbuf.h" +#include "openvswitch/brcompat-netlink.h" +#include "hmap.h" +#include "shash.h" +#include "svec.h" + +#define THIS_MODULE VLM_proc_net_compat +#include "vlog.h" + +/* Netlink socket to bridge compatibility kernel module. */ +static struct nl_sock *brc_sock; + +/* The Generic Netlink family number used for bridge compatibility. */ +static int brc_family = 0; + +/* Rate limiting for log messages. */ +static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5); + +static void flush_dir(const char *dir); +static int set_proc_file(const char *dir, const char *file, const char *data); + +/* Initializes the /proc/net compatibility layer. Returns 0 if successful, + * otherwise a positive errno value. */ +int +proc_net_compat_init(void) +{ + if (!brc_sock) { + int retval = nl_lookup_genl_family(BRC_GENL_FAMILY_NAME, &brc_family); + if (retval) { + return retval; + } + + retval = nl_sock_create(NETLINK_GENERIC, 0, 0, 0, &brc_sock); + if (retval) { + return retval; + } + + flush_dir("/proc/net/vlan"); + flush_dir("/proc/net/bonding"); + } + return 0; +} + +static int +set_proc_file(const char *dir, const char *file, const char *data) +{ + struct ofpbuf request, *reply; + int retval; + + ofpbuf_init(&request, 0); + nl_msg_put_genlmsghdr(&request, brc_sock, 1024, brc_family, NLM_F_REQUEST, + BRC_GENL_C_SET_PROC, 1); + nl_msg_put_string(&request, BRC_GENL_A_PROC_DIR, dir); + nl_msg_put_string(&request, BRC_GENL_A_PROC_NAME, file); + if (data) { + nl_msg_put_string(&request, BRC_GENL_A_PROC_DATA, data); + } + + retval = nl_sock_transact(brc_sock, &request, &reply); + ofpbuf_uninit(&request); + ofpbuf_delete(reply); + if (retval) { + VLOG_WARN_RL(&rl, "failed to %s /proc/%s/%s (%s)", + data ? "update" : "remove", dir, file, strerror(retval)); + } + return retval; +} + +static void +flush_dir(const char *dir) +{ + const char *subdir; + struct dirent *de; + DIR *stream; + + assert(!memcmp(dir, "/proc/", 6)); + subdir = dir + 6; + + stream = opendir(dir); + if (!stream) { + if (errno != ENOENT) { + VLOG_WARN_RL(&rl, "%s: open failed (%s)", dir, strerror(errno)); + } + return; + } + + while ((de = readdir(stream)) != NULL) { + if (strcmp(de->d_name, ".") && strcmp(de->d_name, "..")) { + set_proc_file(subdir, de->d_name, NULL); + } + } + closedir(stream); +} + +/* If 'bond' is nonnull, creates a file in /proc/net/bonding for a bond with + * the given 'name' and the details in 'bond'. If 'bond' is null, deletes + * the /proc/net/bonding file with the given 'name'. + * + * This function has no effect unless proc_net_compat_init() has been + * called. */ +void +proc_net_compat_update_bond(const char *name, const struct compat_bond *bond) +{ + struct ds ds; + int i; + + if (!brc_sock) { + return; + } + + if (!bond) { + set_proc_file("net/bonding", name, NULL); + return; + } + + ds_init(&ds); + ds_put_format( + &ds, + "Ethernet Channel Bonding Driver: ovs-vswitchd " + VERSION BUILDNR" ("__DATE__" "__TIME__")\n" + "Bonding Mode: source load balancing\n" + "Primary Slave: None\n" + "Currently Active Slave: None\n" + "MII Status: %s\n" + "MII Polling Interval (ms): 100\n" + "Up Delay (ms): %d\n" + "Down Delay (ms): %d\n" + "\n" + "Source load balancing info:\n", + bond->up ? "up" : "down", bond->updelay, bond->downdelay); + for (i = 0; i < bond->n_slaves; i++) { + const struct compat_bond_slave *slave = &bond->slaves[i]; + ds_put_format( + &ds, + "\n" + "Slave Interface: %s\n" + "MII Status: %s\n" + "Link Failure Count: 0\n" + "Permanent HW addr: "ETH_ADDR_FMT"\n", + slave->name, slave->up ? "up" : "down", + ETH_ADDR_ARGS(slave->mac)); + } + set_proc_file("net/bonding", name, ds_cstr(&ds)); + ds_destroy(&ds); +} + +/* /proc/net/vlan compatibility. + * + * This is much more complex than I expected it to be. */ + +struct compat_vlan { + /* Hash key. */ + struct hmap_node trunk_node; /* Hash map node. */ + char *trunk_dev; /* Name of trunk network device. */ + int vid; /* VLAN number. */ + + /* Auxiliary data. */ + char *vlan_dev; /* sprintf("%s.%d", trunk_dev, vid); */ + struct svec tagged_devs; /* Name of tagged network device(s). */ +}; + +/* Current set of VLAN devices, indexed two different ways. */ +static struct hmap vlans_by_trunk = HMAP_INITIALIZER(&vlans_by_trunk); +static struct shash vlans_by_tagged = SHASH_INITIALIZER(&vlans_by_tagged); + +static bool remove_tagged_dev(struct shash_node *, const char *tagged_dev); +static void update_vlan_config(void); +static void set_vlan_proc_file(const struct compat_vlan *); +static uint32_t hash_vlan(const char *trunk_dev, uint32_t vid); + +/* Updates the /proc/net/vlan compatibility layer's idea of what trunk device + * and VLAN the given 'tagged_dev' is associated with. If 'tagged_dev' has an + * implicit VLAN tag, then 'trunk_dev' should be the name of a network device + * on the same bridge that trunks that VLAN, and 'vid' should be the VLAN tag + * number. If 'tagged_dev' does not have an implicit VLAN tag, then + * 'trunk_dev' should be NULL and 'vid' should be -1. + * + * This function has no effect unless proc_net_compat_init() has been + * called. */ +void +proc_net_compat_update_vlan(const char *tagged_dev, const char *trunk_dev, + int vid) +{ + struct compat_vlan *vlan; + struct shash_node *node; + + if (!brc_sock) { + return; + } + + /* Find the compat_vlan that we currently have for 'tagged_dev' (if + * any). */ + node = shash_find(&vlans_by_tagged, tagged_dev); + vlan = node ? node->data : NULL; + if (vid <= 0 || !trunk_dev) { + if (vlan) { + if (remove_tagged_dev(node, tagged_dev)) { + update_vlan_config(); + } + } + } else { + if (vlan) { + if (!strcmp(trunk_dev, vlan->trunk_dev) && vid == vlan->vid) { + /* No change. */ + return; + } else { + /* 'tagged_dev' is attached to the wrong compat_vlan. Start + * by removing it from that one. */ + remove_tagged_dev(node, tagged_dev); + node = NULL; + vlan = NULL; + } + } + + /* 'tagged_dev' is not attached to any compat_vlan. Find the + * compat_vlan corresponding to (trunk_dev,vid) to attach it to, or + * create a new compat_vlan if none exists for (trunk_dev,vid). */ + HMAP_FOR_EACH_WITH_HASH (vlan, struct compat_vlan, trunk_node, + hash_vlan(trunk_dev, vid), + &vlans_by_trunk) { + if (!strcmp(trunk_dev, vlan->trunk_dev) && vid == vlan->vid) { + break; + } + } + if (!vlan) { + /* Create a new compat_vlan for (trunk_dev,vid). */ + vlan = xcalloc(1, sizeof *vlan); + vlan->trunk_dev = xstrdup(trunk_dev); + vlan->vid = vid; + vlan->vlan_dev = xasprintf("%s.%d", trunk_dev, vid); + svec_init(&vlan->tagged_devs); + hmap_insert(&vlans_by_trunk, &vlan->trunk_node, + hash_vlan(trunk_dev, vid)); + set_vlan_proc_file(vlan); + } + + /* Attach 'tagged_dev' to 'vlan'. */ + svec_add(&vlan->tagged_devs, tagged_dev); + shash_add(&vlans_by_tagged, tagged_dev, vlan); + svec_sort(&vlan->tagged_devs); + update_vlan_config(); + } +} + +/* Remove 'tagged_dev' from the compat_vlan in 'node'. If that causes the + * compat_vlan to have no tagged_devs left, destroy the compat_vlan too. */ +static bool +remove_tagged_dev(struct shash_node *node, const char *tagged_dev) +{ + struct compat_vlan *vlan = node->data; + + svec_del(&vlan->tagged_devs, tagged_dev); + shash_delete(&vlans_by_tagged, node); + if (!vlan->tagged_devs.n) { + set_proc_file("net/vlan", vlan->vlan_dev, NULL); + + hmap_remove(&vlans_by_trunk, &vlan->trunk_node); + svec_destroy(&vlan->tagged_devs); + free(vlan->trunk_dev); + free(vlan->vlan_dev); + free(vlan); + return true; + } + return false; +} + +/* Returns a hash value for (trunk_dev,vid). */ +static uint32_t +hash_vlan(const char *trunk_dev, uint32_t vid) +{ + return hash_int(vid, hash_string(trunk_dev, 0)); +} + +/* Update /proc/net/vlan/ for 'vlan'. */ +static void +set_vlan_proc_file(const struct compat_vlan *vlan) +{ + struct ds ds; + + ds_init(&ds); + ds_put_format( + &ds, + "%s VID: %d\t REORDER_HDR: 1 dev->priv_flags: 81\n" + " total frames received 0\n" + " total bytes received 0\n" + " Broadcast/Multicast Rcvd 0\n" + "\n" + " total frames transmitted 0\n" + " total bytes transmitted 0\n" + " total headroom inc 0\n" + " total encap on xmit 0\n" + "Device: %s\n" + "INGRESS priority mappings: 0:0 1:0 2:0 3:0 4:0 5:0 6:0 7:0\n" + "EGRESSS priority Mappings: \n", + vlan->vlan_dev, vlan->vid, vlan->trunk_dev); + set_proc_file("net/vlan", vlan->vlan_dev, ds_cstr(&ds)); + ds_destroy(&ds); +} + +/* Update /proc/net/vlan/config. */ +static void +update_vlan_config(void) +{ + struct compat_vlan *vlan; + struct ds ds; + + ds_init(&ds); + ds_put_cstr(&ds, "VLAN Dev name | VLAN ID\n" + "Name-Type: VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD\n"); + HMAP_FOR_EACH (vlan, struct compat_vlan, trunk_node, &vlans_by_trunk) { + ds_put_format(&ds, "%-15s| %d | %s\n", + vlan->vlan_dev, vlan->vid, vlan->trunk_dev); + } + set_proc_file("net/vlan", "config", ds_cstr(&ds)); + ds_destroy(&ds); +} -- cgit v1.2.3