]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/tc/qdisc.c
network: wait for QDiscs to be configured
[thirdparty/systemd.git] / src / network / tc / qdisc.c
1 /* SPDX-License-Identifier: LGPL-2.1+
2 * Copyright © 2019 VMware, Inc. */
3
4 #include <linux/pkt_sched.h>
5
6 #include "alloc-util.h"
7 #include "conf-parser.h"
8 #include "in-addr-util.h"
9 #include "netlink-util.h"
10 #include "networkd-manager.h"
11 #include "parse-util.h"
12 #include "qdisc.h"
13 #include "set.h"
14 #include "string-util.h"
15 #include "util.h"
16
17 static int qdisc_new(QDiscs **ret) {
18 QDiscs *qdisc;
19
20 qdisc = new(QDiscs, 1);
21 if (!qdisc)
22 return -ENOMEM;
23
24 *qdisc = (QDiscs) {
25 .family = AF_UNSPEC,
26 .parent = TC_H_ROOT,
27 };
28
29 *ret = TAKE_PTR(qdisc);
30
31 return 0;
32 }
33
34 int qdisc_new_static(Network *network, const char *filename, unsigned section_line, QDiscs **ret) {
35 _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
36 _cleanup_(qdisc_freep) QDiscs *qdisc = NULL;
37 int r;
38
39 assert(network);
40 assert(ret);
41 assert(!!filename == (section_line > 0));
42
43 if (filename) {
44 r = network_config_section_new(filename, section_line, &n);
45 if (r < 0)
46 return r;
47
48 qdisc = ordered_hashmap_get(network->qdiscs_by_section, n);
49 if (qdisc) {
50 *ret = TAKE_PTR(qdisc);
51
52 return 0;
53 }
54 }
55
56 r = qdisc_new(&qdisc);
57 if (r < 0)
58 return r;
59
60 qdisc->network = network;
61
62 if (filename) {
63 qdisc->section = TAKE_PTR(n);
64
65 r = ordered_hashmap_ensure_allocated(&network->qdiscs_by_section, &network_config_hash_ops);
66 if (r < 0)
67 return r;
68
69 r = ordered_hashmap_put(network->qdiscs_by_section, qdisc->section, qdisc);
70 if (r < 0)
71 return r;
72 }
73
74 *ret = TAKE_PTR(qdisc);
75
76 return 0;
77 }
78
79 void qdisc_free(QDiscs *qdisc) {
80 if (!qdisc)
81 return;
82
83 if (qdisc->network && qdisc->section)
84 ordered_hashmap_remove(qdisc->network->qdiscs_by_section, qdisc->section);
85
86 network_config_section_free(qdisc->section);
87
88 free(qdisc);
89 }
90
91 static int qdisc_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
92 int r;
93
94 assert(link);
95 assert(link->qdisc_messages > 0);
96 link->qdisc_messages--;
97
98 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
99 return 1;
100
101 r = sd_netlink_message_get_errno(m);
102 if (r < 0 && r != -EEXIST) {
103 log_link_error_errno(link, r, "Could not set QDisc: %m");
104 link_enter_failed(link);
105 return 1;
106 }
107
108 if (link->route_messages == 0) {
109 log_link_debug(link, "QDiscs configured");
110 link->qdiscs_configured = true;
111 link_check_ready(link);
112 }
113
114 return 1;
115 }
116
117 int qdisc_configure(Link *link, QDiscs *qdisc) {
118 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
119 int r;
120
121 assert(link);
122 assert(link->manager);
123 assert(link->manager->rtnl);
124 assert(link->ifindex > 0);
125
126 r = sd_rtnl_message_new_qdisc(link->manager->rtnl, &req, RTM_NEWQDISC, qdisc->family, link->ifindex);
127 if (r < 0)
128 return log_link_error_errno(link, r, "Could not create RTM_NEWQDISC message: %m");
129
130 r = sd_rtnl_message_set_qdisc_parent(req, qdisc->parent);
131 if (r < 0)
132 return log_link_error_errno(link, r, "Could not create tcm_parent message: %m");
133
134 if (qdisc->parent == TC_H_CLSACT) {
135 r = sd_rtnl_message_set_qdisc_handle(req, TC_H_MAKE(TC_H_CLSACT, 0));
136 if (r < 0)
137 return log_link_error_errno(link, r, "Could not set tcm_handle message: %m");
138
139 r = sd_netlink_message_append_string(req, TCA_KIND, "clsact");
140 if (r < 0)
141 return log_link_error_errno(link, r, "Could not append TCA_KIND attribute: %m");
142 }
143
144 if (qdisc->has_network_emulator) {
145 r = sd_netlink_message_append_string(req, TCA_KIND, "netem");
146 if (r < 0)
147 return log_link_error_errno(link, r, "Could not append TCA_KIND attribute: %m");
148
149 r = network_emulator_fill_message(link, qdisc, req);
150 if (r < 0)
151 return r;
152 }
153
154 r = netlink_call_async(link->manager->rtnl, NULL, req, qdisc_handler, link_netlink_destroy_callback, link);
155 if (r < 0)
156 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
157
158 link_ref(link);
159 link->qdisc_messages++;
160
161 return 0;
162 }
163
164 int config_parse_tc_qdiscs_parent(
165 const char *unit,
166 const char *filename,
167 unsigned line,
168 const char *section,
169 unsigned section_line,
170 const char *lvalue,
171 int ltype,
172 const char *rvalue,
173 void *data,
174 void *userdata) {
175
176 _cleanup_(qdisc_free_or_set_invalidp) QDiscs *qdisc = NULL;
177 Network *network = data;
178 int r;
179
180 assert(filename);
181 assert(lvalue);
182 assert(rvalue);
183 assert(data);
184
185 r = qdisc_new_static(network, filename, section_line, &qdisc);
186 if (r < 0)
187 return r;
188
189 if (streq(rvalue, "root"))
190 qdisc->parent = TC_H_ROOT;
191 else if (streq(rvalue, "clsact"))
192 qdisc->parent = TC_H_CLSACT;
193 else {
194 log_syntax(unit, LOG_ERR, filename, line, r,
195 "Failed to parse [QueueDiscs] 'Parent=', ignoring assignment: %s",
196 rvalue);
197 return 0;
198 }
199
200 qdisc = NULL;
201
202 return 0;
203 }