diff options
-rw-r--r-- | interface-ip.c | 85 | ||||
-rw-r--r-- | interface-ip.h | 14 | ||||
-rw-r--r-- | interface.c | 10 | ||||
-rw-r--r-- | interface.h | 3 | ||||
-rw-r--r-- | proto-static.c | 6 | ||||
-rw-r--r-- | utils.c | 66 | ||||
-rw-r--r-- | utils.h | 35 |
7 files changed, 152 insertions, 67 deletions
diff --git a/interface-ip.c b/interface-ip.c index b86acb5..baa8b0e 100644 --- a/interface-ip.c +++ b/interface-ip.c @@ -10,63 +10,60 @@ #include "ubus.h" #include "system.h" -int interface_add_address(struct interface *iface, struct device_addr *addr) +static void +interface_update_proto_addr(struct vlist_tree *tree, + struct vlist_node *node_new, + struct vlist_node *node_old) { - int family; + struct interface *iface; + struct device *dev; + struct device_addr *addr; - if (addr->flags & DEVADDR_INET6) - family = AF_INET6; - else - family = AF_INET; + iface = container_of(tree, struct interface, proto_addr); + dev = iface->l3_iface->dev; - list_add_tail(&addr->list, &iface->address); - return system_add_address(iface->l3_iface->dev, addr); -} - -void interface_del_address(struct interface *iface, struct device_addr *addr) -{ - int family; - - if (addr->flags & DEVADDR_INET6) - family = AF_INET6; - else - family = AF_INET; + if (node_old) { + addr = container_of(node_old, struct device_addr, node); + system_del_address(dev, addr); + free(addr); + } - list_del(&addr->list); - system_del_address(iface->l3_iface->dev, addr); - free(addr); + if (node_new) { + addr = container_of(node_new, struct device_addr, node); + system_add_address(dev, addr); + } } -void interface_del_ctx_addr(struct interface *iface, void *ctx) +static void +interface_update_proto_route(struct vlist_tree *tree, + struct vlist_node *node_new, + struct vlist_node *node_old) { - struct device_addr *addr, *tmp; + struct interface *iface; + struct device *dev; + struct device_route *route; - list_for_each_entry_safe(addr, tmp, &iface->address, list) { - if (ctx && addr->ctx != ctx) - continue; + iface = container_of(tree, struct interface, proto_route); + dev = iface->l3_iface->dev; - interface_del_address(iface, addr); + if (node_old) { + route = container_of(node_old, struct device_route, node); + system_del_route(dev, route); + free(route); } -} -int interface_add_route(struct interface *iface, struct device_route *route) -{ - list_add_tail(&route->list, &iface->routes); - return system_add_route(iface->l3_iface->dev, route); + if (node_new) { + route = container_of(node_new, struct device_route, node); + system_add_route(dev, route); + } } -void interface_del_route(struct interface *iface, struct device_route *route) +void +interface_ip_init(struct interface *iface) { - list_del(&route->list); - system_del_route(iface->l3_iface->dev, route); - if (!route->keep) - free(route); + vlist_init(&iface->proto_route, interface_update_proto_route, + struct device_route, node, mask, addr); + vlist_init(&iface->proto_addr, interface_update_proto_addr, + struct device_addr, node, mask, addr); } -void interface_del_all_routes(struct interface *iface) -{ - struct device_route *route, *tmp; - - list_for_each_entry_safe(route, tmp, &iface->routes, list) - interface_del_route(iface, route); -} diff --git a/interface-ip.h b/interface-ip.h index e83e327..9266727 100644 --- a/interface-ip.h +++ b/interface-ip.h @@ -17,8 +17,7 @@ union if_addr { }; struct device_addr { - struct list_head list; - void *ctx; + struct vlist_node node; enum device_addr_flags flags; @@ -27,8 +26,7 @@ struct device_addr { }; struct device_route { - struct list_head list; - void *ctx; + struct vlist_node node; enum device_addr_flags flags; bool keep; @@ -38,12 +36,6 @@ struct device_route { union if_addr nexthop; }; -int interface_add_address(struct interface *iface, struct device_addr *addr); -void interface_del_address(struct interface *iface, struct device_addr *addr); -void interface_del_ctx_addr(struct interface *iface, void *ctx); - -int interface_add_route(struct interface *iface, struct device_route *route); -void interface_del_route(struct interface *iface, struct device_route *route); -void interface_del_all_routes(struct interface *iface); +void interface_ip_init(struct interface *iface); #endif diff --git a/interface.c b/interface.c index 387d349..031959c 100644 --- a/interface.c +++ b/interface.c @@ -89,8 +89,8 @@ interface_event(struct interface *iface, enum interface_event ev) static void mark_interface_down(struct interface *iface) { - interface_del_all_routes(iface); - interface_del_ctx_addr(iface, NULL); + vlist_flush_all(&iface->proto_addr); + vlist_flush_all(&iface->proto_route); device_release(&iface->main_dev); iface->state = IFS_DOWN; } @@ -129,8 +129,6 @@ __interface_set_down(struct interface *iface, bool force) iface->state = IFS_TEARDOWN; interface_event(iface, IFEV_DOWN); - - interface_del_all_routes(iface); interface_proto_event(iface->proto, PROTO_CMD_TEARDOWN, force); } @@ -221,8 +219,8 @@ interface_alloc(const char *name, struct blob_attr *attr) strncpy(iface->name, name, sizeof(iface->name) - 1); list_add_tail(&iface->list, &interfaces); INIT_LIST_HEAD(&iface->errors); - INIT_LIST_HEAD(&iface->address); - INIT_LIST_HEAD(&iface->routes); + + interface_ip_init(iface); blobmsg_parse(iface_attrs, IFACE_ATTR_MAX, tb, blob_data(attr), blob_len(attr)); diff --git a/interface.h b/interface.h index 0f2eb85..36bde41 100644 --- a/interface.h +++ b/interface.h @@ -52,7 +52,8 @@ struct interface { const struct proto_handler *proto_handler; struct interface_proto_state *proto; - struct list_head address, routes; + struct vlist_tree proto_addr; + struct vlist_tree proto_route; /* errors/warnings while trying to bring up the interface */ struct list_head errors; diff --git a/proto-static.c b/proto-static.c index 04e2118..3868ab7 100644 --- a/proto-static.c +++ b/proto-static.c @@ -89,14 +89,13 @@ parse_addr(struct interface_proto_state *state, const char *str, bool v6, int ma addr = calloc(1, sizeof(*addr)); addr->flags = v6 ? DEVADDR_INET6 : DEVADDR_INET4; - addr->ctx = state; addr->mask = mask; if (!parse_ip_and_netmask(af, str, &addr->addr, &addr->mask)) { interface_add_error(state->iface, "proto-static", "INVALID_ADDRESS", &str, 1); free(addr); return false; } - interface_add_address(state->iface, addr); + vlist_add(&state->iface->proto_addr, &addr->node); return true; } @@ -132,7 +131,7 @@ parse_gateway_option(struct interface_proto_state *state, struct blob_attr *attr } route->mask = 0; route->flags = DEVADDR_DEVICE | (v6 ? DEVADDR_INET6 : DEVADDR_INET4); - interface_add_route(state->iface, route); + vlist_add(&state->iface->proto_route, &route->node); return true; } @@ -211,7 +210,6 @@ static_handler(struct interface_proto_state *proto, /* fall through */ case PROTO_CMD_TEARDOWN: - interface_del_ctx_addr(state->proto.iface, proto); break; } return ret; @@ -1,8 +1,72 @@ #include <string.h> #include "utils.h" -int avl_strcmp(const void *k1, const void *k2, void *ptr) +int +avl_strcmp(const void *k1, const void *k2, void *ptr) { return strcmp(k1, k2); } +static int +vlist_cmp(const void *k1, const void *k2, void *ptr) +{ + struct vlist_tree *vl = ptr; + return memcmp(k1, k2, vl->data_len); +} + +void +__vlist_init(struct vlist_tree *tree, vlist_update_cb update, int offset, int len) +{ + tree->data_offset = offset; + tree->data_len = len; + tree->update = update; + tree->version = 1; + + avl_init(&tree->avl, vlist_cmp, 0, tree); +} + +void +vlist_delete(struct vlist_tree *tree, struct vlist_node *node) +{ + avl_delete(&tree->avl, &node->avl); + tree->update(tree, NULL, node); +} + +void +vlist_add(struct vlist_tree *tree, struct vlist_node *node) +{ + struct vlist_node *old_node = NULL; + struct avl_node *anode; + + node->avl.key = (char *) node + tree->data_offset; + node->version = tree->version; + + anode = avl_find(&tree->avl, (char *) node + tree->data_offset); + if (anode) { + old_node = container_of(anode, struct vlist_node, avl); + avl_delete(&tree->avl, anode); + } + + avl_insert(&tree->avl, &node->avl); + tree->update(tree, node, old_node); +} + +void +vlist_flush(struct vlist_tree *tree) +{ + struct vlist_node *node, *tmp; + + avl_for_each_element_safe(&tree->avl, node, avl, tmp) { + if (node->version == tree->version) + continue; + + vlist_delete(tree, node); + } +} + +void +vlist_flush_all(struct vlist_tree *tree) +{ + tree->version++; + vlist_flush(tree); +} @@ -2,6 +2,7 @@ #define __NETIFD_UTILS_H #include <libubox/list.h> +#include <libubox/avl.h> #ifdef DEBUG #define DPRINTF(format, ...) fprintf(stderr, "%s(%d): " format, __func__, __LINE__, ## __VA_ARGS__) @@ -15,6 +16,40 @@ static inline void no_debug(const char *fmt, ...) #define __init __attribute__((constructor)) +struct vlist_tree; +struct vlist_node; + +typedef void (*vlist_update_cb)(struct vlist_tree *tree, + struct vlist_node *node_new, + struct vlist_node *node_old); + +struct vlist_tree { + struct avl_tree avl; + + vlist_update_cb update; + + int data_offset; + int data_len; + + int version; +}; + +struct vlist_node { + struct avl_node avl; + int version; +}; + +void __vlist_init(struct vlist_tree *tree, vlist_update_cb update, int offset, int len); + +#define vlist_init(tree, update, type, vlist, first, last) \ + __vlist_init(tree, update, offsetof(type, first) - offsetof(type, vlist), \ + offsetof(type, last) - offsetof(type, first) + sizeof(((type *) 0)->last)) + +void vlist_add(struct vlist_tree *tree, struct vlist_node *node); +void vlist_delete(struct vlist_tree *tree, struct vlist_node *node); +void vlist_flush(struct vlist_tree *tree); +void vlist_flush_all(struct vlist_tree *tree); + #ifdef __linux__ static inline int fls(int x) { |