1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (C) 2021 Felix Fietkau <nbd@nbd.name>
9 static struct blob_buf b
;
12 qosify_ubus_add_array(struct blob_attr
*attr
, uint8_t val
, enum qosify_map_id id
)
14 struct blob_attr
*cur
;
17 if (blobmsg_check_array(attr
, BLOBMSG_TYPE_STRING
) < 0)
18 return UBUS_STATUS_INVALID_ARGUMENT
;
20 blobmsg_for_each_attr(cur
, attr
, rem
)
21 qosify_map_set_entry(id
, false, blobmsg_get_string(cur
), val
);
27 qosify_ubus_set_files(struct blob_attr
*attr
)
29 struct blob_attr
*cur
;
32 if (blobmsg_check_array(attr
, BLOBMSG_TYPE_STRING
) < 0)
33 return UBUS_STATUS_INVALID_ARGUMENT
;
35 qosify_map_clear_files();
37 blobmsg_for_each_attr(cur
, attr
, rem
)
38 qosify_map_load_file(blobmsg_get_string(cur
));
57 static const struct blobmsg_policy qosify_add_policy
[__CL_ADD_MAX
] = {
58 [CL_ADD_DSCP
] = { "dscp", BLOBMSG_TYPE_STRING
},
59 [CL_ADD_TIMEOUT
] = { "timeout", BLOBMSG_TYPE_INT32
},
60 [CL_ADD_IPV4
] = { "ipv4", BLOBMSG_TYPE_ARRAY
},
61 [CL_ADD_IPV6
] = { "ipv6", BLOBMSG_TYPE_ARRAY
},
62 [CL_ADD_TCP_PORT
] = { "tcp_port", BLOBMSG_TYPE_ARRAY
},
63 [CL_ADD_UDP_PORT
] = { "udp_port", BLOBMSG_TYPE_ARRAY
},
64 [CL_ADD_DNS
] = { "dns", BLOBMSG_TYPE_ARRAY
},
69 qosify_ubus_reload(struct ubus_context
*ctx
, struct ubus_object
*obj
,
70 struct ubus_request_data
*req
, const char *method
,
71 struct blob_attr
*msg
)
79 qosify_ubus_add(struct ubus_context
*ctx
, struct ubus_object
*obj
,
80 struct ubus_request_data
*req
, const char *method
,
81 struct blob_attr
*msg
)
83 int prev_timemout
= qosify_map_timeout
;
84 struct blob_attr
*tb
[__CL_ADD_MAX
];
85 struct blob_attr
*cur
;
89 blobmsg_parse(qosify_add_policy
, __CL_ADD_MAX
, tb
,
90 blobmsg_data(msg
), blobmsg_len(msg
));
92 if (!strcmp(method
, "add")) {
93 if ((cur
= tb
[CL_ADD_DSCP
]) == NULL
||
94 qosify_map_dscp_value(blobmsg_get_string(cur
), &dscp
))
95 return UBUS_STATUS_INVALID_ARGUMENT
;
97 if ((cur
= tb
[CL_ADD_TIMEOUT
]) != NULL
)
98 qosify_map_timeout
= blobmsg_get_u32(cur
);
101 if ((cur
= tb
[CL_ADD_IPV4
]) != NULL
&&
102 (ret
= qosify_ubus_add_array(cur
, dscp
, CL_MAP_IPV4_ADDR
) != 0))
105 if ((cur
= tb
[CL_ADD_IPV6
]) != NULL
&&
106 (ret
= qosify_ubus_add_array(cur
, dscp
, CL_MAP_IPV6_ADDR
) != 0))
109 if ((cur
= tb
[CL_ADD_TCP_PORT
]) != NULL
&&
110 (ret
= qosify_ubus_add_array(cur
, dscp
, CL_MAP_TCP_PORTS
) != 0))
113 if ((cur
= tb
[CL_ADD_UDP_PORT
]) != NULL
&&
114 (ret
= qosify_ubus_add_array(cur
, dscp
, CL_MAP_UDP_PORTS
) != 0))
117 if ((cur
= tb
[CL_ADD_DNS
]) != NULL
&&
118 (ret
= qosify_ubus_add_array(cur
, dscp
, CL_MAP_DNS
) != 0))
121 qosify_map_timeout
= prev_timemout
;
133 CL_CONFIG_INTERFACES
,
139 static const struct blobmsg_policy qosify_config_policy
[__CL_CONFIG_MAX
] = {
140 [CL_CONFIG_RESET
] = { "reset", BLOBMSG_TYPE_BOOL
},
141 [CL_CONFIG_FILES
] = { "files", BLOBMSG_TYPE_ARRAY
},
142 [CL_CONFIG_TIMEOUT
] = { "timeout", BLOBMSG_TYPE_INT32
},
143 [CL_CONFIG_DSCP_UDP
] = { "dscp_default_udp", BLOBMSG_TYPE_STRING
},
144 [CL_CONFIG_DSCP_TCP
] = { "dscp_default_tcp", BLOBMSG_TYPE_STRING
},
145 [CL_CONFIG_DSCP_ICMP
] = { "dscp_icmp", BLOBMSG_TYPE_STRING
},
146 [CL_CONFIG_INTERFACES
] = { "interfaces", BLOBMSG_TYPE_TABLE
},
147 [CL_CONFIG_DEVICES
] = { "devices", BLOBMSG_TYPE_TABLE
},
148 [CL_CONFIG_CLASSES
] = { "classes", BLOBMSG_TYPE_TABLE
},
152 qosify_ubus_config(struct ubus_context
*ctx
, struct ubus_object
*obj
,
153 struct ubus_request_data
*req
, const char *method
,
154 struct blob_attr
*msg
)
156 struct blob_attr
*tb
[__CL_CONFIG_MAX
];
157 struct blob_attr
*cur
;
162 blobmsg_parse(qosify_config_policy
, __CL_CONFIG_MAX
, tb
,
163 blobmsg_data(msg
), blobmsg_len(msg
));
165 if ((cur
= tb
[CL_CONFIG_RESET
]) != NULL
)
166 reset
= blobmsg_get_bool(cur
);
169 qosify_map_reset_config();
171 if ((cur
= tb
[CL_CONFIG_CLASSES
]) != NULL
|| reset
)
172 qosify_map_set_classes(cur
);
174 if ((cur
= tb
[CL_CONFIG_TIMEOUT
]) != NULL
)
175 qosify_map_timeout
= blobmsg_get_u32(cur
);
177 if ((cur
= tb
[CL_CONFIG_FILES
]) != NULL
&&
178 (ret
= qosify_ubus_set_files(cur
) != 0))
181 if (map_parse_flow_config(&flow_config
, msg
, reset
) ||
182 map_fill_dscp_value(&config
.dscp_icmp
, tb
[CL_CONFIG_DSCP_ICMP
], reset
))
183 return UBUS_STATUS_INVALID_ARGUMENT
;
185 map_fill_dscp_value(&dscp
, tb
[CL_CONFIG_DSCP_UDP
], true);
187 qosify_map_set_dscp_default(CL_MAP_UDP_PORTS
, dscp
);
189 map_fill_dscp_value(&dscp
, tb
[CL_CONFIG_DSCP_TCP
], true);
191 qosify_map_set_dscp_default(CL_MAP_TCP_PORTS
, dscp
);
193 qosify_map_update_config();
195 qosify_iface_config_update(tb
[CL_CONFIG_INTERFACES
], tb
[CL_CONFIG_DEVICES
]);
197 qosify_iface_check();
204 qosify_ubus_dump(struct ubus_context
*ctx
, struct ubus_object
*obj
,
205 struct ubus_request_data
*req
, const char *method
,
206 struct blob_attr
*msg
)
208 blob_buf_init(&b
, 0);
210 ubus_send_reply(ctx
, req
, b
.head
);
217 qosify_ubus_status(struct ubus_context
*ctx
, struct ubus_object
*obj
,
218 struct ubus_request_data
*req
, const char *method
,
219 struct blob_attr
*msg
)
221 blob_buf_init(&b
, 0);
222 qosify_iface_status(&b
);
223 ubus_send_reply(ctx
, req
, b
.head
);
230 qosify_ubus_get_stats(struct ubus_context
*ctx
, struct ubus_object
*obj
,
231 struct ubus_request_data
*req
, const char *method
,
232 struct blob_attr
*msg
)
234 static const struct blobmsg_policy policy
=
235 { "reset", BLOBMSG_TYPE_BOOL
};
236 struct blob_attr
*tb
;
239 blobmsg_parse(&policy
, 1, &tb
, blobmsg_data(msg
), blobmsg_len(msg
));
241 reset
= tb
&& blobmsg_get_u8(tb
);
243 blob_buf_init(&b
, 0);
244 qosify_map_stats(&b
, reset
);
245 ubus_send_reply(ctx
, req
, b
.head
);
252 qosify_ubus_check_devices(struct ubus_context
*ctx
, struct ubus_object
*obj
,
253 struct ubus_request_data
*req
, const char *method
,
254 struct blob_attr
*msg
)
256 qosify_iface_check();
269 static const struct blobmsg_policy qosify_dns_policy
[__CL_DNS_HOST_MAX
] = {
270 [CL_DNS_HOST_NAME
] = { "name", BLOBMSG_TYPE_STRING
},
271 [CL_DNS_HOST_TYPE
] = { "type", BLOBMSG_TYPE_STRING
},
272 [CL_DNS_HOST_ADDR
] = { "address", BLOBMSG_TYPE_STRING
},
273 [CL_DNS_HOST_TTL
] = { "ttl", BLOBMSG_TYPE_INT32
},
277 __qosify_ubus_add_dns_host(struct blob_attr
*msg
)
279 struct blob_attr
*tb
[__CL_DNS_HOST_MAX
];
280 struct blob_attr
*cur
;
283 blobmsg_parse(qosify_dns_policy
, __CL_DNS_HOST_MAX
, tb
,
284 blobmsg_data(msg
), blobmsg_len(msg
));
286 if (!tb
[CL_DNS_HOST_NAME
] || !tb
[CL_DNS_HOST_TYPE
] ||
287 !tb
[CL_DNS_HOST_ADDR
])
288 return UBUS_STATUS_INVALID_ARGUMENT
;
290 if ((cur
= tb
[CL_DNS_HOST_TTL
]) != NULL
)
291 ttl
= blobmsg_get_u32(cur
);
293 if (qosify_map_add_dns_host(blobmsg_get_string(tb
[CL_DNS_HOST_NAME
]),
294 blobmsg_get_string(tb
[CL_DNS_HOST_ADDR
]),
295 blobmsg_get_string(tb
[CL_DNS_HOST_TYPE
]),
297 return UBUS_STATUS_INVALID_ARGUMENT
;
303 qosify_ubus_add_dns_host(struct ubus_context
*ctx
, struct ubus_object
*obj
,
304 struct ubus_request_data
*req
, const char *method
,
305 struct blob_attr
*msg
)
307 return __qosify_ubus_add_dns_host(msg
);
310 static const struct ubus_method qosify_methods
[] = {
311 UBUS_METHOD_NOARG("reload", qosify_ubus_reload
),
312 UBUS_METHOD("add", qosify_ubus_add
, qosify_add_policy
),
313 UBUS_METHOD_MASK("remove", qosify_ubus_add
, qosify_add_policy
,
314 ((1 << __CL_ADD_MAX
) - 1) & ~(1 << CL_ADD_DSCP
)),
315 UBUS_METHOD("config", qosify_ubus_config
, qosify_config_policy
),
316 UBUS_METHOD_NOARG("dump", qosify_ubus_dump
),
317 UBUS_METHOD_NOARG("status", qosify_ubus_status
),
318 UBUS_METHOD_NOARG("get_stats", qosify_ubus_get_stats
),
319 UBUS_METHOD("add_dns_host", qosify_ubus_add_dns_host
, qosify_dns_policy
),
320 UBUS_METHOD_NOARG("check_devices", qosify_ubus_check_devices
),
323 static struct ubus_object_type qosify_object_type
=
324 UBUS_OBJECT_TYPE("qosify", qosify_methods
);
326 static struct ubus_object qosify_object
= {
328 .type
= &qosify_object_type
,
329 .methods
= qosify_methods
,
330 .n_methods
= ARRAY_SIZE(qosify_methods
),
334 qosify_subscribe_dnsmasq(struct ubus_context
*ctx
)
336 static struct ubus_subscriber sub
= {
337 .cb
= qosify_ubus_add_dns_host
,
342 ubus_register_subscriber(ctx
, &sub
))
345 if (ubus_lookup_id(ctx
, "dnsmasq.dns", &id
))
348 ubus_subscribe(ctx
, &sub
, id
);
352 qosify_ubus_event_cb(struct ubus_context
*ctx
, struct ubus_event_handler
*ev
,
353 const char *type
, struct blob_attr
*msg
)
355 static const struct blobmsg_policy policy
=
356 { "path", BLOBMSG_TYPE_STRING
};
357 struct blob_attr
*attr
;
360 blobmsg_parse(&policy
, 1, &attr
, blobmsg_data(msg
), blobmsg_len(msg
));
365 path
= blobmsg_get_string(attr
);
366 if (!strcmp(path
, "dnsmasq.dns"))
367 qosify_subscribe_dnsmasq(ctx
);
368 else if (!strcmp(path
, "bridger"))
369 qosify_ubus_update_bridger(false);
374 ubus_connect_handler(struct ubus_context
*ctx
)
376 static struct ubus_event_handler ev
= {
377 .cb
= qosify_ubus_event_cb
380 ubus_add_object(ctx
, &qosify_object
);
381 ubus_register_event_handler(ctx
, &ev
, "ubus.object.add");
382 qosify_subscribe_dnsmasq(ctx
);
385 static struct ubus_auto_conn conn
;
387 void qosify_ubus_update_bridger(bool shutdown
)
389 struct ubus_request req
;
393 if (ubus_lookup_id(&conn
.ctx
, "bridger", &id
))
396 blob_buf_init(&b
, 0);
397 blobmsg_add_string(&b
, "name", "qosify");
398 c
= blobmsg_open_array(&b
, "devices");
400 qosify_iface_get_devices(&b
);
401 blobmsg_close_array(&b
, c
);
403 ubus_invoke_async(&conn
.ctx
, id
, "set_blacklist", b
.head
, &req
);
406 int qosify_ubus_init(void)
408 conn
.cb
= ubus_connect_handler
;
409 ubus_auto_connect(&conn
);
414 void qosify_ubus_stop(void)
416 qosify_ubus_update_bridger(true);
417 ubus_auto_shutdown(&conn
);
426 netifd_if_cb(struct ubus_request
*req
, int type
, struct blob_attr
*msg
)
428 struct iface_req
*ifr
= req
->priv
;
434 static const struct blobmsg_policy policy
[__IFS_ATTR_MAX
] = {
435 [IFS_ATTR_UP
] = { "up", BLOBMSG_TYPE_BOOL
},
436 [IFS_ATTR_DEV
] = { "l3_device", BLOBMSG_TYPE_STRING
},
438 struct blob_attr
*tb
[__IFS_ATTR_MAX
];
440 blobmsg_parse(policy
, __IFS_ATTR_MAX
, tb
, blobmsg_data(msg
), blobmsg_len(msg
));
442 if (!tb
[IFS_ATTR_UP
] || !tb
[IFS_ATTR_DEV
])
445 if (!blobmsg_get_bool(tb
[IFS_ATTR_UP
]))
448 snprintf(ifr
->name
, ifr
->len
, "%s", blobmsg_get_string(tb
[IFS_ATTR_DEV
]));
451 int qosify_ubus_check_interface(const char *name
, char *ifname
, int ifname_len
)
453 struct iface_req req
= { ifname
, ifname_len
};
454 char *obj_name
= "network.interface.";
457 #define PREFIX "network.interface."
458 obj_name
= alloca(sizeof(PREFIX
) + strlen(name
) + 1);
459 sprintf(obj_name
, PREFIX
"%s", name
);
464 if (ubus_lookup_id(&conn
.ctx
, obj_name
, &id
))
467 blob_buf_init(&b
, 0);
468 ubus_invoke(&conn
.ctx
, id
, "status", b
.head
, netifd_if_cb
, &req
, 1000);