1 /* SPDX-License-Identifier: LGPL-2.1-or-later
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 "netem.h"
9 #include "netlink-util.h"
10 #include "networkd-manager.h"
11 #include "parse-util.h"
12 #include "qdisc.h"
13 #include "strv.h"
14 #include "tc-util.h"
15
network_emulator_fill_message(Link * link,QDisc * qdisc,sd_netlink_message * req)16 static int network_emulator_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) {
17 NetworkEmulator *ne;
18 int r;
19
20 assert(link);
21 assert(qdisc);
22 assert(req);
23
24 assert_se(ne = NETEM(qdisc));
25
26 struct tc_netem_qopt opt = {
27 .limit = ne->limit > 0 ? ne->limit : 1000,
28 .loss = ne->loss,
29 .duplicate = ne->duplicate,
30 };
31
32 if (ne->delay != USEC_INFINITY) {
33 r = tc_time_to_tick(ne->delay, &opt.latency);
34 if (r < 0)
35 return log_link_error_errno(link, r, "Failed to calculate latency in TCA_OPTION: %m");
36 }
37
38 if (ne->jitter != USEC_INFINITY) {
39 r = tc_time_to_tick(ne->jitter, &opt.jitter);
40 if (r < 0)
41 return log_link_error_errno(link, r, "Failed to calculate jitter in TCA_OPTION: %m");
42 }
43
44 r = sd_netlink_message_append_data(req, TCA_OPTIONS, &opt, sizeof(opt));
45 if (r < 0)
46 return r;
47
48 return 0;
49 }
50
config_parse_network_emulator_delay(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)51 int config_parse_network_emulator_delay(
52 const char *unit,
53 const char *filename,
54 unsigned line,
55 const char *section,
56 unsigned section_line,
57 const char *lvalue,
58 int ltype,
59 const char *rvalue,
60 void *data,
61 void *userdata) {
62
63 _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
64 Network *network = data;
65 NetworkEmulator *ne;
66 usec_t u;
67 int r;
68
69 assert(filename);
70 assert(lvalue);
71 assert(rvalue);
72 assert(data);
73
74 r = qdisc_new_static(QDISC_KIND_NETEM, network, filename, section_line, &qdisc);
75 if (r == -ENOMEM)
76 return log_oom();
77 if (r < 0) {
78 log_syntax(unit, LOG_WARNING, filename, line, r,
79 "More than one kind of queueing discipline, ignoring assignment: %m");
80 return 0;
81 }
82
83 ne = NETEM(qdisc);
84
85 if (isempty(rvalue)) {
86 if (STR_IN_SET(lvalue, "DelaySec", "NetworkEmulatorDelaySec"))
87 ne->delay = USEC_INFINITY;
88 else if (STR_IN_SET(lvalue, "DelayJitterSec", "NetworkEmulatorDelayJitterSec"))
89 ne->jitter = USEC_INFINITY;
90
91 TAKE_PTR(qdisc);
92 return 0;
93 }
94
95 r = parse_sec(rvalue, &u);
96 if (r < 0) {
97 log_syntax(unit, LOG_WARNING, filename, line, r,
98 "Failed to parse '%s=', ignoring assignment: %s",
99 lvalue, rvalue);
100 return 0;
101 }
102
103 if (STR_IN_SET(lvalue, "DelaySec", "NetworkEmulatorDelaySec"))
104 ne->delay = u;
105 else if (STR_IN_SET(lvalue, "DelayJitterSec", "NetworkEmulatorDelayJitterSec"))
106 ne->jitter = u;
107
108 TAKE_PTR(qdisc);
109
110 return 0;
111 }
112
config_parse_network_emulator_rate(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)113 int config_parse_network_emulator_rate(
114 const char *unit,
115 const char *filename,
116 unsigned line,
117 const char *section,
118 unsigned section_line,
119 const char *lvalue,
120 int ltype,
121 const char *rvalue,
122 void *data,
123 void *userdata) {
124
125 _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
126 Network *network = data;
127 NetworkEmulator *ne;
128 uint32_t rate;
129 int r;
130
131 assert(filename);
132 assert(lvalue);
133 assert(rvalue);
134 assert(data);
135
136 r = qdisc_new_static(QDISC_KIND_NETEM, network, filename, section_line, &qdisc);
137 if (r == -ENOMEM)
138 return log_oom();
139 if (r < 0) {
140 log_syntax(unit, LOG_WARNING, filename, line, r,
141 "More than one kind of queueing discipline, ignoring assignment: %m");
142 return 0;
143 }
144
145 ne = NETEM(qdisc);
146
147 if (isempty(rvalue)) {
148 if (STR_IN_SET(lvalue, "LossRate", "NetworkEmulatorLossRate"))
149 ne->loss = 0;
150 else if (STR_IN_SET(lvalue, "DuplicateRate", "NetworkEmulatorDuplicateRate"))
151 ne->duplicate = 0;
152
153 TAKE_PTR(qdisc);
154 return 0;
155 }
156
157 r = parse_tc_percent(rvalue, &rate);
158 if (r < 0) {
159 log_syntax(unit, LOG_WARNING, filename, line, r,
160 "Failed to parse '%s=', ignoring assignment: %s",
161 lvalue, rvalue);
162 return 0;
163 }
164
165 if (STR_IN_SET(lvalue, "LossRate", "NetworkEmulatorLossRate"))
166 ne->loss = rate;
167 else if (STR_IN_SET(lvalue, "DuplicateRate", "NetworkEmulatorDuplicateRate"))
168 ne->duplicate = rate;
169
170 TAKE_PTR(qdisc);
171 return 0;
172 }
173
config_parse_network_emulator_packet_limit(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)174 int config_parse_network_emulator_packet_limit(
175 const char *unit,
176 const char *filename,
177 unsigned line,
178 const char *section,
179 unsigned section_line,
180 const char *lvalue,
181 int ltype,
182 const char *rvalue,
183 void *data,
184 void *userdata) {
185
186 _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
187 Network *network = data;
188 NetworkEmulator *ne;
189 int r;
190
191 assert(filename);
192 assert(lvalue);
193 assert(rvalue);
194 assert(data);
195
196 r = qdisc_new_static(QDISC_KIND_NETEM, network, filename, section_line, &qdisc);
197 if (r == -ENOMEM)
198 return log_oom();
199 if (r < 0) {
200 log_syntax(unit, LOG_WARNING, filename, line, r,
201 "More than one kind of queueing discipline, ignoring assignment: %m");
202 return 0;
203 }
204
205 ne = NETEM(qdisc);
206
207 if (isempty(rvalue)) {
208 ne->limit = 0;
209
210 TAKE_PTR(qdisc);
211 return 0;
212 }
213
214 r = safe_atou(rvalue, &ne->limit);
215 if (r < 0) {
216 log_syntax(unit, LOG_WARNING, filename, line, r,
217 "Failed to parse '%s=', ignoring assignment: %s",
218 lvalue, rvalue);
219 return 0;
220 }
221
222 TAKE_PTR(qdisc);
223 return 0;
224 }
225
226 const QDiscVTable netem_vtable = {
227 .object_size = sizeof(NetworkEmulator),
228 .tca_kind = "netem",
229 .fill_message = network_emulator_fill_message,
230 };
231