#include "opt_inet6.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/module.h>
#include <sys/priv.h>
#include <sys/proc.h>
#include <sys/rwlock.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/time.h>
#include <sys/taskqueue.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/ip_var.h>
#include <netinet/ip_fw.h>
#include <netinet/ip_dummynet.h>
#include <netpfil/ipfw/ip_fw_private.h>
#include <netpfil/ipfw/dn_heap.h>
#include <netpfil/ipfw/ip_dn_private.h>
#ifdef NEW_AQM
#include <netpfil/ipfw/dn_aqm.h>
#endif
#include <netpfil/ipfw/dn_sched.h>
struct dn_heap_entry7 {
int64_t key;
void *object;
};
struct dn_heap7 {
int size;
int elements;
int offset;
struct dn_heap_entry7 *p;
};
struct dn_flow_set {
SLIST_ENTRY(dn_flow_set) next;
u_short fs_nr ;
u_short flags_fs;
#define DNOLD_HAVE_FLOW_MASK 0x0001
#define DNOLD_IS_RED 0x0002
#define DNOLD_IS_GENTLE_RED 0x0004
#define DNOLD_QSIZE_IS_BYTES 0x0008
#define DNOLD_NOERROR 0x0010
#define DNOLD_HAS_PROFILE 0x0020
#define DNOLD_IS_PIPE 0x4000
#define DNOLD_IS_QUEUE 0x8000
struct dn_pipe7 *pipe ;
u_short parent_nr ;
int weight ;
int qsize ;
int plr[4] ;
struct ipfw_flow_id flow_mask ;
int rq_size ;
int rq_elements ;
struct dn_flow_queue7 **rq ;
u_int32_t last_expired ;
int backlogged ;
#define SCALE_RED 16
#define SCALE(x) ( (x) << SCALE_RED )
#define SCALE_VAL(x) ( (x) >> SCALE_RED )
#define SCALE_MUL(x,y) ( ( (x) * (y) ) >> SCALE_RED )
int w_q ;
int max_th ;
int min_th ;
int max_p ;
u_int c_1 ;
u_int c_2 ;
u_int c_3 ;
u_int c_4 ;
u_int * w_q_lookup ;
u_int lookup_depth ;
int lookup_step ;
int lookup_weight ;
int avg_pkt_size ;
int max_pkt_size ;
};
SLIST_HEAD(dn_flow_set_head, dn_flow_set);
#define DN_IS_PIPE 0x4000
#define DN_IS_QUEUE 0x8000
struct dn_flow_queue7 {
struct dn_flow_queue7 *next ;
struct ipfw_flow_id id ;
struct mbuf *head, *tail ;
u_int len ;
u_int len_bytes ;
u_long numbytes;
u_int64_t tot_pkts ;
u_int64_t tot_bytes ;
u_int32_t drops ;
int hash_slot ;
int avg ;
int count ;
int random ;
u_int32_t q_time;
struct dn_flow_set *fs ;
int heap_pos ;
int64_t sched_time ;
int64_t S,F ;
};
struct dn_pipe7 {
SLIST_ENTRY(dn_pipe7) next;
int pipe_nr ;
uint32_t bandwidth;
int delay ;
struct mbuf *head, *tail ;
struct dn_heap7 scheduler_heap ;
struct dn_heap7 not_eligible_heap;
struct dn_heap7 idle_heap ;
int64_t V ;
int sum;
int numbytes;
int64_t sched_time ;
char if_name[IFNAMSIZ];
struct ifnet *ifp ;
int ready ;
struct dn_flow_set fs ;
};
SLIST_HEAD(dn_pipe_head7, dn_pipe7);
struct dn_flow_queue8 {
struct dn_flow_queue8 *next ;
struct ipfw_flow_id id ;
struct mbuf *head, *tail ;
u_int len ;
u_int len_bytes ;
uint64_t numbytes ;
int64_t extra_bits;
u_int64_t tot_pkts ;
u_int64_t tot_bytes ;
u_int32_t drops ;
int hash_slot ;
int avg ;
int count ;
int random ;
int64_t idle_time;
struct dn_flow_set *fs ;
int heap_pos ;
int64_t sched_time ;
int64_t S,F ;
};
struct dn_pipe8 {
SLIST_ENTRY(dn_pipe8) next;
int pipe_nr ;
uint32_t bandwidth;
int delay ;
struct mbuf *head, *tail ;
struct dn_heap7 scheduler_heap ;
struct dn_heap7 not_eligible_heap;
struct dn_heap7 idle_heap ;
int64_t V ;
int sum;
int64_t numbytes;
uint64_t burst;
int64_t sched_time ;
int64_t idle_time;
char if_name[IFNAMSIZ];
struct ifnet *ifp ;
int ready ;
struct dn_flow_set fs ;
#define ED_MAX_NAME_LEN 32
char name[ED_MAX_NAME_LEN];
int loss_level;
int samples_no;
int *samples;
};
#define ED_MAX_SAMPLES_NO 1024
struct dn_pipe_max8 {
struct dn_pipe8 pipe;
int samples[ED_MAX_SAMPLES_NO];
};
SLIST_HEAD(dn_pipe_head8, dn_pipe8);
#define O_NEXT(p, len) ((void *)((char *)p + len))
static void
oid_fill(struct dn_id *oid, int len, int type, uintptr_t id)
{
oid->len = len;
oid->type = type;
oid->subtype = 0;
oid->id = id;
}
static void *
o_next(struct dn_id **o, int len, int type)
{
struct dn_id *ret = *o;
oid_fill(ret, len, type, 0);
*o = O_NEXT(*o, len);
return ret;
}
static size_t pipesize7 = sizeof(struct dn_pipe7);
static size_t pipesize8 = sizeof(struct dn_pipe8);
static size_t pipesizemax8 = sizeof(struct dn_pipe_max8);
static int is7 = 0;
static int
convertflags2new(int src)
{
int dst = 0;
if (src & DNOLD_HAVE_FLOW_MASK)
dst |= DN_HAVE_MASK;
if (src & DNOLD_QSIZE_IS_BYTES)
dst |= DN_QSIZE_BYTES;
if (src & DNOLD_NOERROR)
dst |= DN_NOERROR;
if (src & DNOLD_IS_RED)
dst |= DN_IS_RED;
if (src & DNOLD_IS_GENTLE_RED)
dst |= DN_IS_GENTLE_RED;
if (src & DNOLD_HAS_PROFILE)
dst |= DN_HAS_PROFILE;
return dst;
}
static int
convertflags2old(int src)
{
int dst = 0;
if (src & DN_HAVE_MASK)
dst |= DNOLD_HAVE_FLOW_MASK;
if (src & DN_IS_RED)
dst |= DNOLD_IS_RED;
if (src & DN_IS_GENTLE_RED)
dst |= DNOLD_IS_GENTLE_RED;
if (src & DN_NOERROR)
dst |= DNOLD_NOERROR;
if (src & DN_HAS_PROFILE)
dst |= DNOLD_HAS_PROFILE;
if (src & DN_QSIZE_BYTES)
dst |= DNOLD_QSIZE_IS_BYTES;
return dst;
}
static int
dn_compat_del(void *v)
{
struct dn_pipe7 *p = (struct dn_pipe7 *) v;
struct dn_pipe8 *p8 = (struct dn_pipe8 *) v;
struct {
struct dn_id oid;
uintptr_t a[1];
} cmd;
oid_fill((void *)&cmd, sizeof(cmd), DN_CMD_DELETE, DN_API_VERSION);
if (is7) {
if (p->pipe_nr == 0 && p->fs.fs_nr == 0)
return EINVAL;
if (p->pipe_nr != 0 && p->fs.fs_nr != 0)
return EINVAL;
} else {
if (p8->pipe_nr == 0 && p8->fs.fs_nr == 0)
return EINVAL;
if (p8->pipe_nr != 0 && p8->fs.fs_nr != 0)
return EINVAL;
}
if (p->pipe_nr != 0) {
cmd.a[0] = p->pipe_nr;
cmd.oid.subtype = DN_LINK;
} else {
cmd.oid.subtype = DN_FS;
cmd.a[0] = (is7) ? p->fs.fs_nr : p8->fs.fs_nr;
}
return do_config(&cmd, cmd.oid.len);
}
static int
dn_compat_config_queue(struct dn_fs *fs, void* v)
{
struct dn_pipe7 *p7 = (struct dn_pipe7 *)v;
struct dn_pipe8 *p8 = (struct dn_pipe8 *)v;
struct dn_flow_set *f;
if (is7)
f = &p7->fs;
else
f = &p8->fs;
fs->fs_nr = f->fs_nr;
fs->sched_nr = f->parent_nr;
fs->flow_mask = f->flow_mask;
fs->buckets = f->rq_size;
fs->qsize = f->qsize;
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) {
fs->w_q = f->w_q;
fs->max_th = f->max_th;
fs->min_th = f->min_th;
fs->max_p = f->max_p;
}
return 0;
}
static int
dn_compat_config_pipe(struct dn_sch *sch, struct dn_link *p,
struct dn_fs *fs, void* v)
{
struct dn_pipe7 *p7 = (struct dn_pipe7 *)v;
struct dn_pipe8 *p8 = (struct dn_pipe8 *)v;
int i = p7->pipe_nr;
sch->sched_nr = i;
sch->oid.subtype = 0;
p->link_nr = i;
fs->fs_nr = i + 2*DN_MAX_ID;
fs->sched_nr = i + DN_MAX_ID;
p->bandwidth = p7->bandwidth;
p->delay = p7->delay;
if (!is7) {
p->burst = p8->burst;
}
dn_compat_config_queue(fs, v);
fs->fs_nr = i + 2*DN_MAX_ID;
fs->sched_nr = i + DN_MAX_ID;
sch->buckets = fs->buckets;
fs->buckets = 0;
if (fs->flags & DN_HAVE_MASK) {
sch->flags |= DN_HAVE_MASK;
fs->flags &= ~DN_HAVE_MASK;
sch->sched_mask = fs->flow_mask;
bzero(&fs->flow_mask, sizeof(struct ipfw_flow_id));
}
return 0;
}
static int
dn_compat_config_profile(struct dn_profile *pf, struct dn_link *p,
void *v)
{
struct dn_pipe8 *p8 = (struct dn_pipe8 *)v;
p8->samples = &(((struct dn_pipe_max8 *)p8)->samples[0]);
pf->link_nr = p->link_nr;
pf->loss_level = p8->loss_level;
pf->samples_no = p8->samples_no;
strncpy(pf->name, p8->name,sizeof(pf->name));
bcopy(p8->samples, pf->samples, sizeof(pf->samples));
return 0;
}
static int
dn_compat_configure(void *v)
{
struct dn_id *buf = NULL, *base;
struct dn_sch *sch = NULL;
struct dn_link *p = NULL;
struct dn_fs *fs = NULL;
struct dn_profile *pf = NULL;
int lmax;
int error;
struct dn_pipe7 *p7 = (struct dn_pipe7 *)v;
struct dn_pipe8 *p8 = (struct dn_pipe8 *)v;
int i;
lmax = sizeof(struct dn_id);
lmax += sizeof(struct dn_sch) + sizeof(struct dn_link) +
sizeof(struct dn_fs) + sizeof(struct dn_profile);
base = buf = malloc(lmax, M_DUMMYNET, M_WAITOK|M_ZERO);
o_next(&buf, sizeof(struct dn_id), DN_CMD_CONFIG);
base->id = DN_API_VERSION;
i = p7->pipe_nr;
if (i != 0) {
sch = o_next(&buf, sizeof(*sch), DN_SCH);
p = o_next(&buf, sizeof(*p), DN_LINK);
fs = o_next(&buf, sizeof(*fs), DN_FS);
error = dn_compat_config_pipe(sch, p, fs, v);
if (error) {
free(buf, M_DUMMYNET);
return error;
}
if (!is7 && p8->samples_no > 0) {
pf = o_next(&buf, sizeof(*pf), DN_PROFILE);
error = dn_compat_config_profile(pf, p, v);
if (error) {
free(buf, M_DUMMYNET);
return error;
}
}
} else {
fs = o_next(&buf, sizeof(*fs), DN_FS);
error = dn_compat_config_queue(fs, v);
if (error) {
free(buf, M_DUMMYNET);
return error;
}
}
error = do_config(base, (char *)buf - (char *)base);
if (buf)
free(buf, M_DUMMYNET);
return error;
}
int
dn_compat_calc_size(void)
{
int need = 0;
need += V_dn_cfg.schk_count * sizeof(struct dn_pipe8) / 2;
need += V_dn_cfg.fsk_count * sizeof(struct dn_flow_set);
need += V_dn_cfg.si_count * sizeof(struct dn_flow_queue8);
need += V_dn_cfg.queue_count * sizeof(struct dn_flow_queue8);
return need;
}
int
dn_c_copy_q (void *_ni, void *arg)
{
struct copy_args *a = arg;
struct dn_flow_queue7 *fq7 = (struct dn_flow_queue7 *)*a->start;
struct dn_flow_queue8 *fq8 = (struct dn_flow_queue8 *)*a->start;
struct dn_flow *ni = (struct dn_flow *)_ni;
int size = 0;
fq7->len = ni->length;
fq7->len_bytes = ni->len_bytes;
fq7->id = ni->fid;
if (is7) {
size = sizeof(struct dn_flow_queue7);
fq7->tot_pkts = ni->tot_pkts;
fq7->tot_bytes = ni->tot_bytes;
fq7->drops = ni->drops;
} else {
size = sizeof(struct dn_flow_queue8);
fq8->tot_pkts = ni->tot_pkts;
fq8->tot_bytes = ni->tot_bytes;
fq8->drops = ni->drops;
}
*a->start += size;
return 0;
}
int
dn_c_copy_pipe(struct dn_schk *s, struct copy_args *a, int nq)
{
struct dn_link *l = &s->link;
struct dn_fsk *f = s->fs;
struct dn_pipe7 *pipe7 = (struct dn_pipe7 *)*a->start;
struct dn_pipe8 *pipe8 = (struct dn_pipe8 *)*a->start;
struct dn_flow_set *fs;
int size = 0;
if (is7) {
fs = &pipe7->fs;
size = sizeof(struct dn_pipe7);
} else {
fs = &pipe8->fs;
size = sizeof(struct dn_pipe8);
}
pipe7->next.sle_next = (struct dn_pipe7 *)DN_IS_PIPE;
pipe7->bandwidth = l->bandwidth;
pipe7->delay = l->delay * 1000 / hz;
pipe7->pipe_nr = l->link_nr - DN_MAX_ID;
if (!is7) {
if (s->profile) {
struct dn_profile *pf = s->profile;
strncpy(pipe8->name, pf->name, sizeof(pf->name));
pipe8->loss_level = pf->loss_level;
pipe8->samples_no = pf->samples_no;
}
pipe8->burst = div64(l->burst , 8 * hz);
}
fs->flow_mask = s->sch.sched_mask;
fs->rq_size = s->sch.buckets ? s->sch.buckets : 1;
fs->parent_nr = l->link_nr - DN_MAX_ID;
fs->qsize = f->fs.qsize;
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;
fs->max_p = f->fs.max_p;
fs->rq_elements = nq;
fs->flags_fs = convertflags2old(f->fs.flags);
*a->start += size;
return 0;
}
int
dn_compat_copy_pipe(struct copy_args *a, void *_o)
{
int have = a->end - *a->start;
int need = 0;
int pipe_size = sizeof(struct dn_pipe8);
int queue_size = sizeof(struct dn_flow_queue8);
int n_queue = 0;
struct dn_schk *s = (struct dn_schk *)_o;
n_queue = (s->sch.flags & DN_HAVE_MASK ? dn_ht_entries(s->siht) :
(s->siht ? 1 : 0));
need = pipe_size + queue_size * n_queue;
if (have < need) {
D("have %d < need %d", have, need);
return 1;
}
dn_c_copy_pipe(s, a, n_queue);
if (s->sch.flags & DN_HAVE_MASK)
dn_ht_scan(s->siht, dn_c_copy_q, a);
else if (s->siht)
dn_c_copy_q(s->siht, a);
return 0;
}
int
dn_c_copy_fs(struct dn_fsk *f, struct copy_args *a, int nq)
{
struct dn_flow_set *fs = (struct dn_flow_set *)*a->start;
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[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;
fs->max_p = f->fs.max_p;
fs->flow_mask = f->fs.flow_mask;
fs->rq_elements = nq;
fs->rq_size = (f->fs.buckets ? f->fs.buckets : 1);
fs->parent_nr = f->fs.sched_nr;
fs->weight = f->fs.par[0];
fs->flags_fs = convertflags2old(f->fs.flags);
*a->start += sizeof(struct dn_flow_set);
return 0;
}
int
dn_compat_copy_queue(struct copy_args *a, void *_o)
{
int have = a->end - *a->start;
int need = 0;
int fs_size = sizeof(struct dn_flow_set);
int queue_size = sizeof(struct dn_flow_queue8);
struct dn_fsk *fs = (struct dn_fsk *)_o;
int n_queue = 0;
n_queue = (fs->fs.flags & DN_HAVE_MASK ? dn_ht_entries(fs->qht) :
(fs->qht ? 1 : 0));
need = fs_size + queue_size * n_queue;
if (have < need) {
D("have < need");
return 1;
}
dn_c_copy_fs(fs, a, n_queue);
if (fs->fs.flags & DN_HAVE_MASK)
dn_ht_scan(fs->qht, dn_c_copy_q, a);
else if (fs->qht)
dn_c_copy_q(fs->qht, a);
return 0;
}
int
copy_data_helper_compat(void *_o, void *_arg)
{
struct copy_args *a = _arg;
if (a->type == DN_COMPAT_PIPE) {
struct dn_schk *s = _o;
if (s->sch.oid.subtype != 1 || s->sch.sched_nr <= DN_MAX_ID) {
return 0;
}
if(dn_compat_copy_pipe(a, _o))
return DNHT_SCAN_END;
} else if (a->type == DN_COMPAT_QUEUE) {
struct dn_fsk *fs = _o;
if (fs->fs.fs_nr >= DN_MAX_ID)
return 0;
if (dn_compat_copy_queue(a, _o))
return DNHT_SCAN_END;
}
return 0;
}
int
ip_dummynet_compat(struct sockopt *sopt)
{
int error=0;
void *v = NULL;
struct dn_id oid;
int len = sopt->sopt_valsize;
if (len == pipesize7) {
D("setting compatibility with FreeBSD 7.2");
is7 = 1;
}
else if (len == pipesize8 || len == pipesizemax8) {
D("setting compatibility with FreeBSD 8");
is7 = 0;
}
switch (sopt->sopt_name) {
default:
printf("dummynet: -- unknown option %d", sopt->sopt_name);
error = EINVAL;
break;
case IP_DUMMYNET_FLUSH:
oid_fill(&oid, sizeof(oid), DN_CMD_FLUSH, DN_API_VERSION);
do_config(&oid, oid.len);
break;
case IP_DUMMYNET_DEL:
v = malloc(len, M_TEMP, M_WAITOK);
error = sooptcopyin(sopt, v, len, len);
if (error)
break;
error = dn_compat_del(v);
free(v, M_TEMP);
break;
case IP_DUMMYNET_CONFIGURE:
v = malloc(len, M_TEMP, M_NOWAIT);
if (v == NULL) {
error = ENOMEM;
break;
}
error = sooptcopyin(sopt, v, len, len);
if (error)
break;
error = dn_compat_configure(v);
free(v, M_TEMP);
break;
case IP_DUMMYNET_GET: {
void *buf;
int ret;
int original_size = sopt->sopt_valsize;
int size;
ret = dummynet_get(sopt, &buf);
if (ret)
return 0;
size = sopt->sopt_valsize;
sopt->sopt_valsize = original_size;
D("size=%d, buf=%p", size, buf);
ret = sooptcopyout(sopt, buf, size);
if (ret)
printf(" %s ERROR sooptcopyout\n", __FUNCTION__);
if (buf)
free(buf, M_DUMMYNET);
}
}
return error;
}