• 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:u", vldev->id);
 55         if (vlan && blobmsg_len(vlan))
 56                 blob_put_raw(&b, blobmsg_data(vlan), blobmsg_len(vlan));
 57         blobmsg_close_array(&b, a);
 58 
 59         if (add)
 60                 return dev->hotplug_ops->add(dev, member, blobmsg_data(b.head));
 61         else
 62                 return dev->hotplug_ops->del(dev, member, blobmsg_data(b.head));
 63 }
 64 
 65 static int
 66 vlan_hotplug_add(struct device *dev, struct device *member, struct blob_attr *vlan)
 67 {
 68         return __vlan_hotplug_op(dev, member, vlan, true);
 69 }
 70 
 71 static int
 72 vlan_hotplug_del(struct device *dev, struct device *member, struct blob_attr *vlan)
 73 {
 74         return __vlan_hotplug_op(dev, member, vlan, false);
 75 }
 76 
 77 static int
 78 vlan_hotplug_prepare(struct device *dev, struct device **bridge_dev)
 79 {
 80         struct vlan_device *vldev = container_of(dev, struct vlan_device, dev);
 81 
 82         dev = vldev->dep.dev;
 83         if (!dev || !dev->hotplug_ops)
 84                 return UBUS_STATUS_NOT_SUPPORTED;
 85 
 86         return dev->hotplug_ops->prepare(dev, bridge_dev);
 87 }
 88 
 89 static void vlan_hotplug_check(struct vlan_device *vldev, struct device *dev)
 90 {
 91         static const struct device_hotplug_ops hotplug_ops = {
 92                 .prepare = vlan_hotplug_prepare,
 93                 .add = vlan_hotplug_add,
 94                 .del = vlan_hotplug_del
 95         };
 96 
 97         if (!dev || !dev->hotplug_ops || avl_is_empty(&dev->vlans.avl)) {
 98                 vldev->dev.hotplug_ops = NULL;
 99                 return;
100         }
101 
102         vldev->dev.hotplug_ops = &hotplug_ops;
103 }
104 
105 
106 static int vlan_set_device_state(struct device *dev, bool up)
107 {
108         struct vlan_device *vldev;
109         int ret = 0;
110 
111         vldev = container_of(dev, struct vlan_device, dev);
112         if (!up) {
113                 vldev->set_state(dev, false);
114                 system_vlan_del(dev);
115                 device_release(&vldev->dep);
116                 return 0;
117         }
118 
119         ret = device_claim(&vldev->dep);
120         if (ret < 0)
121                 return ret;
122 
123         system_vlan_add(vldev->dep.dev, vldev->id);
124         ret = vldev->set_state(dev, true);
125         if (ret)
126                 device_release(&vldev->dep);
127 
128         return ret;
129 }
130 
131 static void vlan_dev_cb(struct device_user *dep, enum device_event ev)
132 {
133         char name[IFNAMSIZ];
134         struct vlan_device *vldev;
135 
136         vldev = container_of(dep, struct vlan_device, dep);
137         switch(ev) {
138         case DEV_EVENT_ADD:
139                 device_set_present(&vldev->dev, true);
140                 break;
141         case DEV_EVENT_REMOVE:
142                 device_set_present(&vldev->dev, false);
143                 break;
144         case DEV_EVENT_UPDATE_IFNAME:
145                 vlan_hotplug_check(vldev, dep->dev);
146                 vldev->dev.hidden = dep->dev->hidden;
147                 if (snprintf(name, sizeof(name), "%s.%d", dep->dev->ifname,
148                              vldev->id) >= (int)sizeof(name) - 1 ||
149                     device_set_ifname(&vldev->dev, name))
150                         free_vlan_if(&vldev->dev);
151                 break;
152         case DEV_EVENT_TOPO_CHANGE:
153                 /* Propagate topo changes */
154                 device_broadcast_event(&vldev->dev, DEV_EVENT_TOPO_CHANGE);
155                 break;
156         default:
157                 break;
158         }
159 }
160 
161 static void
162 vlan_config_init(struct device *dev)
163 {
164         struct vlan_device *vldev;
165 
166         vldev = container_of(dev, struct vlan_device, dev);
167         vlan_hotplug_check(vldev, vldev->dep.dev);
168 }
169 
170 static struct device *get_vlan_device(struct device *dev, char *id_str, bool create)
171 {
172         static struct device_type vlan_type = {
173                 .name = "VLAN",
174                 .config_params = &device_attr_list,
175                 .config_init = vlan_config_init,
176                 .free = free_vlan_if,
177         };
178         struct vlan_device *vldev;
179         struct device_user *dep;
180         char name[IFNAMSIZ];
181         char *err = NULL;
182         int id, *alias_id;
183 
184         id = strtoul(id_str, &err, 10);
185         if (err && *err) {
186                 alias_id = kvlist_get(&dev->vlan_aliases, id_str);
187                 if (!alias_id)
188                         return NULL;
189 
190                 id = *alias_id;
191         }
192 
193         /* look for an existing interface before creating a new one */
194         list_for_each_entry(dep, &dev->users.list, list.list) {
195                 if (dep->cb != vlan_dev_cb)
196                         continue;
197 
198                 vldev = container_of(dep, struct vlan_device, dep);
199                 if (vldev->id != id)
200                         continue;
201 
202                 return &vldev->dev;
203         }
204 
205         if (!create)
206                 return NULL;
207 
208         if (snprintf(name, sizeof(name), "%s.%d", dev->ifname, id) >= (int)sizeof(name) - 1)
209                 return NULL;
210 
211         D(DEVICE, "Create vlan device '%s'", name);
212 
213         vldev = calloc(1, sizeof(*vldev));
214         if (!vldev)
215                 return NULL;
216 
217         vldev->id = id;
218         vldev->dev.hidden = dev->hidden;
219         strcpy(vldev->dev.ifname, name);
220 
221         if (device_init(&vldev->dev, &vlan_type, NULL) < 0)
222                 goto error;
223 
224         vldev->dev.default_config = true;
225         vldev->dev.config_pending = true;
226 
227         vldev->set_state = vldev->dev.set_state;
228         vldev->dev.set_state = vlan_set_device_state;
229 
230         vldev->dep.cb = vlan_dev_cb;
231         vlan_hotplug_check(vldev, vldev->dep.dev);
232         device_add_user(&vldev->dep, dev);
233 
234         return &vldev->dev;
235 
236 error:
237         device_cleanup(&vldev->dev);
238         free(vldev);
239         return NULL;
240 }
241 
242 static char *split_vlan(char *s)
243 {
244         s = strchr(s, '.');
245         if (!s)
246                 return NULL;
247 
248         *s = 0;
249         s++;
250 
251         return s;
252 }
253 
254 struct device *get_vlan_device_chain(const char *ifname, int create)
255 {
256         struct device *dev = NULL, *vldev;
257         char *buf, *s, *next;
258 
259         buf = strdup(ifname);
260         if (!buf)
261                 return NULL;
262 
263         s = split_vlan(buf);
264         dev = __device_get(buf, create, false);
265         if (!dev || !s)
266                 goto out;
267 
268         /* for the first split, we need to check if we're using an alias or
269          * if the . separator isn't being used as a vlan separator (e.g. for
270          * AP WDS VLANs */
271         if (!isdigit(s[0])) {
272                 next = split_vlan(s);
273                 vldev = get_vlan_device(dev, s, create);
274                 if (!vldev) {
275                         s[-1] = '.';
276                         dev = __device_get(buf, create, false);
277                         if (!dev)
278                                 goto out;
279 
280                         if (next)
281                                 next[-1] = '.';
282                 } else {
283                         dev = vldev;
284                         s = next;
285                 }
286         }
287 
288 
289         while (s) {
290                 next = split_vlan(s);
291                 dev = get_vlan_device(dev, s, create);
292                 if (!dev)
293                         break;
294 
295                 s = next;
296         }
297 
298 out:
299         free(buf);
300         return dev;
301 }
302 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt