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
, struct qosify_dscp_val val
,
13 enum qosify_map_id id
)
15 struct blob_attr
*cur
;
18 if (blobmsg_check_array(attr
, BLOBMSG_TYPE_STRING
) < 0)
19 return UBUS_STATUS_INVALID_ARGUMENT
;
21 blobmsg_for_each_attr(cur
, attr
, rem
)
22 qosify_map_set_entry(id
, false, blobmsg_get_string(cur
), val
);
28 qosify_ubus_set_files(struct blob_attr
*attr
)
30 struct blob_attr
*cur
;
33 if (blobmsg_check_array(attr
, BLOBMSG_TYPE_STRING
) < 0)
34 return UBUS_STATUS_INVALID_ARGUMENT
;
36 qosify_map_clear_files();
38 blobmsg_for_each_attr(cur
, attr
, rem
)
39 qosify_map_load_file(blobmsg_get_string(cur
));
58 static const struct blobmsg_policy qosify_add_policy
[__CL_ADD_MAX
] = {
59 [CL_ADD_DSCP
] = { "dscp", BLOBMSG_TYPE_STRING
},
60 [CL_ADD_TIMEOUT
] = { "timeout", BLOBMSG_TYPE_INT32
},
61 [CL_ADD_IPV4
] = { "ipv4", BLOBMSG_TYPE_ARRAY
},
62 [CL_ADD_IPV6
] = { "ipv6", BLOBMSG_TYPE_ARRAY
},
63 [CL_ADD_TCP_PORT
] = { "tcp_port", BLOBMSG_TYPE_ARRAY
},
64 [CL_ADD_UDP_PORT
] = { "udp_port", BLOBMSG_TYPE_ARRAY
},
65 [CL_ADD_DNS
] = { "dns", BLOBMSG_TYPE_ARRAY
},
70 qosify_ubus_reload(struct ubus_context
*ctx
, struct ubus_object
*obj
,
71 struct ubus_request_data
*req
, const char *method
,
72 struct blob_attr
*msg
)
80 qosify_ubus_add(struct ubus_context
*ctx
, struct ubus_object
*obj
,
81 struct ubus_request_data
*req
, const char *method
,
82 struct blob_attr
*msg
)
84 int prev_timemout
= qosify_map_timeout
;
85 struct blob_attr
*tb
[__CL_ADD_MAX
];
86 struct blob_attr
*cur
;
87 struct qosify_dscp_val dscp
= { 0xff, 0xff };
90 blobmsg_parse(qosify_add_policy
, __CL_ADD_MAX
, tb
,
91 blobmsg_data(msg
), blobmsg_len(msg
));
93 if (!strcmp(method
, "add")) {
94 if ((cur
= tb
[CL_ADD_DSCP
]) == NULL
||
95 qosify_map_dscp_value(blobmsg_get_string(cur
), &dscp
))
96 return UBUS_STATUS_INVALID_ARGUMENT
;
98 if ((cur
= tb
[CL_ADD_TIMEOUT
]) != NULL
)
99 qosify_map_timeout
= blobmsg_get_u32(cur
);
102 if ((cur
= tb
[CL_ADD_IPV4
]) != NULL
&&
103 (ret
= qosify_ubus_add_array(cur
, dscp
, CL_MAP_IPV4_ADDR
) != 0))
106 if ((cur
= tb
[CL_ADD_IPV6
]) != NULL
&&
107 (ret
= qosify_ubus_add_array(cur
, dscp
, CL_MAP_IPV6_ADDR
) != 0))
110 if ((cur
= tb
[CL_ADD_TCP_PORT
]) != NULL
&&
111 (ret
= qosify_ubus_add_array(cur
, dscp
, CL_MAP_TCP_PORTS
) != 0))
114 if ((cur
= tb
[CL_ADD_UDP_PORT
]) != NULL
&&
115 (ret
= qosify_ubus_add_array(cur
, dscp
, CL_MAP_UDP_PORTS
) != 0))
118 if ((cur
= tb
[CL_ADD_DNS
]) != NULL
&&
119 (ret
= qosify_ubus_add_array(cur
, dscp
, CL_MAP_DNS
) != 0))
122 qosify_map_timeout
= prev_timemout
;
136 CL_CONFIG_BULK_TIMEOUT
,
138 CL_CONFIG_PRIO_PKT_LEN
,
139 CL_CONFIG_INTERFACES
,
145 static const struct blobmsg_policy qosify_config_policy
[__CL_CONFIG_MAX
] = {
146 [CL_CONFIG_RESET
] = { "reset", BLOBMSG_TYPE_BOOL
},
147 [CL_CONFIG_FILES
] = { "files", BLOBMSG_TYPE_ARRAY
},
148 [CL_CONFIG_TIMEOUT
] = { "timeout", BLOBMSG_TYPE_INT32
},
149 [CL_CONFIG_DSCP_UDP
] = { "dscp_default_udp", BLOBMSG_TYPE_STRING
},
150 [CL_CONFIG_DSCP_TCP
] = { "dscp_default_tcp", BLOBMSG_TYPE_STRING
},
151 [CL_CONFIG_DSCP_PRIO
] = { "dscp_prio", BLOBMSG_TYPE_STRING
},
152 [CL_CONFIG_DSCP_BULK
] = { "dscp_bulk", BLOBMSG_TYPE_STRING
},
153 [CL_CONFIG_DSCP_ICMP
] = { "dscp_icmp", BLOBMSG_TYPE_STRING
},
154 [CL_CONFIG_BULK_TIMEOUT
] = { "bulk_trigger_timeout", BLOBMSG_TYPE_INT32
},
155 [CL_CONFIG_BULK_PPS
] = { "bulk_trigger_pps", BLOBMSG_TYPE_INT32
},
156 [CL_CONFIG_PRIO_PKT_LEN
] = { "prio_max_avg_pkt_len", BLOBMSG_TYPE_INT32
},
157 [CL_CONFIG_INTERFACES
] = { "interfaces", BLOBMSG_TYPE_TABLE
},
158 [CL_CONFIG_DEVICES
] = { "devices", BLOBMSG_TYPE_TABLE
},
159 [CL_CONFIG_ALIASES
] = { "aliases", BLOBMSG_TYPE_TABLE
},
162 static int __set_dscp(struct qosify_dscp_val
*dest
, struct blob_attr
*attr
, bool reset
)
165 dest
->ingress
= 0xff;
172 if (qosify_map_dscp_value(blobmsg_get_string(attr
), dest
))
179 qosify_ubus_config(struct ubus_context
*ctx
, struct ubus_object
*obj
,
180 struct ubus_request_data
*req
, const char *method
,
181 struct blob_attr
*msg
)
183 struct blob_attr
*tb
[__CL_CONFIG_MAX
];
184 struct blob_attr
*cur
;
185 struct qosify_dscp_val dscp
;
189 blobmsg_parse(qosify_config_policy
, __CL_CONFIG_MAX
, tb
,
190 blobmsg_data(msg
), blobmsg_len(msg
));
192 if ((cur
= tb
[CL_CONFIG_RESET
]) != NULL
)
193 reset
= blobmsg_get_bool(cur
);
196 qosify_map_reset_config();
198 if ((cur
= tb
[CL_CONFIG_ALIASES
]) != NULL
|| reset
)
199 qosify_map_set_aliases(cur
);
201 if ((cur
= tb
[CL_CONFIG_TIMEOUT
]) != NULL
)
202 qosify_map_timeout
= blobmsg_get_u32(cur
);
204 if ((cur
= tb
[CL_CONFIG_FILES
]) != NULL
&&
205 (ret
= qosify_ubus_set_files(cur
) != 0))
208 __set_dscp(&dscp
, tb
[CL_CONFIG_DSCP_UDP
], true);
209 if (dscp
.ingress
!= 0xff)
210 qosify_map_set_dscp_default(CL_MAP_UDP_PORTS
, dscp
);
212 __set_dscp(&dscp
, tb
[CL_CONFIG_DSCP_TCP
], true);
213 if (dscp
.ingress
!= 0xff)
214 qosify_map_set_dscp_default(CL_MAP_TCP_PORTS
, dscp
);
216 if (__set_dscp(&config
.dscp_prio
, tb
[CL_CONFIG_DSCP_PRIO
], reset
) ||
217 __set_dscp(&config
.dscp_bulk
, tb
[CL_CONFIG_DSCP_BULK
], reset
) ||
218 __set_dscp(&config
.dscp_icmp
, tb
[CL_CONFIG_DSCP_ICMP
], reset
))
219 return UBUS_STATUS_INVALID_ARGUMENT
;
221 if ((cur
= tb
[CL_CONFIG_BULK_TIMEOUT
]) != NULL
)
222 config
.bulk_trigger_timeout
= blobmsg_get_u32(cur
);
224 if ((cur
= tb
[CL_CONFIG_BULK_PPS
]) != NULL
)
225 config
.bulk_trigger_pps
= blobmsg_get_u32(cur
);
227 if ((cur
= tb
[CL_CONFIG_PRIO_PKT_LEN
]) != NULL
)
228 config
.prio_max_avg_pkt_len
= blobmsg_get_u32(cur
);
230 qosify_map_update_config();
232 qosify_iface_config_update(tb
[CL_CONFIG_INTERFACES
], tb
[CL_CONFIG_DEVICES
]);
234 qosify_iface_check();
241 qosify_ubus_dump(struct ubus_context
*ctx
, struct ubus_object
*obj
,
242 struct ubus_request_data
*req
, const char *method
,
243 struct blob_attr
*msg
)
245 blob_buf_init(&b
, 0);
247 ubus_send_reply(ctx
, req
, b
.head
);
254 qosify_ubus_status(struct ubus_context
*ctx
, struct ubus_object
*obj
,
255 struct ubus_request_data
*req
, const char *method
,
256 struct blob_attr
*msg
)
258 blob_buf_init(&b
, 0);
259 qosify_iface_status(&b
);
260 ubus_send_reply(ctx
, req
, b
.head
);
267 qosify_ubus_check_devices(struct ubus_context
*ctx
, struct ubus_object
*obj
,
268 struct ubus_request_data
*req
, const char *method
,
269 struct blob_attr
*msg
)
271 qosify_iface_check();
284 static const struct blobmsg_policy qosify_dns_policy
[__CL_DNS_HOST_MAX
] = {
285 [CL_DNS_HOST_NAME
] = { "name", BLOBMSG_TYPE_STRING
},
286 [CL_DNS_HOST_TYPE
] = { "type", BLOBMSG_TYPE_STRING
},
287 [CL_DNS_HOST_ADDR
] = { "address", BLOBMSG_TYPE_STRING
},
288 [CL_DNS_HOST_TTL
] = { "ttl", BLOBMSG_TYPE_INT32
},
292 __qosify_ubus_add_dns_host(struct blob_attr
*msg
)
294 struct blob_attr
*tb
[__CL_DNS_HOST_MAX
];
295 struct blob_attr
*cur
;
298 blobmsg_parse(qosify_dns_policy
, __CL_DNS_HOST_MAX
, tb
,
299 blobmsg_data(msg
), blobmsg_len(msg
));
301 if (!tb
[CL_DNS_HOST_NAME
] || !tb
[CL_DNS_HOST_TYPE
] ||
302 !tb
[CL_DNS_HOST_ADDR
])
303 return UBUS_STATUS_INVALID_ARGUMENT
;
305 if ((cur
= tb
[CL_DNS_HOST_TTL
]) != NULL
)
306 ttl
= blobmsg_get_u32(cur
);
308 if (qosify_map_add_dns_host(blobmsg_get_string(tb
[CL_DNS_HOST_NAME
]),
309 blobmsg_get_string(tb
[CL_DNS_HOST_ADDR
]),
310 blobmsg_get_string(tb
[CL_DNS_HOST_TYPE
]),
312 return UBUS_STATUS_INVALID_ARGUMENT
;
318 qosify_ubus_add_dns_host(struct ubus_context
*ctx
, struct ubus_object
*obj
,
319 struct ubus_request_data
*req
, const char *method
,
320 struct blob_attr
*msg
)
322 return __qosify_ubus_add_dns_host(msg
);
325 static const struct ubus_method qosify_methods
[] = {
326 UBUS_METHOD_NOARG("reload", qosify_ubus_reload
),
327 UBUS_METHOD("add", qosify_ubus_add
, qosify_add_policy
),
328 UBUS_METHOD_MASK("remove", qosify_ubus_add
, qosify_add_policy
,
329 ((1 << __CL_ADD_MAX
) - 1) & ~(1 << CL_ADD_DSCP
)),
330 UBUS_METHOD("config", qosify_ubus_config
, qosify_config_policy
),
331 UBUS_METHOD_NOARG("dump", qosify_ubus_dump
),
332 UBUS_METHOD_NOARG("status", qosify_ubus_status
),
333 UBUS_METHOD("add_dns_host", qosify_ubus_add_dns_host
, qosify_dns_policy
),
334 UBUS_METHOD_NOARG("check_devices", qosify_ubus_check_devices
),
337 static struct ubus_object_type qosify_object_type
=
338 UBUS_OBJECT_TYPE("qosify", qosify_methods
);
340 static struct ubus_object qosify_object
= {
342 .type
= &qosify_object_type
,
343 .methods
= qosify_methods
,
344 .n_methods
= ARRAY_SIZE(qosify_methods
),
348 qosify_dnsmasq_cb(struct ubus_context
*ctx
, struct ubus_object
*obj
,
349 struct ubus_request_data
*req
, const char *method
,
350 struct blob_attr
*msg
)
352 if (!strcmp(method
, "dns_result"))
353 __qosify_ubus_add_dns_host(msg
);
359 qosify_subscribe_dnsmasq(struct ubus_context
*ctx
)
361 static struct ubus_subscriber sub
= {
362 .cb
= qosify_dnsmasq_cb
,
367 ubus_register_subscriber(ctx
, &sub
))
370 if (ubus_lookup_id(ctx
, "dnsmasq.dns", &id
))
373 ubus_subscribe(ctx
, &sub
, id
);
377 qosify_ubus_event_cb(struct ubus_context
*ctx
, struct ubus_event_handler
*ev
,
378 const char *type
, struct blob_attr
*msg
)
380 static const struct blobmsg_policy policy
=
381 { "path", BLOBMSG_TYPE_STRING
};
382 struct blob_attr
*attr
;
385 blobmsg_parse(&policy
, 1, &attr
, blobmsg_data(msg
), blobmsg_len(msg
));
390 path
= blobmsg_get_string(attr
);
391 if (!strcmp(path
, "dnsmasq.dns"))
392 qosify_subscribe_dnsmasq(ctx
);
397 ubus_connect_handler(struct ubus_context
*ctx
)
399 static struct ubus_event_handler ev
= {
400 .cb
= qosify_ubus_event_cb
403 ubus_add_object(ctx
, &qosify_object
);
404 ubus_register_event_handler(ctx
, &ev
, "ubus.object.add");
405 qosify_subscribe_dnsmasq(ctx
);
408 static struct ubus_auto_conn conn
;
410 int qosify_ubus_init(void)
412 conn
.cb
= ubus_connect_handler
;
413 ubus_auto_connect(&conn
);
418 void qosify_ubus_stop(void)
420 ubus_auto_shutdown(&conn
);
429 netifd_if_cb(struct ubus_request
*req
, int type
, struct blob_attr
*msg
)
431 struct iface_req
*ifr
= req
->priv
;
437 static const struct blobmsg_policy policy
[__IFS_ATTR_MAX
] = {
438 [IFS_ATTR_UP
] = { "up", BLOBMSG_TYPE_BOOL
},
439 [IFS_ATTR_DEV
] = { "l3_device", BLOBMSG_TYPE_STRING
},
441 struct blob_attr
*tb
[__IFS_ATTR_MAX
];
443 blobmsg_parse(policy
, __IFS_ATTR_MAX
, tb
, blobmsg_data(msg
), blobmsg_len(msg
));
445 if (!tb
[IFS_ATTR_UP
] || !tb
[IFS_ATTR_DEV
])
448 if (!blobmsg_get_bool(tb
[IFS_ATTR_UP
]))
451 snprintf(ifr
->name
, ifr
->len
, "%s", blobmsg_get_string(tb
[IFS_ATTR_DEV
]));
454 int qosify_ubus_check_interface(const char *name
, char *ifname
, int ifname_len
)
456 struct iface_req req
= { ifname
, ifname_len
};
457 char *obj_name
= "network.interface.";
460 #define PREFIX "network.interface."
461 obj_name
= alloca(sizeof(PREFIX
) + strlen(name
) + 1);
462 sprintf(obj_name
, PREFIX
"%s", name
);
467 if (ubus_lookup_id(&conn
.ctx
, obj_name
, &id
))
470 ubus_invoke(&conn
.ctx
, id
, "status", b
.head
, netifd_if_cb
, &req
, 1000);