integrated
[ipfw.git] / ipfw / dummynet.c
index 201549a..231f52f 100644 (file)
@@ -10,7 +10,7 @@
  *
  * This software is provided ``AS IS'' without any warranties of any kind.
  *
- * $FreeBSD: user/luigi/ipfw3-head/sbin/ipfw/dummynet.c 203321 2010-01-31 21:39:25Z luigi $
+ * $FreeBSD: head/sbin/ipfw/dummynet.c 206843 2010-04-19 15:11:45Z luigi $
  *
  * dummynet support
  */
@@ -101,6 +101,18 @@ o_next(struct dn_id **o, int len, int type)
        return ret;
 }
 
+/* handle variable lenght structures moving back the pointer and fixing lenght */
+static void *
+o_compact(struct dn_id **o, int len, int real_length, int type)
+{
+        struct dn_id *ret = *o;
+
+        ret = O_NEXT(*o, -len);
+        oid_fill(ret, real_length, type, 0);
+        *o = O_NEXT(ret, real_length);
+        return ret;
+}
+
 #if 0
 static int
 sort_q(void *arg, const void *pa, const void *pb)
@@ -146,10 +158,6 @@ print_mask(struct ipfw_flow_id *id)
                    id->proto,
                    id->src_ip, id->src_port,
                    id->dst_ip, id->dst_port);
-
-               printf("BKT Prot ___Source IP/port____ "
-                   "____Dest. IP/port____ "
-                   "Tot_pkt/bytes Pkt/Byte Drp\n");
        } else {
                char buf[255];
                printf("\n        mask: %sproto: 0x%02x, flow_id: 0x%08x,  ",
@@ -159,22 +167,35 @@ print_mask(struct ipfw_flow_id *id)
                printf("%s/0x%04x -> ", buf, id->src_port);
                inet_ntop(AF_INET6, &(id->dst_ip6), buf, sizeof(buf));
                printf("%s/0x%04x\n", buf, id->dst_port);
+       }
+}
 
+static void
+print_header(struct ipfw_flow_id *id)
+{
+       if (!IS_IP6_FLOW_ID(id))
+               printf("BKT Prot ___Source IP/port____ "
+                   "____Dest. IP/port____ "
+                   "Tot_pkt/bytes Pkt/Byte Drp\n");
+       else
                printf("BKT ___Prot___ _flow-id_ "
                    "______________Source IPv6/port_______________ "
                    "_______________Dest. IPv6/port_______________ "
                    "Tot_pkt/bytes Pkt/Byte Drp\n");
-       }
 }
 
 static void
-list_flow(struct dn_flow *ni)
+list_flow(struct dn_flow *ni, int *print)
 {
        char buff[255];
        struct protoent *pe = NULL;
        struct in_addr ina;
        struct ipfw_flow_id *id = &ni->fid;
 
+       if (*print) {
+               print_header(&ni->fid);
+               *print = 0;
+       }
        pe = getprotobynumber(id->proto);
                /* XXX: Should check for IPv4 flows */
        printf("%3u%c", (ni->oid.id) & 0xff,
@@ -203,31 +224,10 @@ list_flow(struct dn_flow *ni)
                    inet_ntop(AF_INET6, &(id->dst_ip6), buff, sizeof(buff)),
                    id->dst_port);
        }
-
-       /* Tcc relies on msvcrt.dll for printf, and
-        * it does not support ANSI %llu syntax
-        */
-#ifndef TCC
-       printf("%4llu %8llu %2u %4u %3u\n",
-           align_uint64(&ni->tot_pkts),
-           align_uint64(&ni->tot_bytes),
+       pr_u64(&ni->tot_pkts, 4);
+       pr_u64(&ni->tot_bytes, 8);
+       printf("%2u %4u %3u\n",
            ni->length, ni->len_bytes, ni->drops);
-#else
-       /* XXX This should be printed correctly, but for some
-        * weird reason, it is not. Making a printf for each
-        * value is a workaround, until we don't undestand what's wrong
-        */
-       /*printf("%4I64u %8I64u %2u %4u %3u\n",
-           align_uint64(&ni->tot_pkts),
-           align_uint64(&ni->tot_bytes),
-           ni->length, ni->len_bytes, ni->drops);*/
-       
-       printf("%4I64u ",align_uint64(&ni->tot_pkts));
-       printf("%8I64u ",align_uint64(&ni->tot_bytes));
-       printf("%2u ",ni->length);
-       printf("%4u ",ni->len_bytes);
-       printf("%3u\n",ni->drops);
-#endif
 }
 
 static void
