diff --git a/sbin/ipfw/dummynet.c b/sbin/ipfw/dummynet.c --- a/sbin/ipfw/dummynet.c +++ b/sbin/ipfw/dummynet.c @@ -471,7 +471,7 @@ { int l; char qs[30]; - char plr[30]; + char plr[40]; char red[200]; /* Display RED parameters */ l = fs->qsize; @@ -482,9 +482,17 @@ sprintf(qs, "%d B", l); } else sprintf(qs, "%3d sl.", l); - if (fs->plr) - sprintf(plr, "plr %f", 1.0 * fs->plr / (double)(0x7fffffff)); - else + if (fs->plr[0] || fs->plr[1]) { + if (fs->plr[1] == 0) + sprintf(plr, "plr %f", + 1.0 * fs->plr[0] / (double)(0x7fffffff)); + else + sprintf(plr, "plr %f,%f,%f,%f", + 1.0 * fs->plr[0] / (double)(0x7fffffff), + 1.0 * fs->plr[1] / (double)(0x7fffffff), + 1.0 * fs->plr[2] / (double)(0x7fffffff), + 1.0 * fs->plr[3] / (double)(0x7fffffff)); + } else plr[0] = '\0'; if (fs->flags & DN_IS_RED) { /* RED parameters */ @@ -1408,13 +1416,27 @@ case TOK_PLR: NEED(fs, "plr is only for pipes"); - NEED1("plr needs argument 0..1\n"); - d = strtod(av[0], NULL); - if (d > 1) - d = 1; - else if (d < 0) - d = 0; - fs->plr = (int)(d*0x7fffffff); + NEED1("plr needs one or four arguments 0..1\n"); + if ((end = strsep(&av[0], ","))) { + d = strtod(end, NULL); + d = (d < 0) ? 0 : (d <= 1) ? d : 1; + fs->plr[0] = (int)(d*0x7fffffff); + } + if ((end = strsep(&av[0], ","))) { + d = strtod(end, NULL); + d = (d < 0) ? 0 : (d <= 1) ? d : 1; + fs->plr[1] = (int)(d*0x7fffffff); + } + if ((end = strsep(&av[0], ","))) { + d = strtod(end, NULL); + d = (d < 0) ? 0 : (d <= 1) ? d : 1; + fs->plr[2] = (int)(d*0x7fffffff); + } + if ((end = strsep(&av[0], ","))) { + d = strtod(end, NULL); + d = (d < 0) ? 0 : (d <= 1) ? d : 1; + fs->plr[3] = (int)(d*0x7fffffff); + } ac--; av++; break; diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8 --- a/sbin/ipfw/ipfw.8 +++ b/sbin/ipfw/ipfw.8 @@ -1,5 +1,5 @@ .\" -.Dd September 28, 2023 +.Dd December 9, 2023 .Dt IPFW 8 .Os .Sh NAME @@ -3039,12 +3039,41 @@ loss or congestion at a remote router. .Pp .It Cm plr Ar packet-loss-rate +.It Cm plr Ar k,p,h,r Packet loss rate. Argument .Ar packet-loss-rate is a floating-point number between 0 and 1, with 0 meaning no loss, 1 meaning 100% loss. -The loss rate is internally represented on 31 bits. +.Pp +When invoked with four arguments, the simple Gilbert-Elliott +channel model with two states (Good and Bad) is used. +.Bd -literal -offset indent + r + .----------------. + v | + .------------. .------------. + | G | | B | + | drop (1-k) | | drop (1-h) | + '------------' '------------' + | ^ + '----------------' + p + +.Ed +This has the associated probabilities +.Po Ar k +and +.Ar h Pc +for the probability of successful transmission. This is inverted +from the simple loss probability when giving only a single argument, +to stay in line with the literature of this model. In addition the +state change probabilities +.Po Ar p +and +.Ar r Pc +are given. +All of the above probabilities are internally represented on 31 bits. .Pp .It Cm queue Brq Ar slots | size Ns Cm Kbytes Queue size, in diff --git a/sys/netinet/ip_dummynet.h b/sys/netinet/ip_dummynet.h --- a/sys/netinet/ip_dummynet.h +++ b/sys/netinet/ip_dummynet.h @@ -145,7 +145,7 @@ uint32_t fs_nr; /* the flowset number */ uint32_t flags; /* userland flags */ int qsize; /* queue size in slots or bytes */ - int32_t plr; /* PLR, pkt loss rate (2^31-1 means 100%) */ + int32_t pl_state; /* packet loss state */ uint32_t buckets; /* buckets used for the queue hash table */ struct ipfw_flow_id flow_mask; @@ -168,6 +168,7 @@ int min_th ; /* minimum threshold for queue (scaled) */ int max_p ; /* maximum value for p_b (scaled) */ + int32_t plr[4]; /* PLR, pkt loss rate (2^31-1 means 100%) */ }; /* diff --git a/sys/netpfil/ipfw/ip_dn_glue.c b/sys/netpfil/ipfw/ip_dn_glue.c --- a/sys/netpfil/ipfw/ip_dn_glue.c +++ b/sys/netpfil/ipfw/ip_dn_glue.c @@ -77,35 +77,35 @@ /* Common to 7.2 and 8 */ struct dn_flow_set { - SLIST_ENTRY(dn_flow_set) next; /* linked list in a hash slot */ + SLIST_ENTRY(dn_flow_set) next; /* linked list in a hash slot */ - u_short fs_nr ; /* flow_set number */ + u_short fs_nr ; /* flow_set number */ u_short flags_fs; #define DNOLD_HAVE_FLOW_MASK 0x0001 -#define DNOLD_IS_RED 0x0002 +#define DNOLD_IS_RED 0x0002 #define DNOLD_IS_GENTLE_RED 0x0004 -#define DNOLD_QSIZE_IS_BYTES 0x0008 /* queue size is measured in bytes */ -#define DNOLD_NOERROR 0x0010 /* do not report ENOBUFS on drops */ -#define DNOLD_HAS_PROFILE 0x0020 /* the pipe has a delay profile. */ -#define DNOLD_IS_PIPE 0x4000 -#define DNOLD_IS_QUEUE 0x8000 +#define DNOLD_QSIZE_IS_BYTES 0x0008 /* queue size is measured in bytes */ +#define DNOLD_NOERROR 0x0010 /* do not report ENOBUFS on drops */ +#define DNOLD_HAS_PROFILE 0x0020 /* the pipe has a delay profile. */ +#define DNOLD_IS_PIPE 0x4000 +#define DNOLD_IS_QUEUE 0x8000 - struct dn_pipe7 *pipe ; /* pointer to parent pipe */ - u_short parent_nr ; /* parent pipe#, 0 if local to a pipe */ + struct dn_pipe7 *pipe ; /* pointer to parent pipe */ + u_short parent_nr ; /* parent pipe#, 0 if local to a pipe */ - int weight ; /* WFQ queue weight */ - int qsize ; /* queue size in slots or bytes */ - int plr ; /* pkt loss rate (2^31-1 means 100%) */ + int weight ; /* WFQ queue weight */ + int qsize ; /* queue size in slots or bytes */ + int plr[4] ; /* pkt loss rate (2^31-1 means 100%) */ struct ipfw_flow_id flow_mask ; /* hash table of queues onto this flow_set */ - int rq_size ; /* number of slots */ - int rq_elements ; /* active elements */ - struct dn_flow_queue7 **rq; /* array of rq_size entries */ + int rq_size ; /* number of slots */ + int rq_elements ; /* active elements */ + struct dn_flow_queue7 **rq ; /* array of rq_size entries */ - u_int32_t last_expired ; /* do not expire too frequently */ - int backlogged ; /* #active queues for this flowset */ + u_int32_t last_expired ; /* do not expire too frequently */ + int backlogged ; /* #active queues for this flowset */ /* RED parameters */ #define SCALE_RED 16 @@ -420,7 +420,10 @@ fs->flow_mask = f->flow_mask; fs->buckets = f->rq_size; fs->qsize = f->qsize; - fs->plr = f->plr; + fs->plr[0] = f->plr[0]; + fs->plr[1] = f->plr[1]; + fs->plr[2] = f->plr[2]; + fs->plr[3] = f->plr[3]; fs->par[0] = f->weight; fs->flags = convertflags2new(f->flags_fs); if (fs->flags & DN_IS_GENTLE_RED || fs->flags & DN_IS_RED) { @@ -645,7 +648,10 @@ fs->parent_nr = l->link_nr - DN_MAX_ID; fs->qsize = f->fs.qsize; - fs->plr = f->fs.plr; + fs->plr[0] = f->fs.plr[0]; + fs->plr[1] = f->fs.plr[1]; + fs->plr[2] = f->fs.plr[2]; + fs->plr[3] = f->fs.plr[3]; fs->w_q = f->fs.w_q; fs->max_th = f->max_th; fs->min_th = f->min_th; @@ -698,7 +704,10 @@ fs->next.sle_next = (struct dn_flow_set *)DN_IS_QUEUE; fs->fs_nr = f->fs.fs_nr; fs->qsize = f->fs.qsize; - fs->plr = f->fs.plr; + fs->plr[0] = f->fs.plr[0]; + fs->plr[1] = f->fs.plr[1]; + fs->plr[2] = f->fs.plr[2]; + fs->plr[3] = f->fs.plr[3]; fs->w_q = f->fs.w_q; fs->max_th = f->max_th; fs->min_th = f->min_th; diff --git a/sys/netpfil/ipfw/ip_dn_io.c b/sys/netpfil/ipfw/ip_dn_io.c --- a/sys/netpfil/ipfw/ip_dn_io.c +++ b/sys/netpfil/ipfw/ip_dn_io.c @@ -497,8 +497,28 @@ ni->tot_pkts++; if (drop) goto drop; - if (f->plr && random() < f->plr) - goto drop; + if (f->plr[0] || f->plr[1]) { + if (__predict_true(f->plr[1] == 0)) { + if (random() < f->plr[0]) + goto drop; + } else { + switch (f->pl_state) { + case PLR_STATE_B: + if (random() < f->plr[3]) + f->pl_state = PLR_STATE_G; + if (random() > f->plr[2]) + goto drop; + break; + case PLR_STATE_G: /* FALLTHROUGH */ + default: + if (random() < f->plr[1]) + f->pl_state = PLR_STATE_B; + if (random() > f->plr[0]) + goto drop; + break; + } + } + } if (m->m_pkthdr.rcvif != NULL) m_rcvif_serialize(m); #ifdef NEW_AQM diff --git a/sys/netpfil/ipfw/ip_dn_private.h b/sys/netpfil/ipfw/ip_dn_private.h --- a/sys/netpfil/ipfw/ip_dn_private.h +++ b/sys/netpfil/ipfw/ip_dn_private.h @@ -392,6 +392,15 @@ PROTO_IFB = 0x0c, /* layer2 + ifbridge */ }; +/* + * States for the Packet Loss Rate Gilbert-Elliott + * channel model + */ +enum { + PLR_STATE_G = 0, + PLR_STATE_B, +}; + //extern struct dn_parms V_dn_cfg; VNET_DECLARE(struct dn_parms, dn_cfg); #define V_dn_cfg VNET(dn_cfg)