]> git.proxmox.com Git - mirror_ovs.git/blame - lib/vlandev.c
netlink-socket: Refill comment to fit within 79 columns.
[mirror_ovs.git] / lib / vlandev.c
CommitLineData
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
32VLOG_DEFINE_THIS_MODULE(vlandev);
33
1c3e353d
BP
34/* A vlandev implementation. */
35struct 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
42static const struct vlandev_class vlandev_linux_class;
43#endif
44static const struct vlandev_class vlandev_stub_class;
45static const struct vlandev_class vlandev_dummy_class;
46
47/* The in-use vlandev implementation. */
48static const struct vlandev_class *vd_class;
49
50/* Maps from a VLAN device name (e.g. "eth0.10") to struct vlan_dev. */
51static 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. */
54static struct shash vlan_real_devs = SHASH_INITIALIZER(&vlan_real_devs);
55
56static int vlandev_add__(const char *vlan_dev, const char *real_dev, int vid);
57static int vlandev_del__(const char *vlan_dev);
58static void vlandev_clear__(void);
59
60static const struct vlandev_class *
61vlandev_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. */
80void
81vlandev_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,. */
99int
100vlandev_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. */
108int
109vlandev_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. */
118int
119vlandev_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(). */
131struct shash *
132vlandev_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(). */
142const char *
143vlandev_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
170static struct nln_notifier *vlan_cache_notifier;
025e874a
BP
171static bool cache_valid;
172
173static void
174vlan_cache_cb(const struct rtnetlink_link_change *change OVS_UNUSED,
175 void *aux OVS_UNUSED)
176{
177 cache_valid = false;
178}
179
1c3e353d
BP
180static int
181vlandev_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
234static int
235do_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
252static int
253vlandev_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
268static int
269vlandev_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
282static 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
291static int
292vlandev_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
298static int
299vlandev_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 305static 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
313static int
314vlandev_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
324static int
325vlandev_dummy_del(const char *vlan_dev)
025e874a 326{
1c3e353d 327 return vlandev_del__(vlan_dev);
025e874a
BP
328}
329
1c3e353d
BP
330static const struct vlandev_class vlandev_dummy_class = {
331 NULL, /* vd_refresh */
332 vlandev_dummy_add,
333 vlandev_dummy_del
334};
335\f
336static int
337vlandev_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
373static int
374vlandev_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. */
397static void
398vlandev_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}