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