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