network: add support for configuring extra peers via a separate json file
[project/unetd.git] / ubus.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (C) 2022 Felix Fietkau <nbd@nbd.name>
4 */
5 #include <arpa/inet.h>
6 #include <libubus.h>
7 #include "unetd.h"
8
9 static struct ubus_auto_conn conn;
10 static struct blob_buf b;
11
12 static int
13 ubus_network_add(struct ubus_context *ctx, struct ubus_object *obj,
14 struct ubus_request_data *req, const char *method,
15 struct blob_attr *msg)
16 {
17 struct blob_attr *name;
18
19 blobmsg_parse(&network_policy[NETWORK_ATTR_NAME], 1, &name,
20 blobmsg_data(msg), blobmsg_len(msg));
21
22 if (!name)
23 return UBUS_STATUS_INVALID_ARGUMENT;
24
25 if (unetd_network_add(blobmsg_get_string(name), msg))
26 return UBUS_STATUS_INVALID_ARGUMENT;
27
28 return 0;
29 }
30
31
32 static int
33 ubus_network_del(struct ubus_context *ctx, struct ubus_object *obj,
34 struct ubus_request_data *req, const char *method,
35 struct blob_attr *msg)
36 {
37 struct blob_attr *name;
38
39 blobmsg_parse(&network_policy[NETWORK_ATTR_NAME], 1, &name,
40 blobmsg_data(msg), blobmsg_len(msg));
41
42 if (!name)
43 return UBUS_STATUS_INVALID_ARGUMENT;
44
45 if (unetd_network_remove(blobmsg_get_string(name)))
46 return UBUS_STATUS_NOT_FOUND;
47
48 return 0;
49 }
50
51 static void
52 __network_dump(struct blob_buf *buf, struct network *net)
53 {
54 struct network_host *local = net->net_config.local_host;
55 struct network_peer *peer;
56 void *c, *p;
57 char *str;
58
59 blobmsg_add_field(buf, BLOBMSG_TYPE_TABLE, "config",
60 blobmsg_data(net->config.data),
61 blobmsg_len(net->config.data));
62
63 if (local)
64 blobmsg_add_string(buf, "local_host", network_host_name(local));
65
66 c = blobmsg_open_table(buf, "peers");
67 vlist_for_each_element(&net->peers, peer, node) {
68 union network_endpoint *ep = &peer->state.endpoint;
69 void *addr;
70 int len;
71
72 p = blobmsg_open_table(buf, network_peer_name(peer));
73 blobmsg_add_u8(buf, "connected", peer->state.connected);
74 if (peer->state.connected) {
75 str = blobmsg_alloc_string_buffer(buf, "endpoint",
76 INET6_ADDRSTRLEN + 7);
77 addr = network_endpoint_addr(ep, &len);
78 inet_ntop(ep->sa.sa_family, addr, str, INET6_ADDRSTRLEN);
79 len = strlen(str);
80 snprintf(str + len, INET6_ADDRSTRLEN + 7 - len, ":%d",
81 ntohs(ep->in.sin_port));
82 blobmsg_add_string_buffer(buf);
83 }
84
85 blobmsg_close_table(buf, p);
86 }
87 blobmsg_close_table(buf, c);
88 }
89
90 static int
91 ubus_network_get(struct ubus_context *ctx, struct ubus_object *obj,
92 struct ubus_request_data *req, const char *method,
93 struct blob_attr *msg)
94 {
95 struct blob_attr *name;
96 struct network *net;
97 void *c, *n;
98
99 blobmsg_parse(&network_policy[NETWORK_ATTR_NAME], 1, &name,
100 blobmsg_data(msg), blobmsg_len(msg));
101
102 blob_buf_init(&b, 0);
103 if (name) {
104 net = avl_find_element(&networks, blobmsg_get_string(name), net, node);
105 if (!net)
106 return UBUS_STATUS_NOT_FOUND;
107
108 __network_dump(&b, net);
109 } else {
110 c = blobmsg_open_table(&b, "networks");
111 avl_for_each_element(&networks, net, node) {
112 n = blobmsg_open_table(&b, network_name(net));
113 __network_dump(&b, net);
114 blobmsg_close_table(&b, n);
115 }
116 blobmsg_close_table(&b, c);
117 }
118
119 ubus_send_reply(ctx, req, b.head);
120
121 return 0;
122 }
123
124 enum {
125 SERVICE_ATTR_NETWORK,
126 SERVICE_ATTR_NAME,
127 __SERVICE_ATTR_MAX
128 };
129
130 static const struct blobmsg_policy service_policy[__SERVICE_ATTR_MAX] = {
131 [SERVICE_ATTR_NETWORK] = { "network", BLOBMSG_TYPE_STRING },
132 [SERVICE_ATTR_NAME] = { "name", BLOBMSG_TYPE_STRING },
133 };
134
135
136 static void
137 ubus_service_get_network_members(struct blob_buf *b, struct network *n,
138 const char *name)
139 {
140 struct network_service *s;
141 int i;
142
143 s = vlist_find(&n->services, name, s, node);
144 if (!s)
145 return;
146
147 for (i = 0; i < s->n_members; i++) {
148 struct network_host *host = s->members[i];
149 char *name;
150
151 name = blobmsg_alloc_string_buffer(b, NULL, INET6_ADDRSTRLEN);
152 inet_ntop(AF_INET6, &host->peer.local_addr.in6, name, INET6_ADDRSTRLEN);
153 blobmsg_add_string_buffer(b);
154 }
155 }
156
157
158 static int
159 ubus_service_get(struct ubus_context *ctx, struct ubus_object *obj,
160 struct ubus_request_data *req, const char *method,
161 struct blob_attr *msg)
162 {
163 struct blob_attr *tb[__SERVICE_ATTR_MAX];
164 struct blob_attr *cur;
165 struct network *n = NULL;
166 const char *name;
167 void *c;
168
169 blobmsg_parse(service_policy, __SERVICE_ATTR_MAX, tb,
170 blobmsg_data(msg), blobmsg_len(msg));
171
172 if ((cur = tb[SERVICE_ATTR_NAME]) != NULL)
173 name = blobmsg_get_string(cur);
174 else
175 return UBUS_STATUS_INVALID_ARGUMENT;
176
177 if ((cur = tb[SERVICE_ATTR_NETWORK]) != NULL) {
178 n = avl_find_element(&networks, blobmsg_get_string(cur), n, node);
179 if (!n)
180 return UBUS_STATUS_INVALID_ARGUMENT;
181 }
182
183 blob_buf_init(&b, 0);
184
185 c = blobmsg_open_array(&b, "hosts");
186 if (n) {
187 ubus_service_get_network_members(&b, n, name);
188 } else {
189 avl_for_each_element(&networks, n, node)
190 ubus_service_get_network_members(&b, n, name);
191 }
192 blobmsg_close_array(&b, c);
193 ubus_send_reply(ctx, req, b.head);
194
195 return 0;
196 }
197
198 enum {
199 CONNECT_ATTR_NAME,
200 CONNECT_ATTR_ADDRESS,
201 CONNECT_ATTR_TIMEOUT,
202 __CONNECT_ATTR_MAX
203 };
204
205 static const struct blobmsg_policy connect_policy[__CONNECT_ATTR_MAX] = {
206 [CONNECT_ATTR_NAME] = { "name", BLOBMSG_TYPE_STRING },
207 [CONNECT_ATTR_ADDRESS] = { "address", BLOBMSG_TYPE_STRING },
208 [CONNECT_ATTR_TIMEOUT] = { "timeout", BLOBMSG_TYPE_INT32 },
209 };
210
211 static int
212 ubus_network_connect(struct ubus_context *ctx, struct ubus_object *obj,
213 struct ubus_request_data *req, const char *method,
214 struct blob_attr *msg)
215 {
216 struct blob_attr *tb[__CONNECT_ATTR_MAX];
217 union network_endpoint ep = {};
218 struct blob_attr *cur;
219 struct network *net;
220 unsigned int timeout;
221 const char *name;
222
223 blobmsg_parse(connect_policy, __CONNECT_ATTR_MAX, tb,
224 blobmsg_data(msg), blobmsg_len(msg));
225
226 if ((cur = tb[CONNECT_ATTR_NAME]) != NULL)
227 name = blobmsg_get_string(cur);
228 else
229 return UBUS_STATUS_INVALID_ARGUMENT;
230
231 if ((cur = tb[CONNECT_ATTR_TIMEOUT]) != NULL)
232 timeout = blobmsg_get_u32(cur);
233 else
234 return UBUS_STATUS_INVALID_ARGUMENT;
235
236 if ((cur = tb[CONNECT_ATTR_ADDRESS]) == NULL ||
237 network_get_endpoint(&ep, blobmsg_get_string(cur), UNETD_GLOBAL_PEX_PORT, 0) < 0 ||
238 !ep.in.sin_port)
239 return UBUS_STATUS_INVALID_ARGUMENT;
240
241 net = avl_find_element(&networks, name, net, node);
242 if (!net)
243 return UBUS_STATUS_NOT_FOUND;
244
245 if (net->config.type != NETWORK_TYPE_DYNAMIC)
246 return UBUS_STATUS_INVALID_ARGUMENT;
247
248 network_pex_create_host(net, &ep, timeout);
249
250 return 0;
251 }
252
253
254 static const struct ubus_method unetd_methods[] = {
255 UBUS_METHOD("network_add", ubus_network_add, network_policy),
256 UBUS_METHOD_MASK("network_del", ubus_network_del, network_policy,
257 (1 << NETWORK_ATTR_NAME)),
258 UBUS_METHOD_MASK("network_get", ubus_network_get, network_policy,
259 (1 << NETWORK_ATTR_NAME)),
260 UBUS_METHOD("network_connect", ubus_network_connect, connect_policy),
261 UBUS_METHOD("service_get", ubus_service_get, service_policy),
262 };
263
264 static struct ubus_object_type unetd_object_type =
265 UBUS_OBJECT_TYPE("unetd", unetd_methods);
266
267 static struct ubus_object unetd_object = {
268 .name = "unetd",
269 .type = &unetd_object_type,
270 .methods = unetd_methods,
271 .n_methods = ARRAY_SIZE(unetd_methods),
272 };
273
274 static void
275 ubus_connect_handler(struct ubus_context *ctx)
276 {
277 int ret;
278
279 ret = ubus_add_object(ctx, &unetd_object);
280 if (ret)
281 fprintf(stderr, "Failed to add object: %s\n", ubus_strerror(ret));
282 }
283
284 void unetd_ubus_netifd_update(struct blob_attr *data)
285 {
286 uint32_t id;
287
288 if (ubus_lookup_id(&conn.ctx, "network.interface", &id))
289 return;
290
291 ubus_invoke(&conn.ctx, id, "notify_proto", data, NULL, NULL, 5000);
292 }
293
294 void unetd_ubus_netifd_add_route(struct network *net, union network_endpoint *ep)
295 {
296 uint32_t id;
297 void *addr;
298 char *buf;
299
300 if (!net->config.interface)
301 return;
302
303 if (ubus_lookup_id(&conn.ctx, "network", &id))
304 return;
305
306 blob_buf_init(&b, 0);
307
308 if (ep->in.sin_family == AF_INET6)
309 addr = &ep->in6.sin6_addr;
310 else
311 addr = &ep->in.sin_addr;
312
313 blobmsg_add_u8(&b, "v6", ep->in.sin_family == AF_INET6);
314 buf = blobmsg_alloc_string_buffer(&b, "target", INET6_ADDRSTRLEN);
315 inet_ntop(ep->in.sin_family, addr, buf, INET6_ADDRSTRLEN);
316 blobmsg_add_string_buffer(&b);
317 blobmsg_add_string(&b, "interface", net->config.interface);
318 blobmsg_add_u8(&b, "exclude", true);
319
320 ubus_invoke(&conn.ctx, id, "add_host_route", b.head, NULL, NULL, -1);
321 }
322
323 void unetd_ubus_init(void)
324 {
325 conn.cb = ubus_connect_handler;
326 ubus_auto_connect(&conn);
327 }