@@ -311,8 +311,9 @@ static void
 list_pipes(struct dn_id *oid, struct dn_id *end)
 {
     char buf[160];     /* pending buffer */
+    int toPrint = 1;   /* print header */
+
     buf[0] = '\0';
-       
     for (; oid != end; oid = O_NEXT(oid, oid->len)) {
        if (oid->len < sizeof(*oid))
                errx(1, "invalid oid len %d\n", oid->len);
@@ -349,12 +350,12 @@ list_pipes(struct dn_id *oid, struct dn_id *end)
                        s->sched_nr,
                        s->name, s->flags, s->buckets, s->oid.id);
            if (s->flags & DN_HAVE_MASK)
-                       print_mask(&s->sched_mask);
+               print_mask(&s->sched_mask);
            }
            break;
 
        case DN_FLOW:
-           list_flow((struct dn_flow *)oid);
+           list_flow((struct dn_flow *)oid, &toPrint);
            break;
 
        case DN_LINK: {
@@ -391,7 +392,7 @@ list_pipes(struct dn_id *oid, struct dn_id *end)
            print_extra_delay_parms((struct dn_profile *)oid);
        }
        flush_buf(buf); // XXX does it really go here ?
-       }
+    }
 }
 
 /*
@@ -599,6 +600,70 @@ compare_points(const void *vp1, const void *vp2)
 
 #define ED_EFMT(s) EX_DATAERR,"error in %s at line %d: "#s,filename,lineno
 
+/*
+ * Interpolate a set of proability-value tuples.
+ *
+ * This function takes as input a tuple of values <prob, value>
+ * and samples the interpolated curve described from the tuples.
+ *
+ * The user defined points are stored in the ponts structure.
+ * The number of points is stored in points_no.
+ * The user defined sampling value is stored in samples_no.
+ * The resulting samples are in the "samples" pointer.
+ *
+ *       We assume that The last point for the '1' value of the
+ *       probability should be defined. (XXX add checks for this)
+ *
+ * The input data are points and points_no.
+ * The output data are s (the array of s_no samples)
+ * and s_no (the number of samples)
+ *
+ */
+static void
+interpolate_samples(struct point *p, int points_no, 
+               int *samples, int samples_no, const char *filename)
+{
+       double dy;              /* delta on the y axis */
+       double y;               /* current value of y */
+       double x;               /* current value of x */
+       double m;               /* the y slope */
+       int i;                  /* samples index */
+       int curr;               /* points current index */
+
+        /* make sure that there are enough points. */
+        /* XXX Duplicated should be removed */
+        if (points_no < 3)
+            errx(EX_DATAERR, "%s too few samples, need at least %d",
+                filename, 3);
+
+        qsort(p, points_no, sizeof(struct point), compare_points);
+
+       dy = 1.0/samples_no;
+       y = 0;
+
+       for (i=0, curr = 0; i < samples_no; i++, y+=dy) {
+               /* This statment move the curr pointer to the next point
+                * skipping the points with the same x value. We are
+                * guaranteed to exit from the loop because the
+                * last possible value of y is stricly less than 1
+                * and the last possible value of the y points is 1 */
+               while ( y >= p[curr+1].prob ) curr++;
+
+               /* compute the slope of the curve */
+               m = (p[curr+1].delay - p[curr].delay) / (p[curr+1].prob - p[curr].prob);
+               /* compute the x value starting from the current point */
+               x = p[curr].delay + (y - p[curr].prob) * m;
+               samples[i] = x;
+       }
+
+       /* add the last sample */
+       samples[i] = p[curr+1].delay;
+}
+
+/*
+ * p is the link (old pipe)
+ * pf is the profile
+ */
 static void
 load_extra_delays(const char *filename, struct dn_profile *p,
        struct dn_link *link)
