1 /* Copyright (c) 2009, 2010, 2011 Nicira Networks
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
17 #include "proc-net-compat.h"
25 #include "dynamic-string.h"
27 #include "netlink-protocol.h"
28 #include "netlink-socket.h"
31 #include "openvswitch/brcompat-netlink.h"
37 VLOG_DEFINE_THIS_MODULE(proc_net_compat);
39 /* Netlink socket to bridge compatibility kernel module. */
40 static struct nl_sock *brc_sock;
42 /* The Generic Netlink family number used for bridge compatibility. */
43 static int brc_family = 0;
45 /* Rate limiting for log messages. */
46 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
48 static void flush_dir(const char *dir);
49 static int set_proc_file(const char *dir, const char *file, const char *data);
51 /* Initializes the /proc/net compatibility layer. Returns 0 if successful,
52 * otherwise a positive errno value. */
54 proc_net_compat_init(void)
57 int retval = nl_lookup_genl_family(BRC_GENL_FAMILY_NAME, &brc_family);
62 retval = nl_sock_create(NETLINK_GENERIC, &brc_sock);
67 flush_dir("/proc/net/vlan");
68 flush_dir("/proc/net/bonding");
74 set_proc_file(const char *dir, const char *file, const char *data)
76 struct ofpbuf request;
79 ofpbuf_init(&request, 0);
80 nl_msg_put_genlmsghdr(&request, 1024, brc_family, NLM_F_REQUEST,
81 BRC_GENL_C_SET_PROC, 1);
82 nl_msg_put_string(&request, BRC_GENL_A_PROC_DIR, dir);
83 nl_msg_put_string(&request, BRC_GENL_A_PROC_NAME, file);
85 nl_msg_put_string(&request, BRC_GENL_A_PROC_DATA, data);
88 retval = nl_sock_transact(brc_sock, &request, NULL);
89 ofpbuf_uninit(&request);
91 VLOG_WARN_RL(&rl, "failed to %s /proc/%s/%s (%s)",
92 data ? "update" : "remove", dir, file, strerror(retval));
98 flush_dir(const char *dir)
104 assert(!memcmp(dir, "/proc/", 6));
107 stream = opendir(dir);
109 if (errno != ENOENT) {
110 VLOG_WARN_RL(&rl, "%s: open failed (%s)", dir, strerror(errno));
115 while ((de = readdir(stream)) != NULL) {
116 if (strcmp(de->d_name, ".") && strcmp(de->d_name, "..")) {
117 set_proc_file(subdir, de->d_name, NULL);
123 /* If 'bond' is nonnull, creates a file in /proc/net/bonding for a bond with
124 * the given 'name' and the details in 'bond'. If 'bond' is null, deletes
125 * the /proc/net/bonding file with the given 'name'.
127 * This function has no effect unless proc_net_compat_init() has been
130 proc_net_compat_update_bond(const char *name, const struct compat_bond *bond)
140 set_proc_file("net/bonding", name, NULL);
147 "Ethernet Channel Bonding Driver: ovs-vswitchd "
148 VERSION BUILDNR" ("__DATE__" "__TIME__")\n"
149 "Bonding Mode: source load balancing\n"
150 "Primary Slave: None\n"
151 "Currently Active Slave: None\n"
153 "MII Polling Interval (ms): 100\n"
154 "Up Delay (ms): %d\n"
155 "Down Delay (ms): %d\n"
157 "Source load balancing info:\n",
158 bond->up ? "up" : "down", bond->updelay, bond->downdelay);
160 for (i = 0; i < bond->n_hashes; i++) {
161 const struct compat_bond_hash *cbh = &bond->hashes[i];
162 ds_put_format(&ds, " [%03d] = %s\n", cbh->hash, cbh->netdev_name);
165 for (i = 0; i < bond->n_slaves; i++) {
166 const struct compat_bond_slave *slave = &bond->slaves[i];
170 "Slave Interface: %s\n"
172 "Link Failure Count: 0\n"
173 "Permanent HW addr: "ETH_ADDR_FMT"\n",
174 slave->name, slave->up ? "up" : "down",
175 ETH_ADDR_ARGS(slave->mac));
177 set_proc_file("net/bonding", name, ds_cstr(&ds));
181 /* /proc/net/vlan compatibility.
183 * This is much more complex than I expected it to be. */
187 struct hmap_node trunk_node; /* Hash map node. */
188 char *trunk_dev; /* Name of trunk network device. */
189 int vid; /* VLAN number. */
191 /* Auxiliary data. */
192 char *vlan_dev; /* sprintf("%s.%d", trunk_dev, vid); */
193 struct svec tagged_devs; /* Name of tagged network device(s). */
196 /* Current set of VLAN devices, indexed two different ways. */
197 static struct hmap vlans_by_trunk = HMAP_INITIALIZER(&vlans_by_trunk);
198 static struct shash vlans_by_tagged = SHASH_INITIALIZER(&vlans_by_tagged);
200 static bool remove_tagged_dev(struct shash_node *, const char *tagged_dev);
201 static void update_vlan_config(void);
202 static void set_vlan_proc_file(const struct compat_vlan *);
203 static uint32_t hash_vlan(const char *trunk_dev, uint32_t vid);
205 /* Updates the /proc/net/vlan compatibility layer's idea of what trunk device
206 * and VLAN the given 'tagged_dev' is associated with. If 'tagged_dev' has an
207 * implicit VLAN tag, then 'trunk_dev' should be the name of a network device
208 * on the same bridge that trunks that VLAN, and 'vid' should be the VLAN tag
209 * number. If 'tagged_dev' does not have an implicit VLAN tag, then
210 * 'trunk_dev' should be NULL and 'vid' should be -1.
212 * This function has no effect unless proc_net_compat_init() has been
215 proc_net_compat_update_vlan(const char *tagged_dev, const char *trunk_dev,
218 struct compat_vlan *vlan;
219 struct shash_node *node;
225 /* Find the compat_vlan that we currently have for 'tagged_dev' (if
227 node = shash_find(&vlans_by_tagged, tagged_dev);
228 vlan = node ? node->data : NULL;
229 if (vid <= 0 || !trunk_dev) {
231 if (remove_tagged_dev(node, tagged_dev)) {
232 update_vlan_config();
237 if (!strcmp(trunk_dev, vlan->trunk_dev) && vid == vlan->vid) {
241 /* 'tagged_dev' is attached to the wrong compat_vlan. Start
242 * by removing it from that one. */
243 remove_tagged_dev(node, tagged_dev);
249 /* 'tagged_dev' is not attached to any compat_vlan. Find the
250 * compat_vlan corresponding to (trunk_dev,vid) to attach it to, or
251 * create a new compat_vlan if none exists for (trunk_dev,vid). */
252 HMAP_FOR_EACH_WITH_HASH (vlan, trunk_node, hash_vlan(trunk_dev, vid),
254 if (!strcmp(trunk_dev, vlan->trunk_dev) && vid == vlan->vid) {
259 /* Create a new compat_vlan for (trunk_dev,vid). */
260 vlan = xzalloc(sizeof *vlan);
261 vlan->trunk_dev = xstrdup(trunk_dev);
263 vlan->vlan_dev = xasprintf("%s.%d", trunk_dev, vid);
264 svec_init(&vlan->tagged_devs);
265 hmap_insert(&vlans_by_trunk, &vlan->trunk_node,
266 hash_vlan(trunk_dev, vid));
267 set_vlan_proc_file(vlan);
270 /* Attach 'tagged_dev' to 'vlan'. */
271 svec_add(&vlan->tagged_devs, tagged_dev);
272 shash_add(&vlans_by_tagged, tagged_dev, vlan);
273 svec_sort(&vlan->tagged_devs);
274 update_vlan_config();
278 /* Remove 'tagged_dev' from the compat_vlan in 'node'. If that causes the
279 * compat_vlan to have no tagged_devs left, destroy the compat_vlan too. */
281 remove_tagged_dev(struct shash_node *node, const char *tagged_dev)
283 struct compat_vlan *vlan = node->data;
285 svec_del(&vlan->tagged_devs, tagged_dev);
286 shash_delete(&vlans_by_tagged, node);
287 if (!vlan->tagged_devs.n) {
288 set_proc_file("net/vlan", vlan->vlan_dev, NULL);
290 hmap_remove(&vlans_by_trunk, &vlan->trunk_node);
291 svec_destroy(&vlan->tagged_devs);
292 free(vlan->trunk_dev);
293 free(vlan->vlan_dev);
300 /* Returns a hash value for (trunk_dev,vid). */
302 hash_vlan(const char *trunk_dev, uint32_t vid)
304 return hash_int(vid, hash_string(trunk_dev, 0));
307 /* Update /proc/net/vlan/<vlan_dev> for 'vlan'. */
309 set_vlan_proc_file(const struct compat_vlan *vlan)
316 "%s VID: %d\t REORDER_HDR: 1 dev->priv_flags: 81\n"
317 " total frames received 0\n"
318 " total bytes received 0\n"
319 " Broadcast/Multicast Rcvd 0\n"
321 " total frames transmitted 0\n"
322 " total bytes transmitted 0\n"
323 " total headroom inc 0\n"
324 " total encap on xmit 0\n"
326 "INGRESS priority mappings: 0:0 1:0 2:0 3:0 4:0 5:0 6:0 7:0\n"
327 "EGRESSS priority Mappings: \n",
328 vlan->vlan_dev, vlan->vid, vlan->trunk_dev);
329 set_proc_file("net/vlan", vlan->vlan_dev, ds_cstr(&ds));
333 /* Update /proc/net/vlan/config. */
335 update_vlan_config(void)
337 struct compat_vlan *vlan;
341 ds_put_cstr(&ds, "VLAN Dev name | VLAN ID\n"
342 "Name-Type: VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD\n");
343 HMAP_FOR_EACH (vlan, trunk_node, &vlans_by_trunk) {
344 ds_put_format(&ds, "%-15s| %d | %s\n",
345 vlan->vlan_dev, vlan->vid, vlan->trunk_dev);
347 set_proc_file("net/vlan", "config", ds_cstr(&ds));
350 #else /* !HAVE_NETLINK */
351 #include "compiler.h"
354 proc_net_compat_init(void)
360 proc_net_compat_update_bond(const char *name OVS_UNUSED,
361 const struct compat_bond *bond OVS_UNUSED)
366 proc_net_compat_update_vlan(const char *tagged_dev OVS_UNUSED,
367 const char *trunk_dev OVS_UNUSED,
371 #endif /* !HAVE_NETLINK */