2 * netifd - network interface daemon
3 * Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
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
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.
22 static struct blob_buf b
;
26 struct device_user dep
;
28 device_state_cb set_state
;
32 static void free_vlan_if(struct device
*iface
)
34 struct vlan_device
*vldev
;
36 vldev
= container_of(iface
, struct vlan_device
, dev
);
37 device_remove_user(&vldev
->dep
);
38 device_cleanup(&vldev
->dev
);
43 __vlan_hotplug_op(struct device
*dev
, struct device
*member
, struct blob_attr
*vlan
, bool add
)
45 struct vlan_device
*vldev
= container_of(dev
, struct vlan_device
, dev
);
49 if (!dev
|| !dev
->hotplug_ops
)
50 return UBUS_STATUS_NOT_SUPPORTED
;
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
);
60 return dev
->hotplug_ops
->add(dev
, member
, blobmsg_data(b
.head
));
62 return dev
->hotplug_ops
->del(dev
, member
, blobmsg_data(b
.head
));
66 vlan_hotplug_add(struct device
*dev
, struct device
*member
, struct blob_attr
*vlan
)
68 return __vlan_hotplug_op(dev
, member
, vlan
, true);
72 vlan_hotplug_del(struct device
*dev
, struct device
*member
, struct blob_attr
*vlan
)
74 return __vlan_hotplug_op(dev
, member
, vlan
, false);
78 vlan_hotplug_prepare(struct device
*dev
, struct device
**bridge_dev
)
80 struct vlan_device
*vldev
= container_of(dev
, struct vlan_device
, dev
);
83 if (!dev
|| !dev
->hotplug_ops
)
84 return UBUS_STATUS_NOT_SUPPORTED
;
86 return dev
->hotplug_ops
->prepare(dev
, bridge_dev
);
89 static void vlan_hotplug_check(struct vlan_device
*vldev
, struct device
*dev
)
91 static const struct device_hotplug_ops hotplug_ops
= {
92 .prepare
= vlan_hotplug_prepare
,
93 .add
= vlan_hotplug_add
,
94 .del
= vlan_hotplug_del
97 if (!dev
|| !dev
->hotplug_ops
|| avl_is_empty(&dev
->vlans
.avl
)) {
98 vldev
->dev
.hotplug_ops
= NULL
;
102 vldev
->dev
.hotplug_ops
= &hotplug_ops
;
106 static int vlan_set_device_state(struct device
*dev
, bool up
)
108 struct vlan_device
*vldev
;
111 vldev
= container_of(dev
, struct vlan_device
, dev
);
113 vldev
->set_state(dev
, false);
114 system_vlan_del(dev
);
115 device_release(&vldev
->dep
);
119 ret
= device_claim(&vldev
->dep
);
123 system_vlan_add(vldev
->dep
.dev
, vldev
->id
);
124 ret
= vldev
->set_state(dev
, true);
126 device_release(&vldev
->dep
);
131 static void vlan_dev_cb(struct device_user
*dep
, enum device_event ev
)
134 struct vlan_device
*vldev
;
136 vldev
= container_of(dep
, struct vlan_device
, dep
);
139 device_set_present(&vldev
->dev
, true);
141 case DEV_EVENT_REMOVE
:
142 device_set_present(&vldev
->dev
, false);
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
);
152 case DEV_EVENT_TOPO_CHANGE
:
153 /* Propagate topo changes */
154 device_broadcast_event(&vldev
->dev
, DEV_EVENT_TOPO_CHANGE
);
162 vlan_config_init(struct device
*dev
)
164 struct vlan_device
*vldev
;
166 vldev
= container_of(dev
, struct vlan_device
, dev
);
167 vlan_hotplug_check(vldev
, vldev
->dep
.dev
);
170 static struct device
*get_vlan_device(struct device
*dev
, char *id_str
, bool create
)
172 static struct device_type vlan_type
= {
174 .config_params
= &device_attr_list
,
175 .config_init
= vlan_config_init
,
176 .free
= free_vlan_if
,
178 struct vlan_device
*vldev
;
179 struct device_user
*dep
;
184 id
= strtoul(id_str
, &err
, 10);
186 alias_id
= kvlist_get(&dev
->vlan_aliases
, id_str
);
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
)
198 vldev
= container_of(dep
, struct vlan_device
, dep
);
208 if (snprintf(name
, sizeof(name
), "%s.%d", dev
->ifname
, id
) >= (int)sizeof(name
) - 1)
211 D(DEVICE
, "Create vlan device '%s'\n", name
);
213 vldev
= calloc(1, sizeof(*vldev
));
218 vldev
->dev
.hidden
= dev
->hidden
;
219 strcpy(vldev
->dev
.ifname
, name
);
221 if (device_init(&vldev
->dev
, &vlan_type
, NULL
) < 0)
224 vldev
->dev
.default_config
= true;
225 vldev
->dev
.config_pending
= true;
227 vldev
->set_state
= vldev
->dev
.set_state
;
228 vldev
->dev
.set_state
= vlan_set_device_state
;
230 vldev
->dep
.cb
= vlan_dev_cb
;
231 vlan_hotplug_check(vldev
, vldev
->dep
.dev
);
232 device_add_user(&vldev
->dep
, dev
);
237 device_cleanup(&vldev
->dev
);
242 static char *split_vlan(char *s
)
254 struct device
*get_vlan_device_chain(const char *ifname
, int create
)
256 struct device
*dev
= NULL
, *vldev
;
257 char *buf
, *s
, *next
;
259 buf
= strdup(ifname
);
264 dev
= __device_get(buf
, create
, false);
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
271 if (!isdigit(s
[0])) {
272 next
= split_vlan(s
);
273 vldev
= get_vlan_device(dev
, s
, create
);
276 dev
= __device_get(buf
, create
, false);
290 next
= split_vlan(s
);
291 dev
= get_vlan_device(dev
, s
, create
);