-// SPDX-License-Identifier: GPL-2.0+
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2022 Felix Fietkau <nbd@nbd.name>
*/
enum {
SERVICE_ATTR_TYPE,
+ SERVICE_ATTR_CONFIG,
SERVICE_ATTR_MEMBERS,
__SERVICE_ATTR_MAX
};
static const struct blobmsg_policy service_policy[__SERVICE_ATTR_MAX] = {
[SERVICE_ATTR_TYPE] = { "type", BLOBMSG_TYPE_STRING },
+ [SERVICE_ATTR_CONFIG] = { "config", BLOBMSG_TYPE_TABLE },
[SERVICE_ATTR_MEMBERS] = { "members", BLOBMSG_TYPE_ARRAY },
};
-void network_services_init(struct network *net)
+void network_services_free(struct network *net)
{
- avl_init(&net->services, avl_strcmp, false, NULL);
+ vlist_flush_all(&net->services);
}
-void network_services_free(struct network *net)
+static int
+__service_add_member(struct network_host **list, int *n, struct network_host *member)
{
- struct network_service *s, *tmp;
+ int i;
+
+ for (i = 0; i < *n; i++) {
+ if (list[i] == member)
+ return 0;
+ }
- avl_remove_all_elements(&net->services, s, node, tmp)
- free(s);
+ list[(*n)++] = member;
+ return 1;
+}
+
+static int
+__service_add_group(struct network_host **list, int *n, struct network_group *group)
+{
+ int i, count = 0;
+
+ for (i = 0; i < group->n_members; i++)
+ count += __service_add_member(list, n, group->members[i]);
+
+ return count;
}
static int
return 0;
if (s)
- s->members[s->n_members++] = host;
+ __service_add_member(s->members, &s->n_members, host);
return 1;
}
if (!group)
return 0;
- if (s) {
- memcpy(&s->members[s->n_members], group->members,
- group->n_members * sizeof(group->members[0]));
- s->n_members += group->n_members;
- }
-
- return group->n_members;
+ if (s)
+ return __service_add_group(s->members, &s->n_members, group);
+ else
+ return group->n_members;
}
static int
{
struct network_service *s;
struct blob_attr *tb[__SERVICE_ATTR_MAX];
- struct blob_attr *cur;
+ struct blob_attr *cur, *config;
const char *name = blobmsg_name(data);
const char *type = NULL;
char *name_buf, *type_buf;
+ void *config_buf;
int n_members;
blobmsg_parse(service_policy, __SERVICE_ATTR_MAX, tb,
if (blobmsg_check_array(tb[SERVICE_ATTR_MEMBERS], BLOBMSG_TYPE_STRING) < 0)
return;
+ config = tb[SERVICE_ATTR_CONFIG];
+
n_members = service_parse_members(net, NULL, tb[SERVICE_ATTR_MEMBERS]);
s = calloc_a(sizeof(*s) + n_members * sizeof(s->members[0]),
&name_buf, strlen(name) + 1,
- &type_buf, type ? strlen(type) + 1 : 0);
+ &type_buf, type ? strlen(type) + 1 : 0,
+ &config_buf, config ? blob_pad_len(config) : 0);
- s->node.key = strcpy(name_buf, name);
+ strcpy(name_buf, name);
if (type)
s->type = strcpy(type_buf, type);
+ if (config)
+ s->config = memcpy(config_buf, config, blob_pad_len(config));
+#ifdef linux
+ if (type && !strcmp(type, "vxlan"))
+ s->ops = &vxlan_ops;
+#endif
service_parse_members(net, s, tb[SERVICE_ATTR_MEMBERS]);
- avl_insert(&net->services, &s->node);
+ vlist_add(&net->services, &s->node, name_buf);
}
void network_services_add(struct network *net, struct blob_attr *data)
blobmsg_for_each_attr(cur, data, rem)
service_add(net, cur);
}
+
+static void
+service_update(struct vlist_tree *tree, struct vlist_node *node_new,
+ struct vlist_node *node_old)
+{
+ struct network *net = container_of(tree, struct network, services);
+ struct network_service *s_old, *s_new;
+
+ s_new = container_of_safe(node_new, struct network_service, node);
+ s_old = container_of_safe(node_old, struct network_service, node);
+
+ if (s_new && s_old && s_new->ops && s_new->ops == s_old->ops) {
+ s_new->ops->init(net, s_new, s_old);
+ goto out;
+ }
+
+ if (s_new && s_new->ops)
+ s_new->ops->init(net, s_new, NULL);
+
+ if (s_old && s_old->ops)
+ s_old->ops->free(net, s_old);
+
+out:
+ free(s_old);
+}
+
+void network_services_peer_update(struct network *net, struct network_peer *peer)
+{
+ struct network_service *s;
+
+ vlist_for_each_element(&net->services, s, node) {
+ if (!s->ops || !s->ops->peer_update)
+ continue;
+
+ s->ops->peer_update(net, s, peer);
+ }
+}
+
+void network_services_init(struct network *net)
+{
+ vlist_init(&net->services, avl_strcmp, service_update);
+}