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 <errno.h> 15 #include <string.h> 16 #include <stdlib.h> 17 #include <stdio.h> 18 #include <ctype.h> 19 20 #include "netifd.h" 21 #include "system.h" 22 23 struct vlan_device { 24 struct device dev; 25 struct device_user dep; 26 27 device_state_cb set_state; 28 int id; 29 }; 30 31 static void free_vlan_if(struct device *iface) 32 { 33 struct vlan_device *vldev; 34 35 vldev = container_of(iface, struct vlan_device, dev); 36 device_remove_user(&vldev->dep); 37 device_cleanup(&vldev->dev); 38 free(vldev); 39 } 40 41 static int 42 __vlan_hotplug_op(struct device *dev, struct device *member, struct blob_attr *vlan, bool add) 43 { 44 struct vlan_device *vldev = container_of(dev, struct vlan_device, dev); 45 /* local buffer: chained vlan devices re-enter this function with 46 * the vlan attribute pointing into the caller's buffer */ 47 struct blob_buf b = {}; 48 void *a; 49 int ret; 50 51 dev = vldev->dep.dev; 52 if (!dev || !dev->hotplug_ops) 53 return UBUS_STATUS_NOT_SUPPORTED; 54 55 blob_buf_init(&b, 0); 56 a = blobmsg_open_array(&b, "vlans"); 57 blobmsg_printf(&b, NULL, "%d:u", vldev->id); 58 if (vlan && blobmsg_len(vlan)) 59 blob_put_raw(&b, blobmsg_data(vlan), blobmsg_len(vlan)); 60 blobmsg_close_array(&b, a); 61 62 if (add) 63 ret = dev->hotplug_ops->add(dev, member, blobmsg_data(b.head)); 64 else 65 ret = dev->hotplug_ops->del(dev, member, blobmsg_data(b.head)); 66 67 blob_buf_free(&b); 68 return ret; 69 } 70 71 static int 72 vlan_hotplug_add(struct device *dev, struct device *member, struct blob_attr *vlan) 73 { 74 return __vlan_hotplug_op(dev, member, vlan, true); 75 } 76 77 static int 78 vlan_hotplug_del(struct device *dev, struct device *member, struct blob_attr *vlan) 79 { 80 return __vlan_hotplug_op(dev, member, vlan, false); 81 } 82 83 static int 84 vlan_hotplug_prepare(struct device *dev, struct device **bridge_dev) 85 { 86 struct vlan_device *vldev = container_of(dev, struct vlan_device, dev); 87 88 dev = vldev->dep.dev; 89 if (!dev || !dev->hotplug_ops) 90 return UBUS_STATUS_NOT_SUPPORTED; 91 92 return dev->hotplug_ops->prepare(dev, bridge_dev); 93 } 94 95 static void vlan_hotplug_check(struct vlan_device *vldev, struct device *dev) 96 { 97 static const struct device_hotplug_ops hotplug_ops = { 98 .prepare = vlan_hotplug_prepare, 99 .add = vlan_hotplug_add, 100 .del = vlan_hotplug_del 101 }; 102 103 if (!dev || !dev->hotplug_ops || avl_is_empty(&dev->vlans.avl)) { 104 vldev->dev.hotplug_ops = NULL; 105 return; 106 } 107 108 vldev->dev.hotplug_ops = &hotplug_ops; 109 } 110 111 112 static int vlan_set_device_state(struct device *dev, bool up) 113 { 114 struct vlan_device *vldev; 115 int ret = 0; 116 117 vldev = container_of(dev, struct vlan_device, dev); 118 if (!up) { 119 vldev->set_state(dev, false); 120 system_vlan_del(dev); 121 device_release(&vldev->dep); 122 return 0; 123 } 124 125 ret = device_claim(&vldev->dep); 126 if (ret < 0) 127 return ret; 128 129 /* tolerate a vlan device left behind by a previous netifd run */ 130 if (system_vlan_add(vldev->dep.dev, vldev->id) < 0 && 131 errno != EEXIST) { 132 ret = -1; 133 goto release; 134 } 135 136 ret = vldev->set_state(dev, true); 137 if (ret) 138 goto delete; 139 140 return 0; 141 142 delete: 143 system_vlan_del(dev); 144 release: 145 device_release(&vldev->dep); 146 return ret; 147 } 148 149 static void vlan_dev_cb(struct device_user *dep, enum device_event ev) 150 { 151 char name[IFNAMSIZ]; 152 struct vlan_device *vldev; 153 154 vldev = container_of(dep, struct vlan_device, dep); 155 switch(ev) { 156 case DEV_EVENT_ADD: 157 device_set_present(&vldev->dev, true); 158 break; 159 case DEV_EVENT_REMOVE: 160 device_set_present(&vldev->dev, false); 161 break; 162 case DEV_EVENT_UPDATE_IFNAME: 163 vlan_hotplug_check(vldev, dep->dev); 164 vldev->dev.hidden = dep->dev->hidden; 165 if (snprintf(name, sizeof(name), "%s.%d", dep->dev->ifname, 166 vldev->id) < (int)sizeof(name) && 167 !device_set_ifname(&vldev->dev, name)) 168 break; 169 170 /* freeing the device here would leave dangling device_user 171 * entries behind; detach it and let device_free_unused() 172 * reap it once the last user is gone */ 173 device_set_present(&vldev->dev, false); 174 device_remove_user(&vldev->dep); 175 device_free_unused(); 176 break; 177 case DEV_EVENT_VLAN_UPDATE: 178 vlan_hotplug_check(vldev, dep->dev); 179 break; 180 case DEV_EVENT_TOPO_CHANGE: 181 /* Propagate topo changes */ 182 device_broadcast_event(&vldev->dev, DEV_EVENT_TOPO_CHANGE); 183 break; 184 default: 185 break; 186 } 187 } 188 189 static void 190 vlan_config_init(struct device *dev) 191 { 192 struct vlan_device *vldev; 193 194 vldev = container_of(dev, struct vlan_device, dev); 195 vlan_hotplug_check(vldev, vldev->dep.dev); 196 } 197 198 static struct device *get_vlan_device(struct device *dev, char *id_str, bool create) 199 { 200 static struct device_type vlan_type = { 201 .name = "VLAN", 202 .config_params = &device_attr_list, 203 .config_init = vlan_config_init, 204 .free = free_vlan_if, 205 }; 206 struct vlan_device *vldev; 207 struct device_user *dep; 208 char name[IFNAMSIZ]; 209 char *err = NULL; 210 int id, *alias_id; 211 212 id = strtoul(id_str, &err, 10); 213 if (err && *err) { 214 alias_id = kvlist_get(&dev->vlan_aliases, id_str); 215 if (!alias_id) 216 return NULL; 217 218 id = *alias_id; 219 } 220 221 /* look for an existing interface before creating a new one */ 222 list_for_each_entry(dep, &dev->users.list, list.list) { 223 if (dep->cb != vlan_dev_cb) 224 continue; 225 226 vldev = container_of(dep, struct vlan_device, dep); 227 if (vldev->id != id) 228 continue; 229 230 return &vldev->dev; 231 } 232 233 if (!create) 234 return NULL; 235 236 if (snprintf(name, sizeof(name), "%s.%d", dev->ifname, id) >= (int)sizeof(name)) 237 return NULL; 238 239 D(DEVICE, "Create vlan device '%s'", name); 240 241 vldev = calloc(1, sizeof(*vldev)); 242 if (!vldev) 243 return NULL; 244 245 vldev->id = id; 246 vldev->dev.hidden = dev->hidden; 247 strcpy(vldev->dev.ifname, name); 248 249 if (device_init(&vldev->dev, &vlan_type, NULL) < 0) 250 goto error; 251 252 vldev->dev.default_config = true; 253 vldev->dev.config_pending = true; 254 255 vldev->set_state = vldev->dev.set_state; 256 vldev->dev.set_state = vlan_set_device_state; 257 258 vldev->dep.cb = vlan_dev_cb; 259 vlan_hotplug_check(vldev, vldev->dep.dev); 260 device_add_user(&vldev->dep, dev); 261 262 return &vldev->dev; 263 264 error: 265 device_cleanup(&vldev->dev); 266 free(vldev); 267 return NULL; 268 } 269 270 static char *split_vlan(char *s) 271 { 272 s = strchr(s, '.'); 273 if (!s) 274 return NULL; 275 276 *s = 0; 277 s++; 278 279 return s; 280 } 281 282 struct device *get_vlan_device_chain(const char *ifname, int create) 283 { 284 struct device *dev = NULL, *vldev; 285 char *buf, *s, *next; 286 287 buf = strdup(ifname); 288 if (!buf) 289 return NULL; 290 291 s = split_vlan(buf); 292 dev = __device_get(buf, create, false); 293 if (!dev || !s) 294 goto out; 295 296 /* for the first split, we need to check if we're using an alias or 297 * if the . separator isn't being used as a vlan separator (e.g. for 298 * AP WDS VLANs */ 299 if (!isdigit(s[0])) { 300 next = split_vlan(s); 301 vldev = get_vlan_device(dev, s, create); 302 if (!vldev) { 303 s[-1] = '.'; 304 dev = __device_get(buf, create, false); 305 if (!dev) 306 goto out; 307 308 if (next) 309 next[-1] = '.'; 310 s = next; 311 } else { 312 dev = vldev; 313 s = next; 314 } 315 } 316 317 318 while (s) { 319 next = split_vlan(s); 320 dev = get_vlan_device(dev, s, create); 321 if (!dev) 322 break; 323 324 s = next; 325 } 326 327 out: 328 free(buf); 329 return dev; 330 } 331
This page was automatically generated by LXR 0.3.1. • OpenWrt