Update primary code license to Apache 2.0.
[cascardo/ovs.git] / vswitchd / proc-net-compat.c
1 /* Copyright (c) 2009 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 #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. */
37 static struct nl_sock *brc_sock;
38
39 /* The Generic Netlink family number used for bridge compatibility. */
40 static int brc_family = 0;
41
42 /* Rate limiting for log messages. */
43 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
44
45 static void flush_dir(const char *dir);
46 static 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. */
50 int
51 proc_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
70 static int
71 set_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
95 static void
96 flush_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. */
127 void
128 proc_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);
157     for (i = 0; i < bond->n_slaves; i++) {
158         const struct compat_bond_slave *slave = &bond->slaves[i];
159         ds_put_format(
160             &ds,
161             "\n"
162             "Slave Interface: %s\n"
163             "MII Status: %s\n"
164             "Link Failure Count: 0\n"
165             "Permanent HW addr: "ETH_ADDR_FMT"\n",
166             slave->name, slave->up ? "up" : "down",
167             ETH_ADDR_ARGS(slave->mac));
168     }
169     set_proc_file("net/bonding", name, ds_cstr(&ds));
170     ds_destroy(&ds);
171 }
172 \f
173 /* /proc/net/vlan compatibility.
174  *
175  * This is much more complex than I expected it to be. */
176
177 struct compat_vlan {
178     /* Hash key. */
179     struct hmap_node trunk_node; /* Hash map node. */
180     char *trunk_dev;             /* Name of trunk network device. */
181     int vid;                     /* VLAN number. */
182
183     /* Auxiliary data. */
184     char *vlan_dev;             /* sprintf("%s.%d", trunk_dev, vid); */
185     struct svec tagged_devs;    /* Name of tagged network device(s). */
186 };
187
188 /* Current set of VLAN devices, indexed two different ways. */
189 static struct hmap vlans_by_trunk = HMAP_INITIALIZER(&vlans_by_trunk);
190 static struct shash vlans_by_tagged = SHASH_INITIALIZER(&vlans_by_tagged);
191
192 static bool remove_tagged_dev(struct shash_node *, const char *tagged_dev);
193 static void update_vlan_config(void);
194 static void set_vlan_proc_file(const struct compat_vlan *);
195 static uint32_t hash_vlan(const char *trunk_dev, uint32_t vid);
196
197 /* Updates the /proc/net/vlan compatibility layer's idea of what trunk device
198  * and VLAN the given 'tagged_dev' is associated with.  If 'tagged_dev' has an
199  * implicit VLAN tag, then 'trunk_dev' should be the name of a network device
200  * on the same bridge that trunks that VLAN, and 'vid' should be the VLAN tag
201  * number.  If 'tagged_dev' does not have an implicit VLAN tag, then
202  * 'trunk_dev' should be NULL and 'vid' should be -1.
203  *
204  * This function has no effect unless proc_net_compat_init() has been
205  * called. */
206 void
207 proc_net_compat_update_vlan(const char *tagged_dev, const char *trunk_dev,
208                             int vid)
209 {
210     struct compat_vlan *vlan;
211     struct shash_node *node;
212
213     if (!brc_sock) {
214         return;
215     }
216
217     /* Find the compat_vlan that we currently have for 'tagged_dev' (if
218      * any). */
219     node = shash_find(&vlans_by_tagged, tagged_dev);
220     vlan = node ? node->data : NULL;
221     if (vid <= 0 || !trunk_dev) {
222         if (vlan) {
223             if (remove_tagged_dev(node, tagged_dev)) {
224                 update_vlan_config();
225             }
226         }
227     } else {
228         if (vlan) {
229             if (!strcmp(trunk_dev, vlan->trunk_dev) && vid == vlan->vid) {
230                 /* No change. */
231                 return;
232             } else {
233                 /* 'tagged_dev' is attached to the wrong compat_vlan.  Start
234                  * by removing it from that one. */
235                 remove_tagged_dev(node, tagged_dev);
236                 node = NULL;
237                 vlan = NULL;
238             }
239         }
240
241         /* 'tagged_dev' is not attached to any compat_vlan.  Find the
242          * compat_vlan corresponding to (trunk_dev,vid) to attach it to, or
243          * create a new compat_vlan if none exists for (trunk_dev,vid). */
244         HMAP_FOR_EACH_WITH_HASH (vlan, struct compat_vlan, trunk_node,
245                                  hash_vlan(trunk_dev, vid),
246                                  &vlans_by_trunk) {
247             if (!strcmp(trunk_dev, vlan->trunk_dev) && vid == vlan->vid) {
248                 break;
249             }
250         }
251         if (!vlan) {
252             /* Create a new compat_vlan for (trunk_dev,vid). */
253             vlan = xcalloc(1, sizeof *vlan);
254             vlan->trunk_dev = xstrdup(trunk_dev);
255             vlan->vid = vid;
256             vlan->vlan_dev = xasprintf("%s.%d", trunk_dev, vid);
257             svec_init(&vlan->tagged_devs);
258             hmap_insert(&vlans_by_trunk, &vlan->trunk_node,
259                         hash_vlan(trunk_dev, vid));
260             set_vlan_proc_file(vlan);
261         }
262
263         /* Attach 'tagged_dev' to 'vlan'. */
264         svec_add(&vlan->tagged_devs, tagged_dev);
265         shash_add(&vlans_by_tagged, tagged_dev, vlan);
266         svec_sort(&vlan->tagged_devs);
267         update_vlan_config();
268     }
269 }
270
271 /* Remove 'tagged_dev' from the compat_vlan in 'node'.  If that causes the
272  * compat_vlan to have no tagged_devs left, destroy the compat_vlan too. */
273 static bool
274 remove_tagged_dev(struct shash_node *node, const char *tagged_dev)
275 {
276     struct compat_vlan *vlan = node->data;
277
278     svec_del(&vlan->tagged_devs, tagged_dev);
279     shash_delete(&vlans_by_tagged, node);
280     if (!vlan->tagged_devs.n) {
281         set_proc_file("net/vlan", vlan->vlan_dev, NULL);
282
283         hmap_remove(&vlans_by_trunk, &vlan->trunk_node);
284         svec_destroy(&vlan->tagged_devs);
285         free(vlan->trunk_dev);
286         free(vlan->vlan_dev);
287         free(vlan);
288         return true;
289     }
290     return false;
291 }
292
293 /* Returns a hash value for (trunk_dev,vid). */
294 static uint32_t
295 hash_vlan(const char *trunk_dev, uint32_t vid)
296 {
297     return hash_int(vid, hash_string(trunk_dev, 0));
298 }
299
300 /* Update /proc/net/vlan/<vlan_dev> for 'vlan'. */
301 static void
302 set_vlan_proc_file(const struct compat_vlan *vlan)
303 {
304     struct ds ds;
305
306     ds_init(&ds);
307     ds_put_format(
308         &ds,
309         "%s  VID: %d\t REORDER_HDR: 1  dev->priv_flags: 81\n"
310         "         total frames received            0\n"
311         "          total bytes received            0\n"
312         "      Broadcast/Multicast Rcvd            0\n"
313         "\n"
314         "      total frames transmitted            0\n"
315         "       total bytes transmitted            0\n"
316         "            total headroom inc            0\n"
317         "           total encap on xmit            0\n"
318         "Device: %s\n"
319         "INGRESS priority mappings: 0:0  1:0  2:0  3:0  4:0  5:0  6:0 7:0\n"
320         "EGRESSS priority Mappings: \n",
321         vlan->vlan_dev, vlan->vid, vlan->trunk_dev);
322     set_proc_file("net/vlan", vlan->vlan_dev, ds_cstr(&ds));
323     ds_destroy(&ds);
324 }
325
326 /* Update /proc/net/vlan/config. */
327 static void
328 update_vlan_config(void)
329 {
330     struct compat_vlan *vlan;
331     struct ds ds;
332
333     ds_init(&ds);
334     ds_put_cstr(&ds, "VLAN Dev name     | VLAN ID\n"
335                 "Name-Type: VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD\n");
336     HMAP_FOR_EACH (vlan, struct compat_vlan, trunk_node, &vlans_by_trunk) {
337         ds_put_format(&ds, "%-15s| %d  | %s\n",
338                       vlan->vlan_dev, vlan->vid, vlan->trunk_dev);
339     }
340     set_proc_file("net/vlan", "config", ds_cstr(&ds));
341     ds_destroy(&ds);
342 }