]> git.proxmox.com Git - mirror_ovs.git/blame - vswitchd/proc-net-compat.c
vswitchd: implement bond/hash unixctl
[mirror_ovs.git] / vswitchd / proc-net-compat.c
CommitLineData
064af421
BP
1/* Copyright (c) 2009 Nicira Networks
2 *
a14bc59f
BP
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:
064af421 6 *
a14bc59f 7 * http://www.apache.org/licenses/LICENSE-2.0
064af421 8 *
a14bc59f
BP
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.
064af421
BP
14 */
15
16#include <config.h>
17#include "proc-net-compat.h"
18#include <assert.h>
19#include <dirent.h>
20#include <errno.h>
21#include <inttypes.h>
22#include <string.h>
23#include "dynamic-string.h"
24#include "hash.h"
25#include "netlink-protocol.h"
26#include "netlink.h"
27#include "ofpbuf.h"
28#include "openvswitch/brcompat-netlink.h"
29#include "hmap.h"
30#include "shash.h"
31#include "svec.h"
32
33#define THIS_MODULE VLM_proc_net_compat
34#include "vlog.h"
35
36/* Netlink socket to bridge compatibility kernel module. */
37static struct nl_sock *brc_sock;
38
39/* The Generic Netlink family number used for bridge compatibility. */
40static int brc_family = 0;
41
42/* Rate limiting for log messages. */
43static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
44
45static void flush_dir(const char *dir);
46static int set_proc_file(const char *dir, const char *file, const char *data);
47
48/* Initializes the /proc/net compatibility layer. Returns 0 if successful,
49 * otherwise a positive errno value. */
50int
51proc_net_compat_init(void)
52{
53 if (!brc_sock) {
54 int retval = nl_lookup_genl_family(BRC_GENL_FAMILY_NAME, &brc_family);
55 if (retval) {
56 return retval;
57 }
58
59 retval = nl_sock_create(NETLINK_GENERIC, 0, 0, 0, &brc_sock);
60 if (retval) {
61 return retval;
62 }
63
64 flush_dir("/proc/net/vlan");
65 flush_dir("/proc/net/bonding");
66 }
67 return 0;
68}
69
70static int
71set_proc_file(const char *dir, const char *file, const char *data)
72{
73 struct ofpbuf request, *reply;
74 int retval;
75
76 ofpbuf_init(&request, 0);
77 nl_msg_put_genlmsghdr(&request, brc_sock, 1024, brc_family, NLM_F_REQUEST,
78 BRC_GENL_C_SET_PROC, 1);
79 nl_msg_put_string(&request, BRC_GENL_A_PROC_DIR, dir);
80 nl_msg_put_string(&request, BRC_GENL_A_PROC_NAME, file);
81 if (data) {
82 nl_msg_put_string(&request, BRC_GENL_A_PROC_DATA, data);
83 }
84
85 retval = nl_sock_transact(brc_sock, &request, &reply);
86 ofpbuf_uninit(&request);
87 ofpbuf_delete(reply);
88 if (retval) {
89 VLOG_WARN_RL(&rl, "failed to %s /proc/%s/%s (%s)",
90 data ? "update" : "remove", dir, file, strerror(retval));
91 }
92 return retval;
93}
94
95static void
96flush_dir(const char *dir)
97{
98 const char *subdir;
99 struct dirent *de;
100 DIR *stream;
101
102 assert(!memcmp(dir, "/proc/", 6));
103 subdir = dir + 6;
104
105 stream = opendir(dir);
106 if (!stream) {
107 if (errno != ENOENT) {
108 VLOG_WARN_RL(&rl, "%s: open failed (%s)", dir, strerror(errno));
109 }
110 return;
111 }
112
113 while ((de = readdir(stream)) != NULL) {
114 if (strcmp(de->d_name, ".") && strcmp(de->d_name, "..")) {
115 set_proc_file(subdir, de->d_name, NULL);
116 }
117 }
118 closedir(stream);
119}
120\f
121/* If 'bond' is nonnull, creates a file in /proc/net/bonding for a bond with
122 * the given 'name' and the details in 'bond'. If 'bond' is null, deletes
123 * the /proc/net/bonding file with the given 'name'.
124 *
125 * This function has no effect unless proc_net_compat_init() has been
126 * called. */
127void
128proc_net_compat_update_bond(const char *name, const struct compat_bond *bond)
129{
130 struct ds ds;
131 int i;
132
133 if (!brc_sock) {
134 return;
135 }
136
137 if (!bond) {
138 set_proc_file("net/bonding", name, NULL);
139 return;
140 }
141
142 ds_init(&ds);
143 ds_put_format(
144 &ds,
145 "Ethernet Channel Bonding Driver: ovs-vswitchd "
146 VERSION BUILDNR" ("__DATE__" "__TIME__")\n"
147 "Bonding Mode: source load balancing\n"
148 "Primary Slave: None\n"
149 "Currently Active Slave: None\n"
150 "MII Status: %s\n"
151 "MII Polling Interval (ms): 100\n"
152 "Up Delay (ms): %d\n"
153 "Down Delay (ms): %d\n"
154 "\n"
155 "Source load balancing info:\n",
156 bond->up ? "up" : "down", bond->updelay, bond->downdelay);
2aebae83
BP
157
158 for (i = 0; i < bond->n_hashes; i++) {
159 const struct compat_bond_hash *cbh = &bond->hashes[i];
160 ds_put_format(&ds, " [%03d] = %s\n", cbh->hash, cbh->netdev_name);
161 }
162
064af421
BP
163 for (i = 0; i < bond->n_slaves; i++) {
164 const struct compat_bond_slave *slave = &bond->slaves[i];
165 ds_put_format(
166 &ds,
167 "\n"
168 "Slave Interface: %s\n"
169 "MII Status: %s\n"
170 "Link Failure Count: 0\n"
171 "Permanent HW addr: "ETH_ADDR_FMT"\n",
172 slave->name, slave->up ? "up" : "down",
173 ETH_ADDR_ARGS(slave->mac));
174 }
175 set_proc_file("net/bonding", name, ds_cstr(&ds));
176 ds_destroy(&ds);
177}
178\f
179/* /proc/net/vlan compatibility.
180 *
181 * This is much more complex than I expected it to be. */
182
183struct compat_vlan {
184 /* Hash key. */
185 struct hmap_node trunk_node; /* Hash map node. */
186 char *trunk_dev; /* Name of trunk network device. */
187 int vid; /* VLAN number. */
188
189 /* Auxiliary data. */
190 char *vlan_dev; /* sprintf("%s.%d", trunk_dev, vid); */
191 struct svec tagged_devs; /* Name of tagged network device(s). */
192};
193
194/* Current set of VLAN devices, indexed two different ways. */
195static struct hmap vlans_by_trunk = HMAP_INITIALIZER(&vlans_by_trunk);
196static struct shash vlans_by_tagged = SHASH_INITIALIZER(&vlans_by_tagged);
197
198static bool remove_tagged_dev(struct shash_node *, const char *tagged_dev);
199static void update_vlan_config(void);
200static void set_vlan_proc_file(const struct compat_vlan *);
201static uint32_t hash_vlan(const char *trunk_dev, uint32_t vid);
202
203/* Updates the /proc/net/vlan compatibility layer's idea of what trunk device
204 * and VLAN the given 'tagged_dev' is associated with. If 'tagged_dev' has an
205 * implicit VLAN tag, then 'trunk_dev' should be the name of a network device
206 * on the same bridge that trunks that VLAN, and 'vid' should be the VLAN tag
207 * number. If 'tagged_dev' does not have an implicit VLAN tag, then
208 * 'trunk_dev' should be NULL and 'vid' should be -1.
209 *
210 * This function has no effect unless proc_net_compat_init() has been
211 * called. */
212void
213proc_net_compat_update_vlan(const char *tagged_dev, const char *trunk_dev,
214 int vid)
215{
216 struct compat_vlan *vlan;
217 struct shash_node *node;
218
219 if (!brc_sock) {
220 return;
221 }
222
223 /* Find the compat_vlan that we currently have for 'tagged_dev' (if
224 * any). */
225 node = shash_find(&vlans_by_tagged, tagged_dev);
226 vlan = node ? node->data : NULL;
227 if (vid <= 0 || !trunk_dev) {
228 if (vlan) {
229 if (remove_tagged_dev(node, tagged_dev)) {
230 update_vlan_config();
231 }
232 }
233 } else {
234 if (vlan) {
235 if (!strcmp(trunk_dev, vlan->trunk_dev) && vid == vlan->vid) {
236 /* No change. */
237 return;
238 } else {
239 /* 'tagged_dev' is attached to the wrong compat_vlan. Start
240 * by removing it from that one. */
241 remove_tagged_dev(node, tagged_dev);
242 node = NULL;
243 vlan = NULL;
244 }
245 }
246
247 /* 'tagged_dev' is not attached to any compat_vlan. Find the
248 * compat_vlan corresponding to (trunk_dev,vid) to attach it to, or
249 * create a new compat_vlan if none exists for (trunk_dev,vid). */
250 HMAP_FOR_EACH_WITH_HASH (vlan, struct compat_vlan, trunk_node,
251 hash_vlan(trunk_dev, vid),
252 &vlans_by_trunk) {
253 if (!strcmp(trunk_dev, vlan->trunk_dev) && vid == vlan->vid) {
254 break;
255 }
256 }
257 if (!vlan) {
258 /* Create a new compat_vlan for (trunk_dev,vid). */
259 vlan = xcalloc(1, sizeof *vlan);
260 vlan->trunk_dev = xstrdup(trunk_dev);
261 vlan->vid = vid;
262 vlan->vlan_dev = xasprintf("%s.%d", trunk_dev, vid);
263 svec_init(&vlan->tagged_devs);
264 hmap_insert(&vlans_by_trunk, &vlan->trunk_node,
265 hash_vlan(trunk_dev, vid));
266 set_vlan_proc_file(vlan);
267 }
268
269 /* Attach 'tagged_dev' to 'vlan'. */
270 svec_add(&vlan->tagged_devs, tagged_dev);
271 shash_add(&vlans_by_tagged, tagged_dev, vlan);
272 svec_sort(&vlan->tagged_devs);
273 update_vlan_config();
274 }
275}
276
277/* Remove 'tagged_dev' from the compat_vlan in 'node'. If that causes the
278 * compat_vlan to have no tagged_devs left, destroy the compat_vlan too. */
279static bool
280remove_tagged_dev(struct shash_node *node, const char *tagged_dev)
281{
282 struct compat_vlan *vlan = node->data;
283
284 svec_del(&vlan->tagged_devs, tagged_dev);
285 shash_delete(&vlans_by_tagged, node);
286 if (!vlan->tagged_devs.n) {
287 set_proc_file("net/vlan", vlan->vlan_dev, NULL);
288
289 hmap_remove(&vlans_by_trunk, &vlan->trunk_node);
290 svec_destroy(&vlan->tagged_devs);
291 free(vlan->trunk_dev);
292 free(vlan->vlan_dev);
293 free(vlan);
294 return true;
295 }
296 return false;
297}
298
299/* Returns a hash value for (trunk_dev,vid). */
300static uint32_t
301hash_vlan(const char *trunk_dev, uint32_t vid)
302{
303 return hash_int(vid, hash_string(trunk_dev, 0));
304}
305
306/* Update /proc/net/vlan/<vlan_dev> for 'vlan'. */
307static void
308set_vlan_proc_file(const struct compat_vlan *vlan)
309{
310 struct ds ds;
311
312 ds_init(&ds);
313 ds_put_format(
314 &ds,
315 "%s VID: %d\t REORDER_HDR: 1 dev->priv_flags: 81\n"
316 " total frames received 0\n"
317 " total bytes received 0\n"
318 " Broadcast/Multicast Rcvd 0\n"
319 "\n"
320 " total frames transmitted 0\n"
321 " total bytes transmitted 0\n"
322 " total headroom inc 0\n"
323 " total encap on xmit 0\n"
324 "Device: %s\n"
325 "INGRESS priority mappings: 0:0 1:0 2:0 3:0 4:0 5:0 6:0 7:0\n"
326 "EGRESSS priority Mappings: \n",
327 vlan->vlan_dev, vlan->vid, vlan->trunk_dev);
328 set_proc_file("net/vlan", vlan->vlan_dev, ds_cstr(&ds));
329 ds_destroy(&ds);
330}
331
332/* Update /proc/net/vlan/config. */
333static void
334update_vlan_config(void)
335{
336 struct compat_vlan *vlan;
337 struct ds ds;
338
339 ds_init(&ds);
340 ds_put_cstr(&ds, "VLAN Dev name | VLAN ID\n"
341 "Name-Type: VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD\n");
342 HMAP_FOR_EACH (vlan, struct compat_vlan, trunk_node, &vlans_by_trunk) {
343 ds_put_format(&ds, "%-15s| %d | %s\n",
344 vlan->vlan_dev, vlan->vid, vlan->trunk_dev);
345 }
346 set_proc_file("net/vlan", "config", ds_cstr(&ds));
347 ds_destroy(&ds);
348}