]>
Commit | Line | Data |
---|---|---|
025e874a | 1 | /* |
2f51a7eb | 2 | * Copyright (c) 2011, 2013, 2014 Nicira, Inc. |
025e874a BP |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | * you may not use this file except in compliance with the License. | |
6 | * You may obtain a copy of the License at: | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | ||
17 | #include <config.h> | |
18 | ||
19 | #include "vlandev.h" | |
20 | ||
21 | #include <errno.h> | |
1c3e353d | 22 | #include <net/if.h> |
025e874a BP |
23 | #include <sys/ioctl.h> |
24 | #include <sys/stat.h> | |
25 | ||
1c3e353d | 26 | #include "dummy.h" |
025e874a | 27 | #include "hash.h" |
025e874a | 28 | #include "shash.h" |
259e0b1a | 29 | #include "socket-util.h" |
025e874a BP |
30 | #include "vlog.h" |
31 | ||
32 | VLOG_DEFINE_THIS_MODULE(vlandev); | |
33 | ||
1c3e353d BP |
34 | /* A vlandev implementation. */ |
35 | struct vlandev_class { | |
36 | int (*vd_refresh)(void); | |
37 | int (*vd_add)(const char *real_dev, int vid); | |
38 | int (*vd_del)(const char *vlan_dev); | |
39 | }; | |
40 | ||
2f51a7eb | 41 | #ifdef __linux__ |
1c3e353d BP |
42 | static const struct vlandev_class vlandev_linux_class; |
43 | #endif | |
44 | static const struct vlandev_class vlandev_stub_class; | |
45 | static const struct vlandev_class vlandev_dummy_class; | |
46 | ||
47 | /* The in-use vlandev implementation. */ | |
48 | static const struct vlandev_class *vd_class; | |
49 | ||
50 | /* Maps from a VLAN device name (e.g. "eth0.10") to struct vlan_dev. */ | |
51 | static struct shash vlan_devs = SHASH_INITIALIZER(&vlan_devs); | |
52 | ||
53 | /* Maps from a VLAN real device name (e.g. "eth0") to struct vlan_real_dev. */ | |
54 | static struct shash vlan_real_devs = SHASH_INITIALIZER(&vlan_real_devs); | |
55 | ||
56 | static int vlandev_add__(const char *vlan_dev, const char *real_dev, int vid); | |
57 | static int vlandev_del__(const char *vlan_dev); | |
58 | static void vlandev_clear__(void); | |
59 | ||
60 | static const struct vlandev_class * | |
61 | vlandev_get_class(void) | |
62 | { | |
63 | if (!vd_class) { | |
2f51a7eb | 64 | #if __linux__ |
1c3e353d BP |
65 | vd_class = &vlandev_linux_class; |
66 | #else | |
67 | vd_class = &vlandev_stub_class; | |
68 | #endif | |
69 | } | |
70 | return vd_class; | |
71 | } | |
72 | ||
73 | /* On Linux, the default implementation of VLAN devices creates and destroys | |
74 | * Linux VLAN devices. On other OSess, the default implementation is a | |
75 | * nonfunctional stub. In either case, this function replaces this default | |
76 | * implementation by a "dummy" implementation that simply reports back whatever | |
77 | * the client sets up with vlandev_add() and vlandev_del(). | |
78 | * | |
79 | * Don't call this function directly; use dummy_enable() from dummy.h. */ | |
80 | void | |
81 | vlandev_dummy_enable(void) | |
82 | { | |
83 | if (vd_class != &vlandev_dummy_class) { | |
84 | vd_class = &vlandev_dummy_class; | |
85 | vlandev_clear__(); | |
86 | } | |
87 | } | |
88 | ||
89 | /* Creates a new VLAN device for VLAN 'vid' on top of real Ethernet device | |
90 | * 'real_dev'. Returns 0 if successful, otherwise a positive errno value. On | |
91 | * OSes other than Linux, in the absence of dummies (see | |
92 | * vlandev_dummy_enable()), this always fails. | |
93 | * | |
94 | * The name of the new VLAN device is not easily predictable, because Linux | |
95 | * provides multiple naming schemes, does not allow the client to specify a | |
96 | * name, and does not directly report the new VLAN device's name. Use | |
97 | * vlandev_refresh() then vlandev_get_name() to find out the new VLAN device's | |
98 | * name,. */ | |
99 | int | |
100 | vlandev_add(const char *real_dev, int vid) | |
101 | { | |
102 | return vlandev_get_class()->vd_add(real_dev, vid); | |
103 | } | |
104 | ||
105 | /* Deletes the VLAN device named 'vlan_dev'. Returns 0 if successful, | |
106 | * otherwise a positive errno value. On OSes other than Linux, in the absence | |
107 | * of dummies (see vlandev_dummy_enable()), this always fails. */ | |
108 | int | |
109 | vlandev_del(const char *vlan_dev) | |
110 | { | |
111 | return vlandev_get_class()->vd_del(vlan_dev); | |
112 | } | |
113 | ||
114 | /* Refreshes the cache of real device to VLAN device mappings reported by | |
115 | * vlandev_get_real_devs() and vlandev_get_name(). Without calling this | |
116 | * function, changes made by vlandev_add() and vlandev_del() may not be | |
117 | * reflected by vlandev_get_real_devs() and vlandev_get_name() output. */ | |
118 | int | |
119 | vlandev_refresh(void) | |
120 | { | |
121 | const struct vlandev_class *class = vlandev_get_class(); | |
122 | return class->vd_refresh ? class->vd_refresh() : 0; | |
123 | } | |
124 | ||
125 | /* Returns a shash mapping from the name of real Ethernet devices used as the | |
126 | * basis of VLAN devices to struct vlan_real_devs. The caller must not modify | |
127 | * or free anything in the returned shash. | |
128 | * | |
129 | * Changes made by vlandev_add() and vlandev_del() may not be reflected in this | |
130 | * function's output without an intervening call to vlandev_refresh(). */ | |
131 | struct shash * | |
132 | vlandev_get_real_devs(void) | |
133 | { | |
134 | return &vlan_real_devs; | |
135 | } | |
136 | ||
137 | /* Returns the name of the VLAN device for VLAN 'vid' on top of | |
138 | * 'real_dev_name', or NULL if there is no such VLAN device. | |
139 | * | |
140 | * Changes made by vlandev_add() and vlandev_del() may not be reflected in this | |
141 | * function's output without an intervening call to vlandev_refresh(). */ | |
142 | const char * | |
143 | vlandev_get_name(const char *real_dev_name, int vid) | |
144 | { | |
145 | const struct vlan_real_dev *real_dev; | |
146 | ||
147 | real_dev = shash_find_data(&vlan_real_devs, real_dev_name); | |
148 | if (real_dev) { | |
149 | const struct vlan_dev *vlan_dev; | |
150 | ||
151 | HMAP_FOR_EACH_WITH_HASH (vlan_dev, hmap_node, hash_int(vid, 0), | |
152 | &real_dev->vlan_devs) { | |
153 | if (vlan_dev->vid == vid) { | |
154 | return vlan_dev->name; | |
155 | } | |
156 | } | |
157 | } | |
158 | ||
159 | return NULL; | |
160 | } | |
161 | \f | |
162 | /* The Linux vlandev implementation. */ | |
163 | ||
2f51a7eb | 164 | #ifdef __linux__ |
55abbe69 | 165 | #include "rtnetlink-link.h" |
025e874a BP |
166 | #include <linux/if_vlan.h> |
167 | #include <linux/sockios.h> | |
168 | #include "netdev-linux.h" | |
169 | ||
170 | static struct nln_notifier *vlan_cache_notifier; | |
025e874a BP |
171 | static bool cache_valid; |
172 | ||
173 | static void | |
174 | vlan_cache_cb(const struct rtnetlink_link_change *change OVS_UNUSED, | |
175 | void *aux OVS_UNUSED) | |
176 | { | |
177 | cache_valid = false; | |
178 | } | |
179 | ||
1c3e353d BP |
180 | static int |
181 | vlandev_linux_refresh(void) | |
025e874a BP |
182 | { |
183 | const char *fn = "/proc/net/vlan/config"; | |
025e874a BP |
184 | char line[128]; |
185 | FILE *stream; | |
186 | ||
187 | if (!vlan_cache_notifier) { | |
188 | vlan_cache_notifier = rtnetlink_link_notifier_create(vlan_cache_cb, | |
189 | NULL); | |
190 | if (!vlan_cache_notifier) { | |
191 | return EINVAL; | |
192 | } | |
193 | } | |
194 | ||
195 | if (cache_valid) { | |
196 | return 0; | |
197 | } | |
198 | ||
1c3e353d | 199 | vlandev_clear__(); |
025e874a BP |
200 | |
201 | /* Repopulate cache. */ | |
202 | stream = fopen(fn, "r"); | |
203 | if (!stream) { | |
204 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); | |
205 | int error = errno; | |
206 | struct stat s; | |
207 | ||
208 | if (error == ENOENT && !stat("/proc", &s)) { | |
209 | /* Probably the vlan module just isn't loaded, and probably that's | |
210 | * because no VLAN devices have been created. | |
211 | * | |
212 | * Not really an error. */ | |
213 | return 0; | |
214 | } | |
215 | ||
10a89ef0 | 216 | VLOG_WARN_RL(&rl, "%s: open failed (%s)", fn, ovs_strerror(error)); |
025e874a BP |
217 | return error; |
218 | } | |
219 | ||
220 | while (fgets(line, sizeof line, stream)) { | |
221 | char vlan_dev[16], real_dev[16]; | |
222 | int vid; | |
223 | ||
c2c28dfd | 224 | if (ovs_scan(line, "%15[^ |] | %d | %15s", vlan_dev, &vid, real_dev)) { |
1c3e353d | 225 | vlandev_add__(vlan_dev, real_dev, vid); |
025e874a BP |
226 | } |
227 | } | |
228 | fclose(stream); | |
229 | ||
230 | cache_valid = true; | |
231 | return 0; | |
232 | } | |
233 | ||
025e874a BP |
234 | static int |
235 | do_vlan_ioctl(const char *netdev_name, struct vlan_ioctl_args *via, | |
236 | int cmd, const char *cmd_name) | |
237 | { | |
238 | static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1); | |
239 | int error; | |
025e874a BP |
240 | |
241 | via->cmd = cmd; | |
242 | ovs_strlcpy(via->device1, netdev_name, sizeof via->device1); | |
243 | ||
259e0b1a | 244 | error = af_inet_ioctl(SIOCSIFVLAN, via); |
025e874a BP |
245 | if (error) { |
246 | VLOG_WARN_RL(&rl, "%s: VLAN ioctl %s failed (%s)", | |
10a89ef0 | 247 | netdev_name, cmd_name, ovs_strerror(error)); |
025e874a BP |
248 | } |
249 | return error; | |
250 | } | |
251 | ||
1c3e353d BP |
252 | static int |
253 | vlandev_linux_add(const char *real_dev, int vid) | |
025e874a BP |
254 | { |
255 | struct vlan_ioctl_args via; | |
256 | int error; | |
257 | ||
258 | memset(&via, 0, sizeof via); | |
259 | via.u.VID = vid; | |
260 | ||
261 | error = do_vlan_ioctl(real_dev, &via, ADD_VLAN_CMD, "ADD_VLAN_CMD"); | |
262 | if (!error) { | |
263 | cache_valid = false; | |
264 | } | |
265 | return error; | |
266 | } | |
267 | ||
1c3e353d BP |
268 | static int |
269 | vlandev_linux_del(const char *vlan_dev) | |
025e874a BP |
270 | { |
271 | struct vlan_ioctl_args via; | |
272 | int error; | |
273 | ||
274 | memset(&via, 0, sizeof via); | |
275 | error = do_vlan_ioctl(vlan_dev, &via, DEL_VLAN_CMD, "DEL_VLAN_CMD"); | |
276 | if (!error) { | |
277 | cache_valid = false; | |
278 | } | |
279 | return error; | |
280 | } | |
025e874a | 281 | |
1c3e353d BP |
282 | static const struct vlandev_class vlandev_linux_class = { |
283 | vlandev_linux_refresh, | |
284 | vlandev_linux_add, | |
285 | vlandev_linux_del | |
286 | }; | |
287 | #endif | |
288 | \f | |
289 | /* Stub implementation. */ | |
290 | ||
291 | static int | |
292 | vlandev_stub_add(const char *real_dev OVS_UNUSED, int vid OVS_UNUSED) | |
025e874a | 293 | { |
1c3e353d BP |
294 | VLOG_ERR("not supported on non-Linux platform"); |
295 | return EOPNOTSUPP; | |
025e874a BP |
296 | } |
297 | ||
1c3e353d BP |
298 | static int |
299 | vlandev_stub_del(const char *vlan_dev OVS_UNUSED) | |
025e874a | 300 | { |
1c3e353d BP |
301 | VLOG_ERR("not supported on non-Linux platform"); |
302 | return EOPNOTSUPP; | |
303 | } | |
025e874a | 304 | |
6a8a8528 | 305 | static const struct vlandev_class OVS_UNUSED vlandev_stub_class = { |
1c3e353d BP |
306 | NULL, /* vd_refresh */ |
307 | vlandev_stub_add, | |
308 | vlandev_stub_del | |
309 | }; | |
310 | \f | |
311 | /* Dummy implementation. */ | |
312 | ||
313 | static int | |
314 | vlandev_dummy_add(const char *real_dev, int vid) | |
315 | { | |
316 | char name[IFNAMSIZ]; | |
317 | ||
318 | if (snprintf(name, sizeof name, "%s.%d", real_dev, vid) >= sizeof name) { | |
319 | return ENAMETOOLONG; | |
320 | } | |
321 | return vlandev_add__(name, real_dev, vid); | |
025e874a BP |
322 | } |
323 | ||
1c3e353d BP |
324 | static int |
325 | vlandev_dummy_del(const char *vlan_dev) | |
025e874a | 326 | { |
1c3e353d | 327 | return vlandev_del__(vlan_dev); |
025e874a BP |
328 | } |
329 | ||
1c3e353d BP |
330 | static const struct vlandev_class vlandev_dummy_class = { |
331 | NULL, /* vd_refresh */ | |
332 | vlandev_dummy_add, | |
333 | vlandev_dummy_del | |
334 | }; | |
335 | \f | |
336 | static int | |
337 | vlandev_add__(const char *vlan_dev, const char *real_dev, int vid) | |
025e874a | 338 | { |
1c3e353d BP |
339 | uint32_t vid_hash = hash_int(vid, 0); |
340 | struct vlan_real_dev *vrd; | |
341 | struct vlan_dev *vd; | |
342 | ||
343 | if (vid < 0 || vid > 4095) { | |
344 | return EINVAL; | |
345 | } else if (shash_find(&vlan_devs, vlan_dev)) { | |
346 | return EEXIST; | |
347 | } | |
348 | ||
349 | vrd = shash_find_data(&vlan_real_devs, real_dev); | |
350 | if (!vrd) { | |
351 | vrd = xmalloc(sizeof *vrd); | |
352 | vrd->name = xstrdup(real_dev); | |
353 | hmap_init(&vrd->vlan_devs); | |
354 | shash_add_nocopy(&vlan_real_devs, vrd->name, vrd); | |
355 | } else { | |
356 | HMAP_FOR_EACH_WITH_HASH (vd, hmap_node, vid_hash, &vrd->vlan_devs) { | |
357 | if (vd->vid == vid) { | |
358 | return EEXIST; | |
359 | } | |
360 | } | |
361 | } | |
362 | ||
363 | vd = xmalloc(sizeof *vd); | |
364 | hmap_insert(&vrd->vlan_devs, &vd->hmap_node, vid_hash); | |
365 | vd->name = xstrdup(vlan_dev); | |
366 | vd->vid = vid; | |
367 | vd->real_dev = vrd; | |
368 | shash_add_nocopy(&vlan_devs, vd->name, vd); | |
369 | ||
370 | return 0; | |
025e874a BP |
371 | } |
372 | ||
1c3e353d BP |
373 | static int |
374 | vlandev_del__(const char *vlan_dev) | |
025e874a | 375 | { |
1c3e353d | 376 | struct shash_node *vd_node = shash_find(&vlan_devs, vlan_dev); |
96613315 | 377 | if (vd_node) { |
1c3e353d BP |
378 | struct vlan_dev *vd = vd_node->data; |
379 | struct vlan_real_dev *vrd = vd->real_dev; | |
380 | ||
381 | hmap_remove(&vrd->vlan_devs, &vd->hmap_node); | |
382 | if (hmap_is_empty(&vrd->vlan_devs)) { | |
383 | shash_find_and_delete_assert(&vlan_real_devs, vrd->name); | |
384 | free(vrd); | |
385 | } | |
386 | ||
387 | shash_delete(&vlan_devs, vd_node); | |
388 | free(vd); | |
389 | ||
390 | return 0; | |
391 | } else { | |
392 | return ENOENT; | |
393 | } | |
394 | } | |
395 | ||
396 | /* Clear 'vlan_devs' and 'vlan_real_devs' in preparation for repopulating. */ | |
397 | static void | |
398 | vlandev_clear__(void) | |
399 | { | |
400 | /* We do not free the 'name' members of struct vlan_dev and struct | |
401 | * vlan_real_dev, because the "shash"es own them.. */ | |
402 | struct shash_node *node; | |
403 | ||
404 | shash_clear_free_data(&vlan_devs); | |
405 | SHASH_FOR_EACH (node, &vlan_real_devs) { | |
406 | struct vlan_real_dev *vrd = node->data; | |
407 | ||
408 | hmap_destroy(&vrd->vlan_devs); | |
409 | } | |
410 | shash_clear_free_data(&vlan_real_devs); | |
025e874a | 411 | } |