This can be used to support 802.1x on wired devices.
In order to use this, the device section for each port needing authentication
needs to contain the option auth 1
When set, this option prevents devices from being added to bridges or configured
with IP settings by default, until the set_state ubus call on network.device
sets "auth_status" to true for the device.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
struct device_user dev;
uint16_t pvid;
bool present;
+ bool active;
char name[];
};
}
static int
-bridge_disable_member(struct bridge_member *bm)
+bridge_disable_member(struct bridge_member *bm, bool keep_dev)
{
struct bridge_state *bst = bm->bst;
struct bridge_vlan *vlan;
- if (!bm->present)
+ if (!bm->present || !bm->active)
return 0;
+ bm->active = false;
vlist_for_each_element(&bst->dev.vlans, vlan, node)
bridge_set_member_vlan(bm, vlan, false);
system_bridge_delif(&bst->dev, bm->dev.dev);
- device_release(&bm->dev);
+ if (!keep_dev)
+ device_release(&bm->dev);
device_broadcast_event(&bst->dev, DEV_EVENT_TOPO_CHANGE);
{
struct bridge_state *bst = bm->bst;
struct bridge_vlan *vlan;
+ struct device *dev;
int ret;
if (!bm->present)
if (ret < 0)
goto error;
+ dev = bm->dev.dev;
+ if (dev->settings.auth && !dev->auth_status)
+ return -1;
+
+ if (bm->active)
+ return 0;
+
ret = system_bridge_addif(&bst->dev, bm->dev.dev);
if (ret < 0) {
D(DEVICE, "Bridge device %s could not be added\n", bm->dev.dev->ifname);
goto error;
}
+ bm->active = true;
if (bst->has_vlans) {
/* delete default VLAN 1 */
system_bridge_vlan(bm->dev.dev->ifname, 1, false, 0);
return;
if (bst->dev.active)
- bridge_disable_member(bm);
+ bridge_disable_member(bm, false);
bm->present = false;
bm->bst->n_present--;
}
static void
-bridge_member_cb(struct device_user *dev, enum device_event ev)
+bridge_member_cb(struct device_user *dep, enum device_event ev)
{
- struct bridge_member *bm = container_of(dev, struct bridge_member, dev);
+ struct bridge_member *bm = container_of(dep, struct bridge_member, dev);
struct bridge_state *bst = bm->bst;
+ struct device *dev = dep->dev;
switch (ev) {
case DEV_EVENT_ADD:
if (bst->n_present == 1)
device_set_present(&bst->dev, true);
- if (bst->dev.active && !bridge_enable_member(bm)) {
- /*
- * Adding a bridge member can overwrite the bridge mtu
- * in the kernel, apply the bridge settings in case the
- * bridge mtu is set
- */
- system_if_apply_settings(&bst->dev, &bst->dev.settings,
- DEV_OPT_MTU | DEV_OPT_MTU6);
- }
+ fallthrough;
+ case DEV_EVENT_AUTH_UP:
+ if (!bst->dev.active)
+ break;
+
+ if (bridge_enable_member(bm))
+ break;
+
+ /*
+ * Adding a bridge member can overwrite the bridge mtu
+ * in the kernel, apply the bridge settings in case the
+ * bridge mtu is set
+ */
+ system_if_apply_settings(&bst->dev, &bst->dev.settings,
+ DEV_OPT_MTU | DEV_OPT_MTU6);
+ break;
+ case DEV_EVENT_LINK_DOWN:
+ if (!dev->settings.auth)
+ break;
+ bridge_disable_member(bm, true);
break;
case DEV_EVENT_REMOVE:
- if (dev->hotplug) {
+ if (dep->hotplug) {
vlist_delete(&bst->members, &bm->node);
return;
}
bst->set_state(&bst->dev, false);
vlist_for_each_element(&bst->members, bm, node)
- bridge_disable_member(bm);
+ bridge_disable_member(bm, false);
bridge_disable_interface(bst);
[DEV_ATTR_DROP_GRATUITOUS_ARP] = { .name = "drop_gratuitous_arp", .type = BLOBMSG_TYPE_BOOL },
[DEV_ATTR_DROP_UNSOLICITED_NA] = { .name = "drop_unsolicited_na", .type = BLOBMSG_TYPE_BOOL },
[DEV_ATTR_ARP_ACCEPT] = { .name = "arp_accept", .type = BLOBMSG_TYPE_BOOL },
+ [DEV_ATTR_AUTH] = { .name = "auth", .type = BLOBMSG_TYPE_BOOL },
};
const struct uci_blob_param_list device_attr_list = {
s->drop_unsolicited_na : os->drop_unsolicited_na;
n->arp_accept = s->flags & DEV_OPT_ARP_ACCEPT ?
s->arp_accept : os->arp_accept;
+ n->auth = s->flags & DEV_OPT_AUTH ? s->auth : os->auth;
n->flags = s->flags | os->flags | os->valid_flags;
}
s->flags |= DEV_OPT_ARP_ACCEPT;
}
+ if ((cur = tb[DEV_ATTR_AUTH])) {
+ s->auth = blobmsg_get_bool(cur);
+ s->flags |= DEV_OPT_AUTH;
+ }
+
device_set_disabled(dev, disabled);
}
__device_set_present(dev, state);
}
+void
+device_set_auth_status(struct device *dev, bool value)
+{
+ if (dev->auth_status == value)
+ return;
+
+ dev->auth_status = value;
+ if (!dev->present)
+ return;
+
+ if (dev->auth_status) {
+ device_broadcast_event(dev, DEV_EVENT_AUTH_UP);
+ return;
+ }
+
+ device_broadcast_event(dev, DEV_EVENT_LINK_DOWN);
+ if (!dev->link_active)
+ return;
+
+ device_broadcast_event(dev, DEV_EVENT_LINK_UP);
+}
+
void device_set_present(struct device *dev, bool state)
{
if (dev->sys_present == state)
netifd_log_message(L_NOTICE, "%s '%s' link is %s\n", dev->type->name, dev->ifname, state ? "up" : "down" );
dev->link_active = state;
+ if (!state)
+ dev->auth_status = false;
device_broadcast_event(dev, state ? DEV_EVENT_LINK_UP : DEV_EVENT_LINK_DOWN);
}
blobmsg_add_u8(b, "up", !!dev->active);
blobmsg_add_u8(b, "carrier", !!dev->link_active);
+ blobmsg_add_u8(b, "auth_status", !!dev->auth_status);
if (dev->type->dump_info)
dev->type->dump_info(dev, b);
blobmsg_add_u8(b, "drop_unsolicited_na", st.drop_unsolicited_na);
if (st.flags & DEV_OPT_ARP_ACCEPT)
blobmsg_add_u8(b, "arp_accept", st.arp_accept);
+ if (st.flags & DEV_OPT_AUTH)
+ blobmsg_add_u8(b, "auth", st.auth);
}
s = blobmsg_open_table(b, "statistics");
DEV_ATTR_DROP_GRATUITOUS_ARP,
DEV_ATTR_DROP_UNSOLICITED_NA,
DEV_ATTR_ARP_ACCEPT,
+ DEV_ATTR_AUTH,
__DEV_ATTR_MAX,
};
DEV_OPT_MLDVERSION = (1 << 8),
DEV_OPT_NEIGHREACHABLETIME = (1 << 9),
DEV_OPT_DEFAULT_MACADDR = (1 << 10),
- /* 1 bit hole */
+ DEV_OPT_AUTH = (1 << 11),
DEV_OPT_MTU6 = (1 << 12),
DEV_OPT_DADTRANSMITS = (1 << 13),
DEV_OPT_MULTICAST_TO_UNICAST = (1 << 14),
DEV_EVENT_UP,
DEV_EVENT_DOWN,
+ DEV_EVENT_AUTH_UP,
DEV_EVENT_LINK_UP,
DEV_EVENT_LINK_DOWN,
bool drop_gratuitous_arp;
bool drop_unsolicited_na;
bool arp_accept;
+ bool auth;
};
/*
int active;
/* DEV_EVENT_LINK_UP */
bool link_active;
+ bool auth_status;
bool external;
bool disabled;
void alias_notify_device(const char *name, struct device *dev);
struct device *device_alias_get(const char *name);
+void device_set_auth_status(struct device *dev, bool value);
+
static inline void
device_set_deferred(struct device *dev, bool value)
{
device_refresh_present(dev);
}
+static inline bool
+device_link_active(struct device *dev)
+{
+ if (dev->settings.auth && !dev->auth_status)
+ return false;
+
+ return dev->link_active;
+}
+
bool device_check_ip6segmentrouting(void);
#endif
}
}
+static bool
+interface_force_link(struct interface *iface)
+{
+ struct device *dev = iface->main_dev.dev;
+
+ if (dev && dev->settings.auth)
+ return false;
+
+ return iface->force_link;
+}
+
static void
interface_clear_errors(struct interface *iface)
{
static void
interface_check_state(struct interface *iface)
{
- bool link_state = iface->link_state || iface->force_link;
+ bool link_state = iface->link_state || interface_force_link(iface);
switch (iface->state) {
case IFS_UP:
iface->link_state = new_state;
interface_check_state(iface);
- if (new_state && iface->force_link && iface->state == IFS_UP && !iface->link_up_event) {
+ if (new_state && interface_force_link(iface) &&
+ iface->state == IFS_UP && !iface->link_up_event) {
interface_event(iface, IFEV_LINK_UP);
iface->link_up_event = true;
}
case DEV_EVENT_DOWN:
interface_set_enabled(iface, false);
break;
+ case DEV_EVENT_AUTH_UP:
case DEV_EVENT_LINK_UP:
- interface_set_link_state(iface, true);
- break;
case DEV_EVENT_LINK_DOWN:
- interface_set_link_state(iface, false);
+ interface_set_link_state(iface, device_link_active(dep->dev));
break;
case DEV_EVENT_TOPO_CHANGE:
interface_proto_event(iface->proto, PROTO_CMD_RENEW, false);
enum {
DEV_STATE_NAME,
DEV_STATE_DEFER,
+ DEV_STATE_AUTH_STATUS,
__DEV_STATE_MAX,
};
static const struct blobmsg_policy dev_state_policy[__DEV_STATE_MAX] = {
[DEV_STATE_NAME] = { .name = "name", .type = BLOBMSG_TYPE_STRING },
[DEV_STATE_DEFER] = { .name = "defer", .type = BLOBMSG_TYPE_BOOL },
+ [DEV_STATE_AUTH_STATUS] = { .name = "auth_status", .type = BLOBMSG_TYPE_BOOL },
};
static int
if (cur)
device_set_deferred(dev, !!blobmsg_get_u8(cur));
+ cur = tb[DEV_STATE_AUTH_STATUS];
+ if (cur)
+ device_set_auth_status(dev, !!blobmsg_get_u8(cur));
+
return 0;
}