]>
Commit | Line | Data |
---|---|---|
bfb1f2d5 | 1 | /* Copyright (c) 2009, 2010 Nicira Networks |
064af421 | 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" | |
bfb1f2d5 BP |
18 | |
19 | #ifdef HAVE_NETLINK | |
064af421 BP |
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); | |
2aebae83 BP |
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 | ||
064af421 BP |
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, struct compat_vlan, trunk_node, | |
253 | hash_vlan(trunk_dev, vid), | |
254 | &vlans_by_trunk) { | |
255 | if (!strcmp(trunk_dev, vlan->trunk_dev) && vid == vlan->vid) { | |
256 | break; | |
257 | } | |
258 | } | |
259 | if (!vlan) { | |
260 | /* Create a new compat_vlan for (trunk_dev,vid). */ | |
ec6fde61 | 261 | vlan = xzalloc(sizeof *vlan); |
064af421 BP |
262 | vlan->trunk_dev = xstrdup(trunk_dev); |
263 | vlan->vid = vid; | |
264 | vlan->vlan_dev = xasprintf("%s.%d", trunk_dev, vid); | |
265 | svec_init(&vlan->tagged_devs); | |
266 | hmap_insert(&vlans_by_trunk, &vlan->trunk_node, | |
267 | hash_vlan(trunk_dev, vid)); | |
268 | set_vlan_proc_file(vlan); | |
269 | } | |
270 | ||
271 | /* Attach 'tagged_dev' to 'vlan'. */ | |
272 | svec_add(&vlan->tagged_devs, tagged_dev); | |
273 | shash_add(&vlans_by_tagged, tagged_dev, vlan); | |
274 | svec_sort(&vlan->tagged_devs); | |
275 | update_vlan_config(); | |
276 | } | |
277 | } | |
278 | ||
279 | /* Remove 'tagged_dev' from the compat_vlan in 'node'. If that causes the | |
280 | * compat_vlan to have no tagged_devs left, destroy the compat_vlan too. */ | |
281 | static bool | |
282 | remove_tagged_dev(struct shash_node *node, const char *tagged_dev) | |
283 | { | |
284 | struct compat_vlan *vlan = node->data; | |
285 | ||
286 | svec_del(&vlan->tagged_devs, tagged_dev); | |
287 | shash_delete(&vlans_by_tagged, node); | |
288 | if (!vlan->tagged_devs.n) { | |
289 | set_proc_file("net/vlan", vlan->vlan_dev, NULL); | |
290 | ||
291 | hmap_remove(&vlans_by_trunk, &vlan->trunk_node); | |
292 | svec_destroy(&vlan->tagged_devs); | |
293 | free(vlan->trunk_dev); | |
294 | free(vlan->vlan_dev); | |
295 | free(vlan); | |
296 | return true; | |
297 | } | |
298 | return false; | |
299 | } | |
300 | ||
301 | /* Returns a hash value for (trunk_dev,vid). */ | |
302 | static uint32_t | |
303 | hash_vlan(const char *trunk_dev, uint32_t vid) | |
304 | { | |
305 | return hash_int(vid, hash_string(trunk_dev, 0)); | |
306 | } | |
307 | ||
308 | /* Update /proc/net/vlan/<vlan_dev> for 'vlan'. */ | |
309 | static void | |
310 | set_vlan_proc_file(const struct compat_vlan *vlan) | |
311 | { | |
312 | struct ds ds; | |
313 | ||
314 | ds_init(&ds); | |
315 | ds_put_format( | |
316 | &ds, | |
317 | "%s VID: %d\t REORDER_HDR: 1 dev->priv_flags: 81\n" | |
318 | " total frames received 0\n" | |
319 | " total bytes received 0\n" | |
320 | " Broadcast/Multicast Rcvd 0\n" | |
321 | "\n" | |
322 | " total frames transmitted 0\n" | |
323 | " total bytes transmitted 0\n" | |
324 | " total headroom inc 0\n" | |
325 | " total encap on xmit 0\n" | |
326 | "Device: %s\n" | |
327 | "INGRESS priority mappings: 0:0 1:0 2:0 3:0 4:0 5:0 6:0 7:0\n" | |
328 | "EGRESSS priority Mappings: \n", | |
329 | vlan->vlan_dev, vlan->vid, vlan->trunk_dev); | |
330 | set_proc_file("net/vlan", vlan->vlan_dev, ds_cstr(&ds)); | |
331 | ds_destroy(&ds); | |
332 | } | |
333 | ||
334 | /* Update /proc/net/vlan/config. */ | |
335 | static void | |
336 | update_vlan_config(void) | |
337 | { | |
338 | struct compat_vlan *vlan; | |
339 | struct ds ds; | |
340 | ||
341 | ds_init(&ds); | |
342 | ds_put_cstr(&ds, "VLAN Dev name | VLAN ID\n" | |
343 | "Name-Type: VLAN_NAME_TYPE_RAW_PLUS_VID_NO_PAD\n"); | |
344 | HMAP_FOR_EACH (vlan, struct compat_vlan, trunk_node, &vlans_by_trunk) { | |
345 | ds_put_format(&ds, "%-15s| %d | %s\n", | |
346 | vlan->vlan_dev, vlan->vid, vlan->trunk_dev); | |
347 | } | |
348 | set_proc_file("net/vlan", "config", ds_cstr(&ds)); | |
349 | ds_destroy(&ds); | |
350 | } | |
bfb1f2d5 BP |
351 | #else /* !HAVE_NETLINK */ |
352 | #include "compiler.h" | |
353 | ||
354 | int | |
355 | proc_net_compat_init(void) | |
356 | { | |
357 | return 0; | |
358 | } | |
359 | ||
360 | void | |
361 | proc_net_compat_update_bond(const char *name OVS_UNUSED, | |
362 | const struct compat_bond *bond OVS_UNUSED) | |
363 | { | |
364 | } | |
365 | ||
366 | void | |
367 | proc_net_compat_update_vlan(const char *tagged_dev OVS_UNUSED, | |
368 | const char *trunk_dev OVS_UNUSED, | |
369 | int vid OVS_UNUSED) | |
370 | { | |
371 | } | |
372 | #endif /* !HAVE_NETLINK */ |