#include <net/pkt_sched.h>
+#define VERSION "1.2"
+
/* Network Emulation Queuing algorithm.
====================================
u32 jitter;
u32 duplicate;
u32 reorder;
+ u32 corrupt;
struct crndstate {
unsigned long last;
unsigned long rho;
- } delay_cor, loss_cor, dup_cor, reorder_cor;
+ } delay_cor, loss_cor, dup_cor, reorder_cor, corrupt_cor;
struct disttable {
u32 size;
q->duplicate = dupsave;
}
+ /*
+ * Randomized packet corruption.
+ * Make copy if needed since we are modifying
+ * If packet is going to be hardware checksummed, then
+ * do it now in software before we mangle it.
+ */
+ if (q->corrupt && q->corrupt >= get_crandom(&q->corrupt_cor)) {
+ if (!(skb = skb_unshare(skb, GFP_ATOMIC))
+ || (skb->ip_summed == CHECKSUM_HW
+ && skb_checksum_help(skb, 0))) {
+ sch->qstats.drops++;
+ return NET_XMIT_DROP;
+ }
+
+ skb->data[net_random() % skb_headlen(skb)] ^= 1<<(net_random() % 8);
+ }
+
if (q->gap == 0 /* not doing reordering */
|| q->counter < q->gap /* inside last reordering gap */
|| q->reorder < get_crandom(&q->reorder_cor)) {
static unsigned int netem_drop(struct Qdisc* sch)
{
struct netem_sched_data *q = qdisc_priv(sch);
- unsigned int len;
+ unsigned int len = 0;
- if ((len = q->qdisc->ops->drop(q->qdisc)) != 0) {
+ if (q->qdisc->ops->drop && (len = q->qdisc->ops->drop(q->qdisc)) != 0) {
sch->q.qlen--;
sch->qstats.drops++;
}
return 0;
}
+static int get_corrupt(struct Qdisc *sch, const struct rtattr *attr)
+{
+ struct netem_sched_data *q = qdisc_priv(sch);
+ const struct tc_netem_corrupt *r = RTA_DATA(attr);
+
+ if (RTA_PAYLOAD(attr) != sizeof(*r))
+ return -EINVAL;
+
+ q->corrupt = r->probability;
+ init_crandom(&q->corrupt_cor, r->correlation);
+ return 0;
+}
+
+/* Parse netlink message to set options */
static int netem_change(struct Qdisc *sch, struct rtattr *opt)
{
struct netem_sched_data *q = qdisc_priv(sch);
if (ret)
return ret;
}
+
if (tb[TCA_NETEM_REORDER-1]) {
ret = get_reorder(sch, tb[TCA_NETEM_REORDER-1]);
if (ret)
return ret;
}
- }
+ if (tb[TCA_NETEM_CORRUPT-1]) {
+ ret = get_corrupt(sch, tb[TCA_NETEM_CORRUPT-1]);
+ if (ret)
+ return ret;
+ }
+ }
return 0;
}
const struct netem_skb_cb *cb
= (const struct netem_skb_cb *)skb->cb;
- if (PSCHED_TLESS(cb->time_to_send, ncb->time_to_send))
+ if (!PSCHED_TLESS(ncb->time_to_send, cb->time_to_send))
break;
}
struct tc_netem_qopt qopt;
struct tc_netem_corr cor;
struct tc_netem_reorder reorder;
+ struct tc_netem_corrupt corrupt;
qopt.latency = q->latency;
qopt.jitter = q->jitter;
reorder.correlation = q->reorder_cor.rho;
RTA_PUT(skb, TCA_NETEM_REORDER, sizeof(reorder), &reorder);
+ corrupt.probability = q->corrupt;
+ corrupt.correlation = q->corrupt_cor.rho;
+ RTA_PUT(skb, TCA_NETEM_CORRUPT, sizeof(corrupt), &corrupt);
+
rta->rta_len = skb->tail - b;
return skb->len;
static int __init netem_module_init(void)
{
+ pr_info("netem: version " VERSION "\n");
return register_qdisc(&netem_qdisc_ops);
}
static void __exit netem_module_exit(void)