@@ -606,7 +671,6 @@ load_extra_delays(const char *filename, struct dn_profile *p,
        char    line[ED_MAX_LINE_LEN];
        FILE    *f;
        int     lineno = 0;
-       int     i;
 
        int     samples = -1;
        double  loss = -1.0;
@@ -620,6 +684,7 @@ load_extra_delays(const char *filename, struct dn_profile *p,
        p->link_nr = link->link_nr;
 
        profile_name[0] = '\0';
+
        f = fopen(filename, "r");
        if (f == NULL)
                err(EX_UNAVAILABLE, "fopen: %s", filename);
@@ -643,10 +708,9 @@ load_extra_delays(const char *filename, struct dn_profile *p,
                        else
                                arg = s;
                }
-               if (name == NULL)       /* empty line */
+
+               if ((name == NULL) || (*name == '#'))   /* empty line */
                        continue;
-               if (arg == NULL)
-                       errx(ED_EFMT("missing arg for %s"), name);
 
                if (!strcasecmp(name, ED_TOK_SAMPLES)) {
                    if (samples > 0)
@@ -654,13 +718,14 @@ load_extra_delays(const char *filename, struct dn_profile *p,
                    if (atoi(arg) <=0)
                        errx(ED_EFMT("invalid number of samples"));
                    samples = atoi(arg);
-                   if (samples>ED_MAX_SAMPLES_NO)
+                   if (samples>=ED_MAX_SAMPLES_NO-1)
                            errx(ED_EFMT("too many samples, maximum is %d"),
-                               ED_MAX_SAMPLES_NO);
+                               ED_MAX_SAMPLES_NO-1);
                    do_points = 0;
                } else if (!strcasecmp(name, ED_TOK_BW)) {
                    char buf[IFNAMSIZ];
                    read_bandwidth(arg, &link->bandwidth, buf, sizeof(buf));
+                   p->bandwidth = link->bandwidth;
                } else if (!strcasecmp(name, ED_TOK_LOSS)) {
                    if (loss != -1.0)
                        errx(ED_EFMT("duplicated token: %s"), name);
@@ -716,34 +781,9 @@ load_extra_delays(const char *filename, struct dn_profile *p,
            loss = 1;
        }
 
-       /* make sure that there are enough points. */
-       if (points_no < ED_MIN_SAMPLES_NO)
-           errx(ED_EFMT("too few samples, need at least %d"),
-               ED_MIN_SAMPLES_NO);
-
-       qsort(points, points_no, sizeof(struct point), compare_points);
-
-       /* interpolation */
-       for (i = 0; i<points_no-1; ++i) {
-           double y1 = points[i].prob * samples;
-           double x1 = points[i].delay;
-           double y2 = points[i+1].prob * samples;
-           double x2 = points[i+1].delay;
+       interpolate_samples(points, points_no, p->samples, samples, filename);
 
-           int ix = y1;
-           int stop = y2;
-
-           if (x1 == x2) {
-               for (; ix<stop; ++ix)
-                   p->samples[ix] = x1;
-           } else {
-               double m = (y2-y1)/(x2-x1);
-               double c = y1 - m*x1;
-               for (; ix<stop ; ++ix)
-                   p->samples[ix] = (ix - c)/m;
-           }
-       }
-       p->samples_no = samples;
+       p->samples_no = samples++;
        p->loss_level = loss * samples;
        strncpy(p->name, profile_name, sizeof(p->name));
 }
@@ -781,6 +821,7 @@ ipfw_config_pipe(int ac, char **av)
        struct ipfw_flow_id *mask = NULL;
        int lmax;
        uint32_t _foo = 0, *flags = &_foo , *buckets = &_foo;
+       size_t max_pf_size = sizeof(struct dn_profile) + ED_MAX_SAMPLES_NO * sizeof(int);
 
        /*
         * allocate space for 1 header,
@@ -788,7 +829,8 @@ ipfw_config_pipe(int ac, char **av)
         */
        lmax = sizeof(struct dn_id);    /* command header */
        lmax += sizeof(struct dn_sch) + sizeof(struct dn_link) +
-               sizeof(struct dn_fs) + sizeof(struct dn_profile);
+               sizeof(struct dn_fs);
+       lmax += max_pf_size;
 
        av++; ac--;
        /* Pipe number */
@@ -1127,12 +1169,21 @@ end_mask:
                        break;
 
                case TOK_PROFILE:
+                   {
+                       size_t real_length;
+
                        NEED((!pf), "profile already set");
                        NEED(p, "profile");
-                   {
                        NEED1("extra delay needs the file name\n");
-                       pf = o_next(&buf, sizeof(*pf), DN_PROFILE);
+
+                       /* load the profile structure using the DN_API */
+                       pf = o_next(&buf, max_pf_size, DN_PROFILE);
                        load_extra_delays(av[0], pf, p); //XXX can't fail?
+
+                       /* compact the dn_id structure */
+                       real_length = sizeof(struct dn_profile) +
+                               pf->samples_no * sizeof(int);
+                       o_compact(&buf, max_pf_size, real_length, DN_PROFILE);
                        --ac; ++av;
                    }
                        break;
@@ -1165,8 +1216,8 @@ end_mask:
        }
        if (fs) {
                /* XXX accept a 0 scheduler to keep the default */
-    if (fs->flags & DN_QSIZE_BYTES) {
-       size_t len;
+           if (fs->flags & DN_QSIZE_BYTES) {
+               size_t len;
                long limit;
 
                len = sizeof(limit);
@@ -1381,7 +1432,7 @@ dummynet_list(int ac, char *av[], int show_counters)
        } else {
                ret = do_cmd(-IP_DUMMYNET3, oid, (uintptr_t)&l);
                if (ret != 0 || oid->id <= sizeof(*oid))
-                       goto done; 
+                       goto done;
                buflen = oid->id + max_size;
                oid->len = sizeof(*oid); /* restore */
        }