tests: Add test suite for packets.h.
[openvswitch] / vswitchd / proc-net-compat.c
1 /* Copyright (c) 2009, 2010, 2011 Nicira Networks
2  *
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:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
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.
14  */
15
16 #include <config.h>
17 #include "proc-net-compat.h"
18
19 #ifdef HAVE_NETLINK
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-socket.h"
29 #include "netlink.h"
30 #include "ofpbuf.h"
31 #include "openvswitch/brcompat-netlink.h"
32 #include "hmap.h"
33 #include "shash.h"
34 #include "svec.h"
35 #include "vlog.h"
36
37 VLOG_DEFINE_THIS_MODULE(proc_net_compat);
38
39 /* Netlink socket to bridge compatibility kernel module. */
40 static struct nl_sock *brc_sock;
41
42 /* The Generic Netlink family number used for bridge compatibility. */
43 static int brc_family = 0;
44
45 /* Rate limiting for log messages. */
46 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
47
48 static void flush_dir(const char *dir);
49 static int set_proc_file(const char *dir, const char *file, const char *data);
50
51 /* Initializes the /proc/net compatibility layer.  Returns 0 if successful,
52  * otherwise a positive errno value. */
53 int
54 proc_net_compat_init(void)
55 {
56     if (!brc_sock) {
57         int retval = nl_lookup_genl_family(BRC_GENL_FAMILY_NAME, &brc_family);
58         if (retval) {
59             return retval;
60         }
61
62         retval = nl_sock_create(NETLINK_GENERIC, &brc_sock);
63         if (retval) {
64             return retval;
65         }
66
67         flush_dir("/proc/net/vlan");
68         flush_dir("/proc/net/bonding");
69     }
70     return 0;
71 }
72
73 static int
74 set_proc_file(const char *dir, const char *file, const char *data)
75 {
76     struct ofpbuf request;
77     int retval;
78
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);
84     if (data) {
85         nl_msg_put_string(&request, BRC_GENL_A_PROC_DATA, data);
86     }
87
88     retval = nl_sock_transact(brc_sock, &request, NULL);
89     ofpbuf_uninit(&request);
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
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);
163     }
164
165     for (i = 0; i < bond->n_slaves; i++) {
166         const struct compat_bond_slave *slave = &bond->slaves[i];
167         ds_put_format(
168             &ds,
169             "\n"
170             "Slave Interface: %s\n"
171             "MII Status: %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));
176     }
177     set_proc_file("net/bonding", name, ds_cstr(&ds));
178     ds_destroy(&ds);
179 }
180 \f
181 /* /proc/net/vlan compatibility.
182  *
183  * This is much more complex than I expected it to be. */
184
185 struct compat_vlan {
186     /* Hash key. */
187     struct hmap_node trunk_node; /* Hash map node. */
188     char *trunk_dev;             /* Name of trunk network device. */
189     int vid;                     /* VLAN number. */
190
191     /* Auxiliary data. */
192     char *vlan_dev;             /* sprintf("%s.%d", trunk_dev, vid); */
193     struct svec tagged_devs;    /* Name of tagged network device(s). */
194 };
195
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);
199
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);
204
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.
211  *
212  * This function has no effect unless proc_net_compat_init() has been
213  * called. */
214 void
215 proc_net_compat_update_vlan(const char *tagged_dev, const char *trunk_dev,
216                             int vid)
217 {
218     struct compat_vlan *vlan;
219     struct shash_node *node;
220
221     if (!brc_sock) {
222         return;
223     }
224
225     /* Find the compat_vlan that we currently have for 'tagged_dev' (if
226      * any). */
227     node = shash_find(&vlans_by_tagged, tagged_dev);
228     vlan = node ? node->data : NULL;
229     if (vid <= 0 || !trunk_dev) {
230         if (vlan) {
231             if (remove_tagged_dev(node, tagged_dev)) {
232                 update_vlan_config();
233             }
234         }
235     } else {
236         if (vlan) {
237             if (!strcmp(trunk_dev, vlan->trunk_dev) && vid == vlan->vid) {
238                 /* No change. */
239                 return;
240             } else {
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);
244                 node = NULL;
245                 vlan = NULL;
246             }
247         }
248
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),
253                                  &vlans_by_trunk) {
254             if (!strcmp(trunk_dev, vlan->trunk_dev) && vid == vlan->vid) {
255                 break;
256             }
257         }
258         if (!vlan) {
259             /* Create a new compat_vlan for (trunk_dev,vid). */
260             vlan = xzalloc(sizeof *vlan);
261             vlan->trunk_dev = xstrdup(trunk_dev);
262             vlan->vid = vid;
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);
268         }
269
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();
275     }
276 }
277
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. */
280 static bool
281 remove_tagged_dev(struct shash_node *node, const char *tagged_dev)
282 {
283     struct compat_vlan *vlan = node->data;
284
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);
289
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);
294         free(vlan);
295         return true;
296     }
297     return false;
298 }
299
300 /* Returns a hash value for (trunk_dev,vid). */
301 static uint32_t
302 hash_vlan(const char *trunk_dev, uint32_t vid)
303 {
304     return hash_int(vid, hash_string(trunk_dev, 0));
305 }
306
307 /* Update /proc/net/vlan/<vlan_dev> for 'vlan'. */
308 static void
309 set_vlan_proc_file(const struct compat_vlan *vlan)
310 {
311     struct ds ds;
312
313     ds_init(&ds);
314     ds_put_format(
315         &ds,
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"
320         "\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"
325         "Device: %s\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));
330     ds_destroy(&ds);
331 }
332
333 /* Update /proc/net/vlan/config. */
334 static void
335 update_vlan_config(void)
336 {
337     struct compat_vlan *vlan;
338     struct ds ds;
339
340     ds_init(&ds);
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);
346     }
347     set_proc_file("net/vlan", "config", ds_cstr(&ds));
348     ds_destroy(&ds);
349 }
350 #else  /* !HAVE_NETLINK */
351 #include "compiler.h"
352
353 int
354 proc_net_compat_init(void)
355 {
356     return 0;
357 }
358
359 void
360 proc_net_compat_update_bond(const char *name OVS_UNUSED,
361                             const struct compat_bond *bond OVS_UNUSED)
362 {
363 }
364
365 void
366 proc_net_compat_update_vlan(const char *tagged_dev OVS_UNUSED,
367                             const char *trunk_dev OVS_UNUSED,
368                             int vid OVS_UNUSED)
369 {
370 }
371 #endif  /* !HAVE_NETLINK */