oops
[libnl.git] / lib / route / sch / netem.c
1 /*
2  * lib/route/sch/netem.c                Network Emulator Qdisc
3  *
4  *      This library is free software; you can redistribute it and/or
5  *      modify it under the terms of the GNU Lesser General Public
6  *      License as published by the Free Software Foundation version 2.1
7  *      of the License.
8  *
9  * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
10  */
11
12 /**
13  * @ingroup qdisc
14  * @defgroup netem Network Emulator
15  * @brief
16  *
17  * For further documentation see http://linux-net.osdl.org/index.php/Netem
18  * @{
19  */
20
21 #include <netlink-local.h>
22 #include <netlink-tc.h>
23 #include <netlink/netlink.h>
24 #include <netlink/utils.h>
25 #include <netlink/route/qdisc.h>
26 #include <netlink/route/qdisc-modules.h>
27 #include <netlink/route/sch/netem.h>
28
29 /** @cond SKIP */
30 #define SCH_NETEM_ATTR_LATENCY          0x001
31 #define SCH_NETEM_ATTR_LIMIT            0x002
32 #define SCH_NETEM_ATTR_LOSS             0x004
33 #define SCH_NETEM_ATTR_GAP              0x008
34 #define SCH_NETEM_ATTR_DUPLICATE        0x010
35 #define SCH_NETEM_ATTR_JITTER           0x020
36 #define SCH_NETEM_ATTR_DELAY_CORR       0x040
37 #define SCH_NETEM_ATTR_LOSS_CORR        0x080
38 #define SCH_NETEM_ATTR_DUP_CORR         0x100
39 #define SCH_NETEM_ATTR_RO_PROB          0x200
40 #define SCH_NETEM_ATTR_RO_CORR          0x400
41 /** @endcond */
42
43 static inline struct rtnl_netem *netem_qdisc(struct rtnl_qdisc *qdisc)
44 {
45         return (struct rtnl_netem *) qdisc->q_subdata;
46 }
47
48 static inline struct rtnl_netem *netem_alloc(struct rtnl_qdisc *qdisc)
49 {
50         if (!qdisc->q_subdata)
51                 qdisc->q_subdata = calloc(1, sizeof(struct rtnl_netem));
52
53         return netem_qdisc(qdisc);
54 }
55
56 static struct nla_policy netem_policy[TCA_NETEM_MAX+1] = {
57         [TCA_NETEM_CORR]        = { .minlen = sizeof(struct tc_netem_corr) },
58         [TCA_NETEM_REORDER]     = { .minlen = sizeof(struct tc_netem_reorder) },
59 };
60
61 static int netem_msg_parser(struct rtnl_qdisc *qdisc)
62 {
63         int len, err = 0;
64         struct rtnl_netem *netem;
65         struct tc_netem_qopt *opts;
66
67         if (qdisc->q_opts->d_size < sizeof(*opts))
68                 return nl_error(EINVAL, "Netem specific options size mismatch");
69
70         netem = netem_alloc(qdisc);
71         if (!netem)
72                 return nl_errno(ENOMEM);
73
74         opts = (struct tc_netem_qopt *) qdisc->q_opts->d_data;
75         netem->qnm_latency = opts->latency;
76         netem->qnm_limit = opts->limit;
77         netem->qnm_loss = opts->loss;
78         netem->qnm_gap = opts->gap;
79         netem->qnm_duplicate = opts->duplicate;
80         netem->qnm_jitter = opts->jitter;
81
82         netem->qnm_mask = (SCH_NETEM_ATTR_LATENCY | SCH_NETEM_ATTR_LIMIT |
83                            SCH_NETEM_ATTR_LOSS | SCH_NETEM_ATTR_GAP |
84                            SCH_NETEM_ATTR_DUPLICATE | SCH_NETEM_ATTR_JITTER);
85
86         len = qdisc->q_opts->d_size - sizeof(*opts);
87
88         if (len > 0) {
89                 struct nlattr *tb[TCA_NETEM_MAX+1];
90
91                 err = nla_parse(tb, TCA_NETEM_MAX, (struct nlattr *)
92                                 qdisc->q_opts->d_data + sizeof(*opts),
93                                 len, netem_policy);
94                 if (err < 0) {
95                         free(netem);
96                         return err;
97                 }
98
99                 if (tb[TCA_NETEM_CORR]) {
100                         struct tc_netem_corr cor;
101
102                         nla_memcpy(&cor, tb[TCA_NETEM_CORR], sizeof(cor));
103                         netem->qnm_corr.nmc_delay = cor.delay_corr;
104                         netem->qnm_corr.nmc_loss = cor.loss_corr;
105                         netem->qnm_corr.nmc_duplicate = cor.dup_corr;
106
107                         netem->qnm_mask |= (SCH_NETEM_ATTR_DELAY_CORR |
108                                             SCH_NETEM_ATTR_LOSS_CORR |
109                                             SCH_NETEM_ATTR_DELAY_CORR);
110                 }
111
112                 if (tb[TCA_NETEM_REORDER]) {
113                         struct tc_netem_reorder ro;
114
115                         nla_memcpy(&ro, tb[TCA_NETEM_REORDER], sizeof(ro));
116                         netem->qnm_ro.nmro_probability = ro.probability;
117                         netem->qnm_ro.nmro_correlation = ro.correlation;
118
119                         netem->qnm_mask |= (SCH_NETEM_ATTR_RO_PROB |
120                                             SCH_NETEM_ATTR_RO_CORR);
121                 }
122         }
123
124         return 0;
125 }
126
127 static void netem_free_data(struct rtnl_qdisc *qdisc)
128 {
129         free(qdisc->q_subdata);
130 }
131
132 static int netem_dump_brief(struct rtnl_qdisc *qdisc, struct nl_dump_params *p,
133                             int line)
134 {
135         struct rtnl_netem *netem = netem_qdisc(qdisc);
136
137         if (netem)
138                 dp_dump(p, "limit %d", netem->qnm_limit);
139
140         return line;
141 }
142
143 static int netem_dump_full(struct rtnl_qdisc *qdisc, struct nl_dump_params *p,
144                            int line)
145 {
146         return line;
147 }
148
149 static struct nl_msg *netem_get_opts(struct rtnl_qdisc *qdisc)
150 {
151         return NULL;
152 }
153
154 /**
155  * @name Queue Limit
156  * @{
157  */
158
159 /**
160  * Set limit of netem qdisc.
161  * @arg qdisc           Netem qdisc to be modified.
162  * @arg limit           New limit in bytes.
163  * @return 0 on success or a negative error code.
164  */
165 int rtnl_netem_set_limit(struct rtnl_qdisc *qdisc, int limit)
166 {
167         struct rtnl_netem *netem;
168
169         netem = netem_alloc(qdisc);
170         if (!netem)
171                 return nl_errno(ENOMEM);
172         
173         netem->qnm_limit = limit;
174         netem->qnm_mask |= SCH_NETEM_ATTR_LIMIT;
175
176         return 0;
177 }
178
179 /**
180  * Get limit of netem qdisc.
181  * @arg qdisc           Netem qdisc.
182  * @return Limit in bytes or a negative error code.
183  */
184 int rtnl_netem_get_limit(struct rtnl_qdisc *qdisc)
185 {
186         struct rtnl_netem *netem;
187
188         netem = netem_qdisc(qdisc);
189         if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_LIMIT))
190                 return netem->qnm_limit;
191         else
192                 return nl_errno(ENOENT);
193 }
194
195 /** @} */
196
197 /**
198  * @name Packet Re-ordering
199  * @{
200  */
201
202 /**
203  * Set re-ordering gap of netem qdisc.
204  * @arg qdisc           Netem qdisc to be modified.
205  * @arg gap             New gap in number of packets.
206  * @return 0 on success or a negative error code.
207  */
208 int rtnl_netem_set_gap(struct rtnl_qdisc *qdisc, int gap)
209 {
210         struct rtnl_netem *netem;
211
212         netem = netem_alloc(qdisc);
213         if (!netem)
214                 return nl_errno(ENOMEM);
215
216         netem->qnm_gap = gap;
217         netem->qnm_mask |= SCH_NETEM_ATTR_GAP;
218
219         return 0;
220 }
221
222 /**
223  * Get re-ordering gap of netem qdisc.
224  * @arg qdisc           Netem qdisc.
225  * @return Re-ordering gap in packets or a negative error code.
226  */
227 int rtnl_netem_get_gap(struct rtnl_qdisc *qdisc)
228 {
229         struct rtnl_netem *netem;
230
231         netem = netem_qdisc(qdisc);
232         if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_GAP))
233                 return netem->qnm_gap;
234         else
235                 return nl_errno(ENOENT);
236 }
237
238 /**
239  * Set re-ordering probability of netem qdisc.
240  * @arg qdisc           Netem qdisc to be modified.
241  * @arg prob            New re-ordering probability.
242  * @return 0 on success or a negative error code.
243  */
244 int rtnl_netem_set_reorder_probability(struct rtnl_qdisc *qdisc, int prob)
245 {
246         struct rtnl_netem *netem;
247
248         netem = netem_alloc(qdisc);
249         if (!netem)
250                 return nl_errno(ENOMEM);
251
252         netem->qnm_ro.nmro_probability = prob;
253         netem->qnm_mask |= SCH_NETEM_ATTR_RO_PROB;
254
255         return 0;
256 }
257
258 /**
259  * Get re-ordering probability of netem qdisc.
260  * @arg qdisc           Netem qdisc.
261  * @return Re-ordering probability or a negative error code.
262  */
263 int rtnl_netem_get_reorder_probability(struct rtnl_qdisc *qdisc)
264 {
265         struct rtnl_netem *netem;
266
267         netem = netem_qdisc(qdisc);
268         if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_RO_PROB))
269                 return netem->qnm_ro.nmro_probability;
270         else
271                 return nl_errno(ENOENT);
272 }
273
274 /**
275  * Set re-order correlation probability of netem qdisc.
276  * @arg qdisc           Netem qdisc to be modified.
277  * @arg prob            New re-ordering correlation probability.
278  * @return 0 on success or a negative error code.
279  */
280 int rtnl_netem_set_reorder_correlation(struct rtnl_qdisc *qdisc, int prob)
281 {
282         struct rtnl_netem *netem;
283
284         netem = netem_alloc(qdisc);
285         if (!netem)
286                 return nl_errno(ENOMEM);
287
288         netem->qnm_ro.nmro_correlation = prob;
289         netem->qnm_mask |= SCH_NETEM_ATTR_RO_CORR;
290
291         return 0;
292 }
293
294 /**
295  * Get re-ordering correlation probability of netem qdisc.
296  * @arg qdisc           Netem qdisc.
297  * @return Re-ordering correlation probability or a negative error code.
298  */
299 int rtnl_netem_get_reorder_correlation(struct rtnl_qdisc *qdisc)
300 {
301         struct rtnl_netem *netem;
302
303         netem = netem_qdisc(qdisc);
304         if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_RO_CORR))
305                 return netem->qnm_ro.nmro_correlation;
306         else
307                 return nl_errno(ENOENT);
308 }
309
310 /** @} */
311
312 /**
313  * @name Packet Loss
314  * @{
315  */
316
317 /**
318  * Set packet loss probability of netem qdisc.
319  * @arg qdisc           Netem qdisc to be modified.
320  * @arg prob            New packet loss probability.
321  * @return 0 on success or a negative error code.
322  */
323 int rtnl_netem_set_loss(struct rtnl_qdisc *qdisc, int prob)
324 {
325         struct rtnl_netem *netem;
326
327         netem = netem_alloc(qdisc);
328         if (!netem)
329                 return nl_errno(ENOMEM);
330
331         netem->qnm_loss = prob;
332         netem->qnm_mask |= SCH_NETEM_ATTR_LOSS;
333
334         return 0;
335 }
336
337 /**
338  * Get packet loss probability of netem qdisc.
339  * @arg qdisc           Netem qdisc.
340  * @return Packet loss probability or a negative error code.
341  */
342 int rtnl_netem_get_loss(struct rtnl_qdisc *qdisc)
343 {
344         struct rtnl_netem *netem;
345
346         netem = netem_qdisc(qdisc);
347         if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_LOSS))
348                 return netem->qnm_loss;
349         else
350                 return nl_errno(ENOENT);
351 }
352
353 /**
354  * Set packet loss correlation probability of netem qdisc.
355  * @arg qdisc           Netem qdisc to be modified.
356  * @arg prob    New packet loss correlation.
357  * @return 0 on success or a negative error code.
358  */
359 int rtnl_netem_set_loss_correlation(struct rtnl_qdisc *qdisc, int prob)
360 {
361         struct rtnl_netem *netem;
362
363         netem = netem_alloc(qdisc);
364         if (!netem)
365                 return nl_errno(ENOMEM);
366
367         netem->qnm_corr.nmc_loss = prob;
368         netem->qnm_mask |= SCH_NETEM_ATTR_LOSS_CORR;
369
370         return 0;
371 }
372
373 /**
374  * Get packet loss correlation probability of netem qdisc.
375  * @arg qdisc           Netem qdisc.
376  * @return Packet loss correlation probability or a negative error code.
377  */
378 int rtnl_netem_get_loss_correlation(struct rtnl_qdisc *qdisc)
379 {
380         struct rtnl_netem *netem;
381
382         netem = netem_qdisc(qdisc);
383         if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_LOSS_CORR))
384                 return netem->qnm_corr.nmc_loss;
385         else
386                 return nl_errno(ENOENT);
387 }
388
389 /** @} */
390
391 /**
392  * @name Packet Duplication
393  * @{
394  */
395
396 /**
397  * Set packet duplication probability of netem qdisc.
398  * @arg qdisc           Netem qdisc to be modified.
399  * @arg prob    New packet duplication probability.
400  * @return 0 on success or a negative error code.
401  */
402 int rtnl_netem_set_duplicate(struct rtnl_qdisc *qdisc, int prob)
403 {
404         struct rtnl_netem *netem;
405
406         netem = netem_alloc(qdisc);
407         if (!netem)
408                 return nl_errno(ENOMEM);
409
410         netem->qnm_duplicate = prob;
411         netem->qnm_mask |= SCH_NETEM_ATTR_DUPLICATE;
412
413         return 0;
414 }
415
416 /**
417  * Get packet duplication probability of netem qdisc.
418  * @arg qdisc           Netem qdisc.
419  * @return Packet duplication probability or a negative error code.
420  */
421 int rtnl_netem_get_duplicate(struct rtnl_qdisc *qdisc)
422 {
423         struct rtnl_netem *netem;
424
425         netem = netem_qdisc(qdisc);
426         if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DUPLICATE))
427                 return netem->qnm_duplicate;
428         else
429                 return nl_errno(ENOENT);
430 }
431
432 /**
433  * Set packet duplication correlation probability of netem qdisc.
434  * @arg qdisc           Netem qdisc to be modified.
435  * @arg prob            New packet duplication correlation probability.
436  * @return 0 on sucess or a negative error code.
437  */
438 int rtnl_netem_set_duplicate_correlation(struct rtnl_qdisc *qdisc, int prob)
439 {
440         struct rtnl_netem *netem;
441
442         netem = netem_alloc(qdisc);
443         if (!netem)
444                 return nl_errno(ENOMEM);
445
446         netem->qnm_corr.nmc_duplicate = prob;
447         netem->qnm_mask |= SCH_NETEM_ATTR_DUP_CORR;
448
449         return 0;
450 }
451
452 /**
453  * Get packet duplication correlation probability of netem qdisc.
454  * @arg qdisc           Netem qdisc.
455  * @return Packet duplication correlation probability or a negative error code.
456  */
457 int rtnl_netem_get_duplicate_correlation(struct rtnl_qdisc *qdisc)
458 {
459         struct rtnl_netem *netem;
460
461         netem = netem_qdisc(qdisc);
462         if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DUP_CORR))
463                 return netem->qnm_corr.nmc_duplicate;
464         else
465                 return nl_errno(ENOENT);
466 }
467
468 /** @} */
469
470 /**
471  * @name Packet Delay
472  * @{
473  */
474
475 /**
476  * Set packet delay of netem qdisc.
477  * @arg qdisc           Netem qdisc to be modified.
478  * @arg delay           New packet delay in micro seconds.
479  * @return 0 on success or a negative error code.
480  */
481 int rtnl_netem_set_delay(struct rtnl_qdisc *qdisc, int delay)
482 {
483         struct rtnl_netem *netem;
484
485         netem = netem_alloc(qdisc);
486         if (!netem)
487                 return nl_errno(ENOMEM);
488
489         netem->qnm_latency = nl_us2ticks(delay);
490         netem->qnm_mask |= SCH_NETEM_ATTR_LATENCY;
491
492         return 0;
493 }
494
495 /**
496  * Get packet delay of netem qdisc.
497  * @arg qdisc           Netem qdisc.
498  * @return Packet delay in micro seconds or a negative error code.
499  */
500 int rtnl_netem_get_delay(struct rtnl_qdisc *qdisc)
501 {
502         struct rtnl_netem *netem;
503
504         netem = netem_qdisc(qdisc);
505         if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_LATENCY))
506                 return nl_ticks2us(netem->qnm_latency);
507         else
508                 return nl_errno(ENOENT);
509 }
510
511 /**
512  * Set packet delay jitter of netem qdisc.
513  * @arg qdisc           Netem qdisc to be modified.
514  * @arg jitter          New packet delay jitter in micro seconds.
515  * @return 0 on success or a negative error code.
516  */
517 int rtnl_netem_set_jitter(struct rtnl_qdisc *qdisc, int jitter)
518 {
519         struct rtnl_netem *netem;
520
521         netem = netem_alloc(qdisc);
522         if (!netem)
523                 return nl_errno(ENOMEM);
524
525         netem->qnm_jitter = nl_us2ticks(jitter);
526         netem->qnm_mask |= SCH_NETEM_ATTR_JITTER;
527
528         return 0;
529 }
530
531 /**
532  * Get packet delay jitter of netem qdisc.
533  * @arg qdisc           Netem qdisc.
534  * @return Packet delay jitter in micro seconds or a negative error code.
535  */
536 int rtnl_netem_get_jitter(struct rtnl_qdisc *qdisc)
537 {
538         struct rtnl_netem *netem;
539
540         netem = netem_qdisc(qdisc);
541         if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_JITTER))
542                 return nl_ticks2us(netem->qnm_jitter);
543         else
544                 return nl_errno(ENOENT);
545 }
546
547 /**
548  * Set packet delay correlation probability of netem qdisc.
549  * @arg qdisc           Netem qdisc to be modified.
550  * @arg prob            New packet delay correlation probability.
551  */
552 int rtnl_netem_set_delay_correlation(struct rtnl_qdisc *qdisc, int prob)
553 {
554         struct rtnl_netem *netem;
555
556         netem = netem_alloc(qdisc);
557         if (!netem)
558                 return nl_errno(ENOMEM);
559
560         netem->qnm_corr.nmc_delay = prob;
561         netem->qnm_mask |= SCH_NETEM_ATTR_DELAY_CORR;
562
563         return 0;
564 }
565
566 /**
567  * Get packet delay correlation probability of netem qdisc.
568  * @arg qdisc           Netem qdisc.
569  * @return Packet delay correlation probability or a negative error code.
570  */
571 int rtnl_netem_get_delay_corellation(struct rtnl_qdisc *qdisc)
572 {
573         struct rtnl_netem *netem;
574
575         netem = netem_qdisc(qdisc);
576         if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DELAY_CORR))
577                 return netem->qnm_corr.nmc_delay;
578         else
579                 return nl_errno(ENOENT);
580 }
581
582 /** @} */
583
584 static struct rtnl_qdisc_ops netem_ops = {
585         .qo_kind                = "netem",
586         .qo_msg_parser          = netem_msg_parser,
587         .qo_free_data           = netem_free_data,
588         .qo_dump[NL_DUMP_BRIEF] = netem_dump_brief,
589         .qo_dump[NL_DUMP_FULL]  = netem_dump_full,
590         .qo_get_opts            = netem_get_opts,
591 };
592
593 static void __init netem_init(void)
594 {
595         rtnl_qdisc_register(&netem_ops);
596 }
597
598 static void __exit netem_exit(void)
599 {
600         rtnl_qdisc_unregister(&netem_ops);
601 }
602
603 /** @} */