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