• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/netifd/vlan.c

  1 /*
  2  * netifd - network interface daemon
  3  * Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
  4  *
  5  * This program is free software; you can redistribute it and/or modify
  6  * it under the terms of the GNU General Public License version 2
  7  * as published by the Free Software Foundation
  8  *
  9  * This program is distributed in the hope that it will be useful,
 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 12  * GNU General Public License for more details.
 13  */
 14 #include <string.h>
 15 #include <stdlib.h>
 16 #include <stdio.h>
 17 #include <ctype.h>
 18 
 19 #include "netifd.h"
 20 #include "system.h"
 21 
 22 static struct blob_buf b;
 23 
 24 struct vlan_device {
 25         struct device dev;
 26         struct device_user dep;
 27 
 28         device_state_cb set_state;
 29         int id;
 30 };
 31 
 32 static void free_vlan_if(struct device *iface)
 33 {
 34         struct vlan_device *vldev;
 35 
 36         vldev = container_of(iface, struct vlan_device, dev);
 37         device_remove_user(&vldev->dep);
 38         device_cleanup(&vldev->dev);
 39         free(vldev);
 40 }
 41 
 42 static int
 43 __vlan_hotplug_op(struct device *dev, struct device *member, struct blob_attr *vlan, bool add)
 44 {
 45         struct vlan_device *vldev = container_of(dev, struct vlan_device, dev);
 46         void *a;
 47 
 48         dev = vldev->dep.dev;
 49         if (!dev || !dev->hotplug_ops)
 50                 return UBUS_STATUS_NOT_SUPPORTED;
 51 
 52         blob_buf_init(&b, 0);
 53         a = blobmsg_open_array(&b, "vlans");
 54         blobmsg_printf(&b, NULL, "%d", vldev->id);
 55         blobmsg_close_array(&b, a);
 56 
 57         if (add)
 58                 return dev->hotplug_ops->add(dev, member, blobmsg_data(b.head));
 59         else
 60                 return dev->hotplug_ops->del(dev, member, blobmsg_data(b.head));
 61 }
 62 
 63 static int
 64 vlan_hotplug_add(struct device *dev, struct device *member, struct blob_attr *vlan)
 65 {
 66         return __vlan_hotplug_op(dev, member, vlan, true);
 67 }
 68 
 69 static int
 70 vlan_hotplug_del(struct device *dev, struct device *member, struct blob_attr *vlan)
 71 {
 72         return __vlan_hotplug_op(dev, member, vlan, false);
 73 }
 74 
 75 static int
 76 vlan_hotplug_prepare(struct device *dev, struct device **bridge_dev)
 77 {
 78         struct vlan_device *vldev = container_of(dev, struct vlan_device, dev);
 79 
 80         dev = vldev->dep.dev;
 81         if (!dev || !dev->hotplug_ops)
 82                 return UBUS_STATUS_NOT_SUPPORTED;
 83 
 84         return dev->hotplug_ops->prepare(dev, bridge_dev);
 85 }
 86 
 87 static void vlan_hotplug_check(struct vlan_device *vldev, struct device *dev)
 88 {
 89         static const struct device_hotplug_ops hotplug_ops = {
 90                 .prepare = vlan_hotplug_prepare,
 91                 .add = vlan_hotplug_add,
 92                 .del = vlan_hotplug_del
 93         };
 94 
 95         if (!dev || !dev->hotplug_ops || avl_is_empty(&dev->vlans.avl)) {
 96                 vldev->dev.hotplug_ops = NULL;
 97                 return;
 98         }
 99 
100         vldev->dev.hotplug_ops = &hotplug_ops;
101 }
102 
103 
104 static int vlan_set_device_state(struct device *dev, bool up)
105 {
106         struct vlan_device *vldev;
107         int ret = 0;
108 
109         vldev = container_of(dev, struct vlan_device, dev);
110         if (!up) {
111                 vldev->set_state(dev, false);
112                 system_vlan_del(dev);
113                 device_release(&vldev->dep);
114                 return 0;
115         }
116 
117         ret = device_claim(&vldev->dep);
118         if (ret < 0)
119                 return ret;
120 
121         system_vlan_add(vldev->dep.dev, vldev->id);
122         ret = vldev->set_state(dev, true);
123         if (ret)
124                 device_release(&vldev->dep);
125 
126         return ret;
127 }
128 
129 static void vlan_dev_cb(struct device_user *dep, enum device_event ev)
130 {
131         char name[IFNAMSIZ + 1];
132         struct vlan_device *vldev;
133 
134         vldev = container_of(dep, struct vlan_device, dep);
135         switch(ev) {
136         case DEV_EVENT_ADD:
137                 device_set_present(&vldev->dev, true);
138                 break;
139         case DEV_EVENT_REMOVE:
140                 device_set_present(&vldev->dev, false);
141                 break;
142         case DEV_EVENT_UPDATE_IFNAME:
143                 vlan_hotplug_check(vldev, dep->dev);
144                 vldev->dev.hidden = dep->dev->hidden;
145                 if (snprintf(name, sizeof(name), "%s.%d", dep->dev->ifname,
146                              vldev->id) >= sizeof(name) - 1 ||
147                     device_set_ifname(&vldev->dev, name))
148                         free_vlan_if(&vldev->dev);
149                 break;
150         case DEV_EVENT_TOPO_CHANGE:
151                 /* Propagate topo changes */
152                 device_broadcast_event(&vldev->dev, DEV_EVENT_TOPO_CHANGE);
153                 break;
154         default:
155                 break;
156         }
157 }
158 
159 static void
160 vlan_config_init(struct device *dev)
161 {
162         struct vlan_device *vldev;
163 
164         vldev = container_of(dev, struct vlan_device, dev);
165         vlan_hotplug_check(vldev, vldev->dep.dev);
166 }
167 
168 static struct device *get_vlan_device(struct device *dev, char *id_str, bool create)
169 {
170         static struct device_type vlan_type = {
171                 .name = "VLAN",
172                 .config_params = &device_attr_list,
173                 .config_init = vlan_config_init,
174                 .free = free_vlan_if,
175         };
176         struct vlan_device *vldev;
177         struct device_user *dep;
178         char name[IFNAMSIZ + 1];
179         char *err = NULL;
180         int id, *alias_id;
181 
182         id = strtoul(id_str, &err, 10);
183         if (err && *err) {
184                 alias_id = kvlist_get(&dev->vlan_aliases, id_str);
185                 if (!alias_id)
186                         return NULL;
187 
188                 id = *alias_id;
189         }
190 
191         /* look for an existing interface before creating a new one */
192         list_for_each_entry(dep, &dev->users.list, list.list) {
193                 if (dep->cb != vlan_dev_cb)
194                         continue;
195 
196                 vldev = container_of(dep, struct vlan_device, dep);
197                 if (vldev->id != id)
198                         continue;
199 
200                 return &vldev->dev;
201         }
202 
203         if (!create)
204                 return NULL;
205 
206         if (snprintf(name, sizeof(name), "%s.%d", dev->ifname, id) >= sizeof(name) - 1)
207                 return NULL;
208 
209         D(DEVICE, "Create vlan device '%s'\n", name);
210 
211         vldev = calloc(1, sizeof(*vldev));
212         if (!vldev)
213                 return NULL;
214 
215         vldev->id = id;
216         vldev->dev.hidden = dev->hidden;
217         strcpy(vldev->dev.ifname, name);
218 
219         if (device_init(&vldev->dev, &vlan_type, NULL) < 0)
220                 goto error;
221 
222         vldev->dev.default_config = true;
223         vldev->dev.config_pending = true;
224 
225         vldev->set_state = vldev->dev.set_state;
226         vldev->dev.set_state = vlan_set_device_state;
227 
228         vldev->dep.cb = vlan_dev_cb;
229         vlan_hotplug_check(vldev, vldev->dep.dev);
230         device_add_user(&vldev->dep, dev);
231 
232         return &vldev->dev;
233 
234 error:
235         device_cleanup(&vldev->dev);
236         free(vldev);
237         return NULL;
238 }
239 
240 static char *split_vlan(char *s)
241 {
242         s = strchr(s, '.');
243         if (!s)
244                 return NULL;
245 
246         *s = 0;
247         s++;
248 
249         return s;
250 }
251 
252 struct device *get_vlan_device_chain(const char *ifname, int create)
253 {
254         struct device *dev = NULL, *vldev;
255         char *buf, *s, *next;
256 
257         buf = strdup(ifname);
258         if (!buf)
259                 return NULL;
260 
261         s = split_vlan(buf);
262         dev = __device_get(buf, create, false);
263         if (!dev || !s)
264                 goto out;
265 
266         /* for the first split, we need to check if we're using an alias or
267          * if the . separator isn't being used as a vlan separator (e.g. for
268          * AP WDS VLANs */
269         if (!isdigit(s[0])) {
270                 next = split_vlan(s);
271                 vldev = get_vlan_device(dev, s, create);
272                 if (!vldev) {
273                         s[-1] = '.';
274                         dev = __device_get(buf, create, false);
275                         if (!dev)
276                                 goto out;
277 
278                         if (next)
279                                 next[-1] = '.';
280                 } else {
281                         dev = vldev;
282                         s = next;
283                 }
284         }
285 
286 
287         while (s) {
288                 next = split_vlan(s);
289                 dev = get_vlan_device(dev, s, create);
290                 if (!dev)
291                         break;
292 
293                 s = next;
294         }
295 
296 out:
297         free(buf);
298         return dev;
299 }
300 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt