]>
Commit | Line | Data |
---|---|---|
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. */ | |
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); | |
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 | ||
183 | struct 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. */ | |
195 | static struct hmap vlans_by_trunk = HMAP_INITIALIZER(&vlans_by_trunk); | |
196 | static struct shash vlans_by_tagged = SHASH_INITIALIZER(&vlans_by_tagged); | |
197 | ||
198 | static bool remove_tagged_dev(struct shash_node *, const char *tagged_dev); | |
199 | static void update_vlan_config(void); | |
200 | static void set_vlan_proc_file(const struct compat_vlan *); | |
201 | static 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. */ | |
212 | void | |
213 | proc_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. */ | |
279 | static bool | |
280 | remove_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). */ | |
300 | static uint32_t | |
301 | hash_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'. */ | |
307 | static void | |
308 | set_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. */ | |
333 | static void | |
334 | update_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 | } |