29b41a20a684c6e417761e5be7a1478b78a569e1
[project/unetd.git] / service.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Copyright (C) 2022 Felix Fietkau <nbd@nbd.name>
4 */
5 #include <libubox/avl-cmp.h>
6 #include "unetd.h"
7
8 enum {
9 SERVICE_ATTR_TYPE,
10 SERVICE_ATTR_CONFIG,
11 SERVICE_ATTR_MEMBERS,
12 __SERVICE_ATTR_MAX
13 };
14
15 static const struct blobmsg_policy service_policy[__SERVICE_ATTR_MAX] = {
16 [SERVICE_ATTR_TYPE] = { "type", BLOBMSG_TYPE_STRING },
17 [SERVICE_ATTR_CONFIG] = { "config", BLOBMSG_TYPE_TABLE },
18 [SERVICE_ATTR_MEMBERS] = { "members", BLOBMSG_TYPE_ARRAY },
19 };
20
21 void network_services_free(struct network *net)
22 {
23 vlist_flush_all(&net->services);
24 }
25
26 static int
27 __service_parse_members(struct network *net, struct network_service *s,
28 const char *name)
29 {
30 struct network_group *group;
31 struct network_host *host;
32
33 if (name[0] != '@') {
34 host = avl_find_element(&net->hosts, name, host, node);
35
36 if (!host)
37 return 0;
38
39 if (s)
40 s->members[s->n_members++] = host;
41
42 return 1;
43 }
44
45 name++;
46 group = avl_find_element(&net->groups, name, group, node);
47 if (!group)
48 return 0;
49
50 if (s) {
51 memcpy(&s->members[s->n_members], group->members,
52 group->n_members * sizeof(group->members[0]));
53 s->n_members += group->n_members;
54 }
55
56 return group->n_members;
57 }
58
59 static int
60 service_parse_members(struct network *net, struct network_service *s,
61 struct blob_attr *data)
62 {
63 struct blob_attr *cur;
64 int rem;
65 int n = 0;
66
67 blobmsg_for_each_attr(cur, data, rem)
68 n += __service_parse_members(net, s, blobmsg_get_string(cur));
69
70 return n;
71 }
72
73 static void
74 service_add(struct network *net, struct blob_attr *data)
75 {
76 struct network_service *s;
77 struct blob_attr *tb[__SERVICE_ATTR_MAX];
78 struct blob_attr *cur, *config;
79 const char *name = blobmsg_name(data);
80 const char *type = NULL;
81 char *name_buf, *type_buf;
82 void *config_buf;
83 int n_members;
84
85 blobmsg_parse(service_policy, __SERVICE_ATTR_MAX, tb,
86 blobmsg_data(data), blobmsg_len(data));
87
88 if ((cur = tb[SERVICE_ATTR_TYPE]) != NULL)
89 type = blobmsg_get_string(cur);
90
91 if (blobmsg_check_array(tb[SERVICE_ATTR_MEMBERS], BLOBMSG_TYPE_STRING) < 0)
92 return;
93
94 config = tb[SERVICE_ATTR_CONFIG];
95
96 n_members = service_parse_members(net, NULL, tb[SERVICE_ATTR_MEMBERS]);
97 s = calloc_a(sizeof(*s) + n_members * sizeof(s->members[0]),
98 &name_buf, strlen(name) + 1,
99 &type_buf, type ? strlen(type) + 1 : 0,
100 &config_buf, config ? blob_pad_len(config) : 0);
101
102 strcpy(name_buf, name);
103 if (type)
104 s->type = strcpy(type_buf, type);
105 if (config)
106 s->config = memcpy(config_buf, config, blob_pad_len(config));
107 #ifdef linux
108 if (type && !strcmp(type, "vxlan"))
109 s->ops = &vxlan_ops;
110 #endif
111
112 service_parse_members(net, s, tb[SERVICE_ATTR_MEMBERS]);
113 vlist_add(&net->services, &s->node, name_buf);
114 }
115
116 void network_services_add(struct network *net, struct blob_attr *data)
117 {
118 struct blob_attr *cur;
119 int rem;
120
121 blobmsg_for_each_attr(cur, data, rem)
122 service_add(net, cur);
123 }
124
125 static void
126 service_update(struct vlist_tree *tree, struct vlist_node *node_new,
127 struct vlist_node *node_old)
128 {
129 struct network *net = container_of(tree, struct network, services);
130 struct network_service *s_old, *s_new;
131
132 s_new = container_of_safe(node_new, struct network_service, node);
133 s_old = container_of_safe(node_old, struct network_service, node);
134
135 if (s_new && s_old && s_new->ops && s_new->ops == s_old->ops) {
136 s_new->ops->init(net, s_new, s_old);
137 goto out;
138 }
139
140 if (s_new && s_new->ops)
141 s_new->ops->init(net, s_new, NULL);
142
143 if (s_old && s_old->ops)
144 s_old->ops->free(net, s_old);
145
146 out:
147 free(s_old);
148 }
149
150 void network_services_peer_update(struct network *net, struct network_peer *peer)
151 {
152 struct network_service *s;
153
154 vlist_for_each_element(&net->services, s, node) {
155 if (!s->ops || !s->ops->peer_update)
156 continue;
157
158 s->ops->peer_update(net, s, peer);
159 }
160 }
161
162 void network_services_init(struct network *net)
163 {
164 vlist_init(&net->services, avl_strcmp, service_update);
165 }