From: Felix Fietkau Date: Mon, 17 May 2021 09:20:09 +0000 (+0200) Subject: device: add support for configuring devices with external auth handler X-Git-Url: http://git.openwrt.org/?p=project%2Fnetifd.git;a=commitdiff_plain;h=2a97fd006c3b09c1aeab30ac881c9ac902365d57 device: add support for configuring devices with external auth handler 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 --- diff --git a/bridge.c b/bridge.c index 099dfe4..397ac97 100644 --- a/bridge.c +++ b/bridge.c @@ -122,6 +122,7 @@ struct bridge_member { struct device_user dev; uint16_t pvid; bool present; + bool active; char name[]; }; @@ -299,19 +300,21 @@ bridge_set_vlan_state(struct bridge_state *bst, struct bridge_vlan *vlan, bool a } 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); @@ -356,6 +359,7 @@ bridge_enable_member(struct bridge_member *bm) { struct bridge_state *bst = bm->bst; struct bridge_vlan *vlan; + struct device *dev; int ret; if (!bm->present) @@ -375,12 +379,20 @@ bridge_enable_member(struct bridge_member *bm) 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); @@ -412,7 +424,7 @@ bridge_remove_member(struct bridge_member *bm) return; if (bst->dev.active) - bridge_disable_member(bm); + bridge_disable_member(bm, false); bm->present = false; bm->bst->n_present--; @@ -481,10 +493,11 @@ bridge_check_retry(struct bridge_state *bst) } 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: @@ -495,19 +508,30 @@ bridge_member_cb(struct device_user *dev, enum device_event ev) 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; } @@ -529,7 +553,7 @@ bridge_set_down(struct bridge_state *bst) 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); diff --git a/device.c b/device.c index 7f011b6..26254cc 100644 --- a/device.c +++ b/device.c @@ -59,6 +59,7 @@ static const struct blobmsg_policy dev_attrs[__DEV_ATTR_MAX] = { [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 = { @@ -270,6 +271,7 @@ device_merge_settings(struct device *dev, struct device_settings *n) 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; } @@ -439,6 +441,11 @@ device_init_settings(struct device *dev, struct blob_attr **tb) 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); } @@ -716,6 +723,28 @@ device_refresh_present(struct device *dev) __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) @@ -734,6 +763,8 @@ void device_set_link(struct device *dev, bool 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); } @@ -1091,6 +1122,7 @@ device_dump_status(struct blob_buf *b, struct device *dev) 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); @@ -1157,6 +1189,8 @@ device_dump_status(struct blob_buf *b, struct device *dev) 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"); diff --git a/device.h b/device.h index f6eaf27..ed07791 100644 --- a/device.h +++ b/device.h @@ -59,6 +59,7 @@ enum { DEV_ATTR_DROP_GRATUITOUS_ARP, DEV_ATTR_DROP_UNSOLICITED_NA, DEV_ATTR_ARP_ACCEPT, + DEV_ATTR_AUTH, __DEV_ATTR_MAX, }; @@ -100,7 +101,7 @@ enum { 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), @@ -134,6 +135,7 @@ enum device_event { DEV_EVENT_UP, DEV_EVENT_DOWN, + DEV_EVENT_AUTH_UP, DEV_EVENT_LINK_UP, DEV_EVENT_LINK_DOWN, @@ -192,6 +194,7 @@ struct device_settings { bool drop_gratuitous_arp; bool drop_unsolicited_na; bool arp_accept; + bool auth; }; /* @@ -220,6 +223,7 @@ struct device { int active; /* DEV_EVENT_LINK_UP */ bool link_active; + bool auth_status; bool external; bool disabled; @@ -324,6 +328,8 @@ struct device *get_vlan_device_chain(const char *ifname, bool create); 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) { @@ -338,6 +344,15 @@ device_set_disabled(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 diff --git a/interface.c b/interface.c index 2a8f604..a91246a 100644 --- a/interface.c +++ b/interface.c @@ -99,6 +99,17 @@ interface_error_flush(struct interface *iface) } } +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) { @@ -344,7 +355,7 @@ __interface_set_up(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: @@ -390,7 +401,8 @@ interface_set_link_state(struct interface *iface, bool new_state) 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; } @@ -424,11 +436,10 @@ interface_main_dev_cb(struct device_user *dep, enum device_event ev) 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); diff --git a/ubus.c b/ubus.c index 9098c66..be15062 100644 --- a/ubus.c +++ b/ubus.c @@ -298,12 +298,14 @@ error: 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 @@ -329,6 +331,10 @@ netifd_handle_set_state(struct ubus_context *ctx, struct ubus_object *obj, 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; }