netem.c 14 KB

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