netif_utils: correctly close fd on read error
[project/ustp.git] / ubus.c
1 /*
2 * ustp - OpenWrt STP/RSTP/MSTP daemon
3 * Copyright (C) 2021 Felix Fietkau <nbd@nbd.name>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2
7 * as published by the Free Software Foundation
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14 #include <libubus.h>
15 #include <libubox/uloop.h>
16 #include "config.h"
17 #include "mstp.h"
18 #include "worker.h"
19 #include "ubus.h"
20
21 struct blob_buf b;
22
23 enum bridge_config_attr {
24 BRIDGE_CONFIG_NAME,
25 BRIDGE_CONFIG_PROTO,
26 BRIDGE_CONFIG_FWD_DELAY,
27 BRIDGE_CONFIG_HELLO_TIME,
28 BRIDGE_CONFIG_MAX_AGE,
29 BRIDGE_CONFIG_AGEING_TIME,
30 __BRIDGE_CONFIG_MAX
31 };
32
33 static const struct blobmsg_policy bridge_config_policy[__BRIDGE_CONFIG_MAX] = {
34 [BRIDGE_CONFIG_NAME] = { "name", BLOBMSG_TYPE_STRING },
35 [BRIDGE_CONFIG_PROTO] = { "proto", BLOBMSG_TYPE_STRING },
36 [BRIDGE_CONFIG_FWD_DELAY] = { "forward_delay", BLOBMSG_TYPE_INT32 },
37 [BRIDGE_CONFIG_HELLO_TIME] = { "hello_time", BLOBMSG_TYPE_INT32 },
38 [BRIDGE_CONFIG_MAX_AGE] = { "max_age", BLOBMSG_TYPE_INT32 },
39 [BRIDGE_CONFIG_AGEING_TIME] = { "ageing_time", BLOBMSG_TYPE_INT32 },
40 };
41
42 static bool
43 ubus_set_bridge_config(struct blob_attr *attr)
44 {
45 struct blob_attr *tb[__BRIDGE_CONFIG_MAX], *cur;
46 struct bridge_config *cfg;
47 CIST_BridgeConfig *bc;
48
49 blobmsg_parse(bridge_config_policy, __BRIDGE_CONFIG_MAX, tb,
50 blobmsg_data(attr), blobmsg_len(attr));
51
52 cur = tb[BRIDGE_CONFIG_NAME];
53 if (!cur)
54 return false;
55
56 cfg = bridge_config_get(blobmsg_get_string(cur), true);
57
58 bc = &cfg->config;
59 bc->protocol_version = protoRSTP;
60 bc->set_protocol_version = true;
61
62 if ((cur = tb[BRIDGE_CONFIG_PROTO]) != NULL) {
63 const char *proto = blobmsg_get_string(cur);
64
65 if (!strcmp(proto, "mstp"))
66 bc->protocol_version = protoMSTP;
67 else if (!strcmp(proto, "stp"))
68 bc->protocol_version = protoSTP;
69 }
70
71 if ((cur = tb[BRIDGE_CONFIG_FWD_DELAY]) != NULL) {
72 bc->bridge_forward_delay = blobmsg_get_u32(cur);
73 bc->set_bridge_forward_delay = true;
74 }
75
76 if ((cur = tb[BRIDGE_CONFIG_HELLO_TIME]) != NULL) {
77 bc->bridge_hello_time = blobmsg_get_u32(cur);
78 bc->set_bridge_hello_time = true;
79 }
80
81 if ((cur = tb[BRIDGE_CONFIG_AGEING_TIME]) != NULL) {
82 bc->bridge_ageing_time = blobmsg_get_u32(cur);
83 bc->set_bridge_ageing_time = true;
84 }
85
86 if ((cur = tb[BRIDGE_CONFIG_MAX_AGE]) != NULL) {
87 bc->bridge_max_age = blobmsg_get_u32(cur);
88 bc->set_bridge_max_age = true;
89 }
90
91 return true;
92 }
93
94 static int
95 ubus_add_bridge(struct ubus_context *ctx, struct ubus_object *obj,
96 struct ubus_request_data *req, const char *method,
97 struct blob_attr *msg)
98 {
99 if (!ubus_set_bridge_config(msg))
100 return UBUS_STATUS_INVALID_ARGUMENT;
101
102 return 0;
103 }
104
105 enum bridge_state_attr {
106 BRIDGE_STATE_NAME,
107 BRIDGE_STATE_ENABLED,
108 __BRIDGE_STATE_MAX
109 };
110
111 static const struct blobmsg_policy bridge_state_policy[__BRIDGE_STATE_MAX] = {
112 [BRIDGE_STATE_NAME] = { "name", BLOBMSG_TYPE_STRING },
113 [BRIDGE_STATE_ENABLED] = { "enabled", BLOBMSG_TYPE_BOOL },
114 };
115
116 static int
117 ubus_bridge_state(struct ubus_context *ctx, struct ubus_object *obj,
118 struct ubus_request_data *req, const char *method,
119 struct blob_attr *msg)
120 {
121 struct blob_attr *tb[__BRIDGE_STATE_MAX];
122 struct bridge_config *cfg;
123 const char *bridge_name;
124 struct worker_event ev = {};
125
126 blobmsg_parse(bridge_state_policy, __BRIDGE_STATE_MAX, tb,
127 blobmsg_data(msg), blobmsg_len(msg));
128
129 if (!tb[BRIDGE_STATE_NAME] || !tb[BRIDGE_STATE_ENABLED])
130 return UBUS_STATUS_INVALID_ARGUMENT;
131
132 bridge_name = blobmsg_get_string(tb[BRIDGE_STATE_NAME]);
133 ev.bridge_idx = if_nametoindex(bridge_name);
134 if (!ev.bridge_idx)
135 return UBUS_STATUS_NOT_FOUND;
136
137 if (blobmsg_get_bool(tb[BRIDGE_STATE_ENABLED])) {
138 cfg = bridge_config_get(bridge_name, false);
139 if (!cfg)
140 return UBUS_STATUS_NOT_FOUND;
141
142 ev.type = WORKER_EV_BRIDGE_ADD;
143 ev.bridge_config = cfg->config;
144 } else {
145 ev.type = WORKER_EV_BRIDGE_REMOVE;
146 }
147
148 worker_queue_event(&ev);
149
150 return 0;
151 }
152
153 static const struct ubus_method ustp_methods[] = {
154 UBUS_METHOD("add_bridge", ubus_add_bridge, bridge_config_policy),
155 UBUS_METHOD("bridge_state", ubus_bridge_state, bridge_state_policy),
156 };
157
158 static struct ubus_object_type ustp_object_type =
159 UBUS_OBJECT_TYPE("ustp", ustp_methods);
160
161 static struct ubus_object ustp_object = {
162 .name = "ustp",
163 .type = &ustp_object_type,
164 .methods = ustp_methods,
165 .n_methods = ARRAY_SIZE(ustp_methods),
166 };
167
168 static int
169 netifd_device_cb(struct ubus_context *ctx, struct ubus_object *obj,
170 struct ubus_request_data *req, const char *method,
171 struct blob_attr *msg)
172 {
173 if (strcmp(method, "stp_init") != 0)
174 return 0;
175
176 ubus_set_bridge_config(msg);
177
178 return 0;
179 }
180
181 static struct ubus_auto_conn conn;
182 static struct ubus_subscriber netifd_sub;
183
184 static void netifd_sub_cb(struct uloop_timeout *t)
185 {
186 uint32_t id;
187
188 if (ubus_lookup_id(&conn.ctx, "network.device", &id) != 0 ||
189 ubus_subscribe(&conn.ctx, &netifd_sub, id) != 0) {
190 uloop_timeout_set(t, 1000);
191 return;
192 }
193
194 blob_buf_init(&b, 0);
195 ubus_invoke(&conn.ctx, id, "stp_init", b.head, NULL, NULL, 1000);
196 }
197
198 static struct uloop_timeout netifd_sub_timer = {
199 .cb = netifd_sub_cb,
200 };
201
202 static void
203 netifd_device_remove_cb(struct ubus_context *ctx,
204 struct ubus_subscriber *obj, uint32_t id)
205 {
206 uloop_timeout_set(&netifd_sub_timer, 1000);
207 }
208
209 static struct ubus_subscriber netifd_sub = {
210 .cb = netifd_device_cb,
211 .remove_cb = netifd_device_remove_cb,
212 };
213
214 static void
215 ubus_connect_handler(struct ubus_context *ctx)
216 {
217 ubus_add_object(ctx, &ustp_object);
218 ubus_register_subscriber(ctx, &netifd_sub);
219 uloop_timeout_set(&netifd_sub_timer, 1);
220 }
221
222 void ustp_ubus_init(void)
223 {
224 conn.cb = ubus_connect_handler;
225 ubus_auto_connect(&conn);
226 }
227
228 void ustp_ubus_exit(void)
229 {
230 uint32_t id;
231
232 ubus_remove_object(&conn.ctx, &ustp_object);
233 ubus_unregister_subscriber(&conn.ctx, &netifd_sub);
234 blob_buf_init(&b, 0);
235 if (ubus_lookup_id(&conn.ctx, "network.device", &id) == 0)
236 ubus_invoke(&conn.ctx, id, "stp_init", b.head, NULL, NULL, 1000);
237 ubus_auto_shutdown(&conn);
238 }