#include #include #include #include #include "netifd.h" #include "device.h" #include "system.h" enum { VRF_ATTR_PORTS, VRF_ATTR_TABLE, __VRF_ATTR_MAX }; static const struct blobmsg_policy vrf_attrs[__VRF_ATTR_MAX] = { [VRF_ATTR_PORTS] = { "ports", BLOBMSG_TYPE_ARRAY }, [VRF_ATTR_TABLE] = { "table", BLOBMSG_TYPE_STRING }, }; static const struct uci_blob_param_info vrf_attr_info[__VRF_ATTR_MAX] = { [VRF_ATTR_PORTS] = { .type = BLOBMSG_TYPE_STRING }, }; static const struct uci_blob_param_list vrf_attr_list = { .n_params = __VRF_ATTR_MAX, .params = vrf_attrs, .info = vrf_attr_info, .n_next = 1, .next = { &device_attr_list }, }; static struct device *vrf_create(const char *name, struct device_type *devtype, struct blob_attr *attr); static void vrf_config_init(struct device *dev); static void vrf_free(struct device *dev); static void vrf_dump_info(struct device *dev, struct blob_buf *b); static enum dev_change_type vrf_reload(struct device *dev, struct blob_attr *attr); static struct device_type vrf_state_type = { .name = "vrf", .config_params = &vrf_attr_list, .bridge_capability = true, .create = vrf_create, .config_init = vrf_config_init, .reload = vrf_reload, .free = vrf_free, .dump_info = vrf_dump_info, }; struct vrf_state { struct device dev; device_state_cb set_state; struct blob_attr *config_data; unsigned int table; bool vrf_empty; struct blob_attr *ports; bool active; bool force_active; struct uloop_timeout retry; struct vrf_member *primary_port; struct vlist_tree members; int n_present; int n_failed; }; struct vrf_member { struct vlist_node node; struct vrf_state *vst; struct device_user dev; bool present; bool active; char name[]; }; static void vrf_reset_primary(struct vrf_state *vst) { struct vrf_member *vm; if (!vst->primary_port && (vst->dev.settings.flags & DEV_OPT_MACADDR)) return; vst->primary_port = NULL; vst->dev.settings.flags &= ~DEV_OPT_MACADDR; vlist_for_each_element(&vst->members, vm, node) { uint8_t *macaddr; if (!vm->present) continue; vst->primary_port = vm; if (vm->dev.dev->settings.flags & DEV_OPT_MACADDR) macaddr = vm->dev.dev->settings.macaddr; else macaddr = vm->dev.dev->orig_settings.macaddr; memcpy(vst->dev.settings.macaddr, macaddr, 6); vst->dev.settings.flags |= DEV_OPT_MACADDR; return; } } static int vrf_disable_member(struct vrf_member *vm, bool keep_dev) { struct vrf_state *vst = vm->vst; if (!vm->present || !vm->active) return 0; vm->active = false; system_vrf_delif(&vst->dev, vm->dev.dev); if (!keep_dev) device_release(&vm->dev); device_broadcast_event(&vst->dev, DEV_EVENT_TOPO_CHANGE); return 0; } static int vrf_enable_interface(struct vrf_state *vst) { int ret; if (vst->active) return 0; ret = system_vrf_addvrf(&vst->dev, vst->table); if (ret < 0) return ret; vst->active = true; return 0; } static void vrf_disable_interface(struct vrf_state *vst) { if (!vst->active) return; system_vrf_delvrf(&vst->dev); vst->active = false; } static int vrf_enable_member(struct vrf_member *vm) { struct vrf_state *vst = vm->vst; struct device *dev; int ret; if (!vm->present) return 0; ret = vrf_enable_interface(vst); if (ret) goto error; /* Disable IPv6 for vrf ports */ if (!(vm->dev.dev->settings.flags & DEV_OPT_IPV6)) { vm->dev.dev->settings.ipv6 = 0; vm->dev.dev->settings.flags |= DEV_OPT_IPV6; } ret = device_claim(&vm->dev); if (ret < 0) goto error; dev = vm->dev.dev; if (dev->settings.auth && !dev->auth_status) return -1; if (vm->active) return 0; ret = system_vrf_addif(&vst->dev, vm->dev.dev); if (ret < 0) { D(DEVICE, "Vrf device %s could not be added\n", vm->dev.dev->ifname); goto error; } vm->active = true; device_set_present(&vst->dev, true); device_broadcast_event(&vst->dev, DEV_EVENT_TOPO_CHANGE); return 0; error: vst->n_failed++; vm->present = false; vst->n_present--; device_release(&vm->dev); return ret; } static void vrf_remove_member(struct vrf_member *vm) { struct vrf_state *vst = vm->vst; if (!vm->present) return; if (vst->dev.active) vrf_disable_member(vm, false); vm->present = false; vm->vst->n_present--; if (vm == vst->primary_port) vrf_reset_primary(vst); if (vst->vrf_empty) return; vst->force_active = false; if (vst->n_present == 0) device_set_present(&vst->dev, false); } static void vrf_free_member(struct vrf_member *vm) { struct device *dev = vm->dev.dev; vrf_remove_member(vm); device_remove_user(&vm->dev); /* * When reloading the config and moving a device from one vrf to * another, the other vrf may have tried to claim this device * before it was removed here. * Ensure that claiming the device is retried by toggling its present * state */ if (dev->present) { device_set_present(dev, false); device_set_present(dev, true); } free(vm); } static void vrf_check_retry(struct vrf_state *vst) { if (!vst->n_failed) return; uloop_timeout_set(&vst->retry, 100); } static void vrf_member_cb(struct device_user *dep, enum device_event ev) { struct vrf_member *vm = container_of(dep, struct vrf_member, dev); struct vrf_state *vst = vm->vst; struct device *dev = dep->dev; switch (ev) { case DEV_EVENT_ADD: assert(!vm->present); vm->present = true; vst->n_present++; if (vst->n_present == 1) device_set_present(&vst->dev, true); fallthrough; case DEV_EVENT_AUTH_UP: if (!vst->dev.active) break; if (vrf_enable_member(vm)) break; /* * Adding a vrf port can overwrite the vrf device mtu * in the kernel, apply the vrf settings in case the * vrf device mtu is set */ system_if_apply_settings(&vst->dev, &vst->dev.settings, DEV_OPT_MTU | DEV_OPT_MTU6); break; case DEV_EVENT_LINK_DOWN: if (!dev->settings.auth) break; vrf_disable_member(vm, true); break; case DEV_EVENT_REMOVE: if (dep->hotplug && !dev->sys_present) { vlist_delete(&vst->members, &vm->node); return; } if (vm->present) vrf_remove_member(vm); break; default: return; } } static int vrf_set_down(struct vrf_state *vst) { struct vrf_member *vm; vst->set_state(&vst->dev, false); vlist_for_each_element(&vst->members, vm, node) vrf_disable_member(vm, false); vrf_disable_interface(vst); return 0; } static int vrf_set_up(struct vrf_state *vst) { struct vrf_member *vm; int ret; if (!vst->n_present) { if (!vst->force_active) return -ENOENT; ret = vrf_enable_interface(vst); if (ret) return ret; } vst->n_failed = 0; vlist_for_each_element(&vst->members, vm, node) vrf_enable_member(vm); vrf_check_retry(vst); if (!vst->force_active && !vst->n_present) { /* initialization of all port member failed */ vrf_disable_interface(vst); device_set_present(&vst->dev, false); return -ENOENT; } vrf_reset_primary(vst); ret = vst->set_state(&vst->dev, true); if (ret < 0) vrf_set_down(vst); return ret; } static int vrf_set_state(struct device *dev, bool up) { struct vrf_state *vst; vst = container_of(dev, struct vrf_state, dev); if (up) return vrf_set_up(vst); else return vrf_set_down(vst); } static struct vrf_member * vrf_create_member(struct vrf_state *vst, const char *name, struct device *dev, bool hotplug) { struct vrf_member *vm; vm = calloc(1, sizeof(*vm) + strlen(name) + 1); if (!vm) return NULL; vm->vst = vst; vm->dev.cb = vrf_member_cb; vm->dev.hotplug = hotplug; strcpy(vm->name, name); vm->dev.dev = dev; vlist_add(&vst->members, &vm->node, vm->name); /* * Need to look up the vrf port again as the above * created pointer will be freed in case the vrf port * already existed */ vm = vlist_find(&vst->members, name, vm, node); if (hotplug && vm) vm->node.version = -1; return vm; } static void vrf_member_update(struct vlist_tree *tree, struct vlist_node *node_new, struct vlist_node *node_old) { struct vrf_member *vm; struct device *dev; if (node_new) { vm = container_of(node_new, struct vrf_member, node); if (node_old) { free(vm); return; } dev = vm->dev.dev; vm->dev.dev = NULL; device_add_user(&vm->dev, dev); } if (node_old) { vm = container_of(node_old, struct vrf_member, node); vrf_free_member(vm); } } static void vrf_add_member(struct vrf_state *vst, const char *name) { struct device *dev; dev = device_get(name, true); if (!dev) return; vrf_create_member(vst, name, dev, false); } static int vrf_hotplug_add(struct device *dev, struct device *member, struct blob_attr *vlan) { struct vrf_state *vst = container_of(dev, struct vrf_state, dev); struct vrf_member *vm; vm = vlist_find(&vst->members, member->ifname, vm, node); if (!vm) vrf_create_member(vst, member->ifname, member, true); return 0; } static int vrf_hotplug_del(struct device *dev, struct device *member, struct blob_attr *vlan) { struct vrf_state *vst = container_of(dev, struct vrf_state, dev); struct vrf_member *vm; vm = vlist_find(&vst->members, member->ifname, vm, node); if (!vm) return UBUS_STATUS_NOT_FOUND; if (vm->dev.hotplug) vlist_delete(&vst->members, &vm->node); return 0; } static int vrf_hotplug_prepare(struct device *dev, struct device **vrf_dev) { struct vrf_state *vst; if (vrf_dev) *vrf_dev = dev; vst = container_of(dev, struct vrf_state, dev); vst->force_active = true; device_set_present(&vst->dev, true); return 0; } static const struct device_hotplug_ops vrf_ops = { .prepare = vrf_hotplug_prepare, .add = vrf_hotplug_add, .del = vrf_hotplug_del }; static void vrf_free(struct device *dev) { struct vrf_state *vst; vst = container_of(dev, struct vrf_state, dev); vlist_flush_all(&vst->members); free(vst->config_data); free(vst); } static void vrf_dump_info(struct device *dev, struct blob_buf *b) { struct vrf_state *vst; struct vrf_member *vm; void *list; vst = container_of(dev, struct vrf_state, dev); system_if_dump_info(dev, b); list = blobmsg_open_array(b, "vrf-members"); vlist_for_each_element(&vst->members, vm, node) { if (vm->dev.dev->hidden) continue; blobmsg_add_string(b, NULL, vm->dev.dev->ifname); } blobmsg_close_array(b, list); } static void vrf_config_init(struct device *dev) { struct vrf_state *vst; struct blob_attr *cur; size_t rem; vst = container_of(dev, struct vrf_state, dev); if (vst->vrf_empty) { vst->force_active = true; device_set_present(&vst->dev, true); } vst->n_failed = 0; vlist_update(&vst->members); if (vst->ports) { blobmsg_for_each_attr(cur, vst->ports, rem) { vrf_add_member(vst, blobmsg_data(cur)); } } vlist_flush(&vst->members); vrf_check_retry(vst); } static void vrf_apply_settings(struct vrf_state *vst, struct blob_attr **tb) { struct blob_attr *cur; vst->vrf_empty = true; // default vrf routing table vst->table = 10; if ((cur = tb[VRF_ATTR_TABLE])) system_resolve_rt_table(blobmsg_data(cur), &vst->table); } static enum dev_change_type vrf_reload(struct device *dev, struct blob_attr *attr) { struct blob_attr *tb_dev[__DEV_ATTR_MAX]; struct blob_attr *tb_v[__VRF_ATTR_MAX]; enum dev_change_type ret = DEV_CONFIG_APPLIED; struct vrf_state *vst; unsigned long diff[2]; BUILD_BUG_ON(sizeof(diff) < __VRF_ATTR_MAX / BITS_PER_LONG); BUILD_BUG_ON(sizeof(diff) < __DEV_ATTR_MAX / BITS_PER_LONG); vst = container_of(dev, struct vrf_state, dev); attr = blob_memdup(attr); blobmsg_parse_attr(device_attr_list.params, __DEV_ATTR_MAX, tb_dev, attr); blobmsg_parse_attr(vrf_attrs, __VRF_ATTR_MAX, tb_v, attr); if (tb_dev[DEV_ATTR_MACADDR]) vst->primary_port = NULL; vst->ports = tb_v[VRF_ATTR_PORTS]; device_init_settings(dev, tb_dev); vrf_apply_settings(vst, tb_v); if (vst->config_data) { struct blob_attr *otb_dev[__DEV_ATTR_MAX]; struct blob_attr *otb_v[__VRF_ATTR_MAX]; blobmsg_parse_attr(device_attr_list.params, __DEV_ATTR_MAX, otb_dev, vst->config_data); diff[0] = diff[1] = 0; uci_blob_diff(tb_dev, otb_dev, &device_attr_list, diff); if (diff[0] | diff[1]) { ret = DEV_CONFIG_RESTART; D(DEVICE, "Vrf %s device attributes have changed, diff=[%lx %lx]\n", dev->ifname, diff[1], diff[0]); } blobmsg_parse_attr(vrf_attrs, __VRF_ATTR_MAX, otb_v, vst->config_data); diff[0] = diff[1] = 0; uci_blob_diff(tb_v, otb_v, &vrf_attr_list, diff); if (diff[0] & ~(1 << VRF_ATTR_PORTS)) { ret = DEV_CONFIG_RESTART; D(DEVICE, "Vrf %s attributes have changed, diff=[%lx %lx]\n", dev->ifname, diff[1], diff[0]); } vrf_config_init(dev); } free(vst->config_data); vst->config_data = attr; return ret; } static void vrf_retry_members(struct uloop_timeout *timeout) { struct vrf_state *vst = container_of(timeout, struct vrf_state, retry); struct vrf_member *vm; vst->n_failed = 0; vlist_for_each_element(&vst->members, vm, node) { if (vm->present) continue; if (!vm->dev.dev->present) continue; vm->present = true; vst->n_present++; vrf_enable_member(vm); } } static struct device * vrf_create(const char *name, struct device_type *devtype, struct blob_attr *attr) { struct vrf_state *vst; struct device *dev = NULL; vst = calloc(1, sizeof(*vst)); if (!vst) return NULL; dev = &vst->dev; if (device_init(dev, devtype, name) < 0) { device_cleanup(dev); free(vst); return NULL; } dev->config_pending = true; vst->retry.cb = vrf_retry_members; vst->set_state = dev->set_state; dev->set_state = vrf_set_state; dev->hotplug_ops = &vrf_ops; vlist_init(&vst->members, avl_strcmp, vrf_member_update); vst->members.keep_old = true; vrf_reload(dev, attr); return dev; } static void __init vrf_state_type_init(void) { device_type_add(&vrf_state_type); }