Merge branch 'master' of ssh://git.onelab.eu/git/sliver-openvswitch
[sliver-openvswitch.git] / lib / vlandev.c
1 /*
2  * Copyright (c) 2011 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 <sys/ioctl.h>
23 #include <sys/stat.h>
24
25 #include "hash.h"
26 #include "shash.h"
27 #include "vlog.h"
28
29 VLOG_DEFINE_THIS_MODULE(vlandev);
30
31 #ifdef __linux__
32 #include "rtnetlink-link.h"
33 #include <linux/if_vlan.h>
34 #include <linux/sockios.h>
35 #include "netdev-linux.h"
36
37 static struct nln_notifier *vlan_cache_notifier;
38 static struct shash vlan_devs = SHASH_INITIALIZER(&vlan_devs);
39 static struct shash vlan_real_devs = SHASH_INITIALIZER(&vlan_real_devs);
40 static bool cache_valid;
41
42 static void
43 vlan_cache_cb(const struct rtnetlink_link_change *change OVS_UNUSED,
44               void *aux OVS_UNUSED)
45 {
46     cache_valid = false;
47 }
48
49 int
50 vlandev_refresh(void)
51 {
52     const char *fn = "/proc/net/vlan/config";
53     struct shash_node *node;
54     char line[128];
55     FILE *stream;
56
57     if (!vlan_cache_notifier) {
58         vlan_cache_notifier = rtnetlink_link_notifier_create(vlan_cache_cb,
59                                                              NULL);
60         if (!vlan_cache_notifier) {
61             return EINVAL;
62         }
63     }
64
65     if (cache_valid) {
66         return 0;
67     }
68
69     /* Free old cache.
70      *
71      * The 'name' members point to strings owned by the "shash"es so we do not
72      * free them ourselves. */
73     shash_clear_free_data(&vlan_devs);
74     SHASH_FOR_EACH (node, &vlan_real_devs) {
75         struct vlan_real_dev *vrd = node->data;
76
77         hmap_destroy(&vrd->vlan_devs);
78     }
79     shash_clear_free_data(&vlan_real_devs);
80
81     /* Repopulate cache. */
82     stream = fopen(fn, "r");
83     if (!stream) {
84         static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
85         int error = errno;
86         struct stat s;
87
88         if (error == ENOENT && !stat("/proc", &s)) {
89             /* Probably the vlan module just isn't loaded, and probably that's
90              * because no VLAN devices have been created.
91              *
92              * Not really an error. */
93             return 0;
94         }
95
96         VLOG_WARN_RL(&rl, "%s: open failed (%s)", fn, strerror(error));
97         return error;
98     }
99
100     while (fgets(line, sizeof line, stream)) {
101         char vlan_dev[16], real_dev[16];
102         int vid;
103
104         if (sscanf(line, "%15[^ |] | %d | %15s", vlan_dev, &vid, real_dev) == 3
105             && vid >= 0 && vid <= 4095
106             && !shash_find(&vlan_devs, vlan_dev)) {
107             struct vlan_real_dev *vrd;
108             struct vlan_dev *vd;
109
110             vrd = shash_find_data(&vlan_real_devs, real_dev);
111             if (!vrd) {
112                 vrd = xmalloc(sizeof *vrd);
113                 vrd->name = xstrdup(real_dev);
114                 hmap_init(&vrd->vlan_devs);
115                 shash_add_nocopy(&vlan_real_devs, vrd->name, vrd);
116             }
117
118             vd = xmalloc(sizeof *vd);
119             hmap_insert(&vrd->vlan_devs, &vd->hmap_node, hash_int(vid, 0));
120             vd->name = xstrdup(vlan_dev);
121             vd->vid = vid;
122             vd->real_dev = vrd;
123             shash_add_nocopy(&vlan_devs, vd->name, vd);
124         }
125     }
126     fclose(stream);
127
128     cache_valid = true;
129     return 0;
130 }
131
132 struct shash *
133 vlandev_get_real_devs(void)
134 {
135     return &vlan_real_devs;
136 }
137
138 const char *
139 vlandev_get_name(const char *real_dev_name, int vid)
140 {
141     const struct vlan_real_dev *real_dev;
142
143     real_dev = shash_find_data(&vlan_real_devs, real_dev_name);
144     if (real_dev) {
145         const struct vlan_dev *vlan_dev;
146
147         HMAP_FOR_EACH_WITH_HASH (vlan_dev, hmap_node, hash_int(vid, 0),
148                                  &real_dev->vlan_devs) {
149             if (vlan_dev->vid == vid) {
150                 return vlan_dev->name;
151             }
152         }
153     }
154
155     return NULL;
156 }
157
158 static int
159 do_vlan_ioctl(const char *netdev_name, struct vlan_ioctl_args *via,
160               int cmd, const char *cmd_name)
161 {
162     static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
163     int error;
164     int sock;
165
166     via->cmd = cmd;
167     ovs_strlcpy(via->device1, netdev_name, sizeof via->device1);
168
169     sock = netdev_linux_get_af_inet_sock();
170     if (sock < 0) {
171         return -sock;
172     }
173
174     error = ioctl(sock, SIOCSIFVLAN, via) < 0 ? errno : 0;
175     if (error) {
176         VLOG_WARN_RL(&rl, "%s: VLAN ioctl %s failed (%s)",
177                      netdev_name, cmd_name, strerror(error));
178     }
179     return error;
180 }
181
182 int
183 vlandev_add(const char *real_dev, int vid)
184 {
185     struct vlan_ioctl_args via;
186     int error;
187
188     memset(&via, 0, sizeof via);
189     via.u.VID = vid;
190
191     error = do_vlan_ioctl(real_dev, &via, ADD_VLAN_CMD, "ADD_VLAN_CMD");
192     if (!error) {
193         cache_valid = false;
194     }
195     return error;
196 }
197
198 int
199 vlandev_del(const char *vlan_dev)
200 {
201     struct vlan_ioctl_args via;
202     int error;
203
204     memset(&via, 0, sizeof via);
205     error = do_vlan_ioctl(vlan_dev, &via, DEL_VLAN_CMD, "DEL_VLAN_CMD");
206     if (!error) {
207         cache_valid = false;
208     }
209     return error;
210 }
211 #else  /* !__linux__ */
212 /* Stubs. */
213
214 int
215 vlandev_refresh(void)
216 {
217     return 0;
218 }
219
220 struct shash *
221 vlandev_get_real_devs(void)
222 {
223     static struct shash vlan_real_devs = SHASH_INITIALIZER(&vlan_real_devs);
224
225     return &vlan_real_devs;
226 }
227
228 const char *
229 vlandev_get_name(const char *real_dev_name OVS_UNUSED, int vid OVS_UNUSED)
230 {
231     return NULL;
232 }
233
234 int
235 vlandev_add(const char *real_dev OVS_UNUSED, int vid OVS_UNUSED)
236 {
237     VLOG_ERR("not supported on non-Linux platform");
238     return EOPNOTSUPP;
239 }
240
241 int
242 vlandev_del(const char *vlan_dev OVS_UNUSED)
243 {
244     VLOG_ERR("not supported on non-Linux platform");
245     return EOPNOTSUPP;
246 }
247 #endif