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