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