Пример #1
0
void test_join_restricted_process()
{
	pfq_t * x = pfq_open_group(Q_CLASS_DEFAULT, Q_POLICY_GROUP_RESTRICTED, 64, 1024, 1024);
	pfq_t * z = pfq_open_group(Q_CLASS_DEFAULT, Q_POLICY_GROUP_SHARED, 64, 1024, 1024);

	assert(x);
	assert(z);

	int p = fork();
	if (p == 0) {
		pfq_t * y = pfq_open_group(Q_CLASS_DEFAULT, Q_POLICY_GROUP_UNDEFINED, 64, 1024, 1024);

		int gid = pfq_group_id(z);
		assert( pfq_join_group(y, gid, Q_CLASS_DEFAULT, Q_POLICY_GROUP_SHARED) == gid);
		assert( pfq_join_group(y, pfq_group_id(x), Q_CLASS_DEFAULT, Q_POLICY_GROUP_SHARED) == -1);

		pfq_close(y);

		_Exit(1);
	}

	wait(NULL);

	pfq_close(x);
	pfq_close(z);
}
Пример #2
0
void test_join_shared_()
{
	{
	pfq_t * q = pfq_open_group(Q_CLASS_DEFAULT, Q_POLICY_GROUP_SHARED, 	64, 1024, 1024);
	pfq_t * y = pfq_open_group(Q_CLASS_DEFAULT, Q_POLICY_GROUP_UNDEFINED,   64, 1024, 1024);

	int gid = pfq_group_id(q);

	assert( pfq_join_group(y, gid, Q_CLASS_DEFAULT, Q_POLICY_GROUP_SHARED) >= 0);

	pfq_close(q);
	pfq_close(y);
	}

	{
	pfq_t * q = pfq_open_group(Q_CLASS_DEFAULT, Q_POLICY_GROUP_SHARED, 64, 1024, 1024);
	pfq_t * y = pfq_open_group(Q_CLASS_DEFAULT, Q_POLICY_GROUP_UNDEFINED, 64, 1024, 1024);

	int gid = pfq_group_id(q);

	assert( pfq_join_group(y, gid, Q_CLASS_DEFAULT, Q_POLICY_GROUP_PRIVATE) < 0);
	assert( pfq_join_group(y, gid, Q_CLASS_DEFAULT, Q_POLICY_GROUP_RESTRICTED) < 0);
	assert( pfq_join_group(y, gid, Q_CLASS_DEFAULT, Q_POLICY_GROUP_UNDEFINED) < 0);

	pfq_close(q);
	pfq_close(y);
	}
}
Пример #3
0
void test_join_group()
{
	pfq_t * q = pfq_open_group(Q_CLASS_DEFAULT, Q_POLICY_GROUP_UNDEFINED, 64, 1024, 1024);
	assert(q);

	int gid = pfq_join_group(q, 0, Q_CLASS_DEFAULT, Q_POLICY_GROUP_SHARED);
	assert(gid == 0);

	gid = pfq_join_group(q, Q_ANY_GROUP, Q_CLASS_DEFAULT, Q_POLICY_GROUP_SHARED);
	assert(gid == 1);

	unsigned long mask;
	assert(pfq_groups_mask(q, &mask) == 0);

	assert(mask == 3);
	pfq_close(q);
}
Пример #4
0
void test_join_deferred()
{
	pfq_t * q = pfq_open_group(Q_CLASS_DEFAULT, Q_POLICY_GROUP_UNDEFINED, 64, 1024, 1024);
        assert(q);

	assert(pfq_join_group(q, 13, Q_CLASS_DEFAULT, Q_POLICY_GROUP_SHARED) == 13);
	assert(pfq_join_group(q, 13, Q_CLASS_DEFAULT, Q_POLICY_GROUP_SHARED) == 13);

	unsigned long mask;

	assert(pfq_groups_mask(q, &mask) == 0);

	assert(mask != 0);
	assert(mask == (1<<13));

	pfq_close(q);
}
Пример #5
0
void *restricted_thread(void *arg)
{
	pfq_t * q = pfq_open_group(Q_CLASS_DEFAULT, Q_POLICY_GROUP_UNDEFINED, 64, 1024, 1024);
        assert(q);

	long int gid = (long int)arg;
	long int ngid = pfq_join_group(q, gid, Q_CLASS_DEFAULT, Q_POLICY_GROUP_RESTRICTED);

	assert(ngid == gid);

	pfq_close(q);
	return 0;
}
Пример #6
0
void test_join_restricted()
{
	pfq_t * q = pfq_open_group(Q_CLASS_DEFAULT, Q_POLICY_GROUP_RESTRICTED, 64, 1024, 1024);
        assert(q);

	pfq_t * y = pfq_open_group(Q_CLASS_DEFAULT, Q_POLICY_GROUP_UNDEFINED, 64, 1024, 1024);

	int gid = pfq_group_id(q);
	assert( pfq_join_group(y, gid, Q_CLASS_DEFAULT, Q_POLICY_GROUP_RESTRICTED) == gid);

	pfq_close(q);
	pfq_close(y);
}
Пример #7
0
void test_leave_group()
{
	pfq_t * q = pfq_open_group(Q_CLASS_DEFAULT, Q_POLICY_GROUP_UNDEFINED, 64, 1024, 1024);
	assert(q);

	int gid = pfq_join_group(q, 22, Q_CLASS_DEFAULT, Q_POLICY_GROUP_SHARED);
	assert(gid == 22);

	assert(pfq_leave_group(q, 22) == 0);
	assert(pfq_group_id(q) == -1);

	unsigned long mask;
	assert(pfq_groups_mask(q, &mask) == 0);
	assert(mask == 0);

	pfq_close(q);
}
Пример #8
0
void test_group_stats()
{
	pfq_t * q = pfq_open(64, 1024, 1024);
        assert(q);

	struct pfq_stats s;
	assert(pfq_get_group_stats(q, 11, &s) == -1);

	assert(pfq_join_group(q, 11, Q_CLASS_DEFAULT, Q_POLICY_GROUP_RESTRICTED) == 11);

	assert(pfq_get_group_stats(q, 11, &s) == 0);

	assert(s.recv == 0);
	assert(s.lost == 0);
	assert(s.drop == 0);

	pfq_close(q);
}
Пример #9
0
int pfq_getsockopt(struct socket *sock,
                int level, int optname,
                char __user * optval, int __user * optlen)
{
        struct pfq_sock *so = pfq_sk(sock->sk);
        struct pfq_rx_opt * ro;
        struct pfq_tx_opt * to;
        int len;

        if (so == NULL)
                return -EFAULT;

        ro = &so->rx_opt;
        to = &so->tx_opt;

        if (get_user(len, optlen))
                return -EFAULT;
        if (len < 0)
                return -EINVAL;

        switch(optname)
        {

        case Q_SO_GROUP_JOIN:
        {
                struct pfq_group_join group;

                if (len != sizeof(group))
                        return -EINVAL;

                if (copy_from_user(&group, optval, sizeof(group)))
                        return -EFAULT;

                if (group.class_mask == 0) {
                        pr_devel("[PFQ|%d] join error: bad class_mask(%lx)!\n", so->id, group.class_mask);
                        return -EINVAL;
                }

                if (group.gid == Q_ANY_GROUP) {

                        group.gid = pfq_join_free_group(so->id, group.class_mask, group.policy);
                        if (group.gid < 0)
                                return -EFAULT;
                        if (copy_to_user(optval, &group, len))
                                return -EFAULT;
                }
                else {
                        CHECK_GROUP(so->id, group.gid, "group join");

                        if (pfq_join_group(group.gid, so->id, group.class_mask, group.policy) < 0) {
                                pr_devel("[PFQ|%d] join error: permission denied (gid:%d)!\n", so->id, group.gid);
                                return -EACCES;
                        }
                }

                pr_devel("[PFQ|%d] join -> gid:%d class_mask:%lx\n", so->id, group.gid, group.class_mask);
        } break;

        case Q_SO_GET_ID:
        {
                if (len != sizeof(so->id))
                        return -EINVAL;
                if (copy_to_user(optval, &so->id, sizeof(so->id)))
                        return -EFAULT;
        } break;

        case Q_SO_GET_STATUS:
        {
                int enabled;
                if (len != sizeof(int))
                        return -EINVAL;

                enabled = so->mem_addr == NULL ? 0 : 1;

                if (copy_to_user(optval, &enabled, sizeof(enabled)))
                        return -EFAULT;

        } break;

        case Q_SO_GET_STATS:
        {
                struct pfq_stats stat;
                if (len != sizeof(struct pfq_stats))
                        return -EINVAL;

                stat.recv = sparse_read(&ro->stat.recv);
                stat.lost = sparse_read(&ro->stat.lost);
                stat.drop = sparse_read(&ro->stat.drop);

                stat.sent = sparse_read(&to->stat.sent);
                stat.disc = sparse_read(&to->stat.disc);

                if (copy_to_user(optval, &stat, sizeof(stat)))
                        return -EFAULT;
        } break;

        case Q_SO_GET_RX_TSTAMP:
        {
                if (len != sizeof(ro->tstamp))
                        return -EINVAL;
                if (copy_to_user(optval, &ro->tstamp, sizeof(ro->tstamp)))
                        return -EFAULT;
        } break;

        case Q_SO_GET_QUEUE_MEM:
        {
                if (len != sizeof(so->mem_size))
                        return -EINVAL;

                if (copy_to_user(optval, &so->mem_size, sizeof(so->mem_size)))
                        return -EFAULT;
        } break;

        case Q_SO_GET_RX_CAPLEN:
        {
                if (len != sizeof(ro->caplen))
                        return -EINVAL;
                if (copy_to_user(optval, &ro->caplen, sizeof(ro->caplen)))
                        return -EFAULT;
        } break;

        case Q_SO_GET_TX_MAXLEN:
        {
                if (len != sizeof(to->maxlen))
                        return -EINVAL;
                if (copy_to_user(optval, &to->maxlen, sizeof(to->maxlen)))
                        return -EFAULT;
        } break;

        case Q_SO_GET_RX_SLOTS:
        {
                if (len != sizeof(ro->size))
                        return -EINVAL;
                if (copy_to_user(optval, &ro->size, sizeof(ro->size)))
                        return -EFAULT;
        } break;

        case Q_SO_GET_TX_SLOTS:
        {
                if (len != sizeof(to->size))
                        return -EINVAL;
                if (copy_to_user(optval, &to->size, sizeof(to->size)))
                        return -EFAULT;
        } break;

        case Q_SO_GET_GROUPS:
        {
                unsigned long grps;
                if(len != sizeof(unsigned long))
                        return -EINVAL;
                grps = pfq_get_groups(so->id);
                if (copy_to_user(optval, &grps, sizeof(grps)))
                        return -EFAULT;
        } break;

        case Q_SO_GET_GROUP_STATS:
        {
                struct pfq_group *g;
                struct pfq_stats stat;
                int gid;

                if (len != sizeof(stat))
                        return -EINVAL;

                if (copy_from_user(&stat, optval, sizeof(stat)))
                        return -EFAULT;

                gid = (int)stat.recv;

                CHECK_GROUP(so->id, gid, "group stat");

                g = pfq_get_group(gid);
                if (!g) {
                        pr_devel("[PFQ|%d] group error: invalid group id %d!\n", so->id, gid);
                        return -EFAULT;
                }

                /* check whether the group is joinable.. */

                if (!__pfq_group_access(gid, so->id, Q_POLICY_GROUP_UNDEFINED, false)) {
                        pr_devel("[PFQ|%d] group stats error: permission denied (gid:%d)!\n", so->id, gid);
                        return -EACCES;
                }

                stat.recv = sparse_read(&g->recv);
                stat.lost = sparse_read(&g->lost);
                stat.drop = sparse_read(&g->drop);

                stat.sent = 0;
                stat.disc = 0;

                if (copy_to_user(optval, &stat, sizeof(stat)))
                        return -EFAULT;
        } break;

        case Q_SO_GET_GROUP_COUNTERS:
        {
                struct pfq_group *g;
                struct pfq_counters cs;
                int i, gid;

                if (len != sizeof(cs))
                        return -EINVAL;

                if (copy_from_user(&cs, optval, sizeof(cs)))
                        return -EFAULT;

                gid = (int)cs.counter[0];

                CHECK_GROUP(so->id, gid, "group stat");

                g = pfq_get_group(gid);
                if (!g) {
                        pr_devel("[PFQ|%d] group error: invalid group id %d!\n", so->id, gid);
                        return -EFAULT;
                }

                /* check whether the group is joinable.. */

                if (!__pfq_group_access(gid, so->id, Q_POLICY_GROUP_UNDEFINED, false)) {
                        pr_devel("[PFQ|%d] group error: permission denied (gid:%d)!\n", so->id, gid);
                        return -EACCES;
                }

                for(i = 0; i < Q_MAX_COUNTERS; i++)
                {
                        cs.counter[i] = sparse_read(&g->ctx.counter[i]);
                }

                if (copy_to_user(optval, &cs, sizeof(cs)))
                        return -EFAULT;
        } break;

        default:
                return -EFAULT;
        }

        return 0;
}
Пример #10
0
pfq_t *
pfq_open_group(unsigned long class_mask, int group_policy, size_t caplen, size_t rx_slots, size_t tx_slots)
{
	int fd = socket(PF_Q, SOCK_RAW, htons(ETH_P_ALL));
	int maxlen;

	pfq_t * q;

	if (fd == -1) {
		return __error = "PFQ: module not loaded", NULL;
	}

	q = (pfq_t *) malloc(sizeof(pfq_t));
	if (q == NULL) {
		return __error = "PFQ: out of memory", NULL;
	}

	memset(q, 0, sizeof(pfq_t));

	q->fd = fd;
	q->hd = -1;
	q->id = -1;
	q->gid = -1;

        memset(&q->nq, 0, sizeof(q->nq));

	/* get id */
	socklen_t size = sizeof(q->id);
	if (getsockopt(fd, PF_Q, Q_SO_GET_ID, &q->id, &size) == -1) {
		return __error = "PFQ: get id error", free(q), NULL;
	}

	/* set rx queue slots */
	if (setsockopt(fd, PF_Q, Q_SO_SET_RX_SLOTS, &rx_slots, sizeof(rx_slots)) == -1) {
		return __error = "PFQ: set Rx slots error", free(q), NULL;
	}

	q->rx_slots = rx_slots;

	/* set caplen */
	if (setsockopt(fd, PF_Q, Q_SO_SET_RX_CAPLEN, &caplen, sizeof(caplen)) == -1) {
		return __error = "PFQ: set Rx caplen error", free(q), NULL;
	}

	q->rx_slot_size = ALIGN(sizeof(struct pfq_pkthdr) + caplen, 8);

	/* set Tx queue slots */
	if (setsockopt(fd, PF_Q, Q_SO_SET_TX_SLOTS, &tx_slots, sizeof(tx_slots)) == -1) {
		return __error = "PFQ: set Tx slots error", free(q), NULL;
	}

        /* get maxlen */

	size = sizeof(maxlen);
        if (getsockopt(fd, PF_Q, Q_SO_GET_TX_MAXLEN, &maxlen, &size) == -1)
        {
		return __error = "PFQ: get Tx maxlen error", free(q), NULL;
        }

	q->tx_slots = tx_slots;
	q->tx_slot_size = ALIGN(sizeof(struct pfq_pkthdr_tx) + maxlen, 8);


	if (group_policy != Q_POLICY_GROUP_UNDEFINED)
	{
		q->gid = pfq_join_group(q, Q_ANY_GROUP, class_mask, group_policy);
		if (q->gid == -1) {
			return __error = q->error, free(q), NULL;
		}
	}

	return __error = NULL, q;
}
Пример #11
0
int pfq_getsockopt(struct socket *sock,
                int level, int optname,
                char __user * optval, int __user * optlen)
{
        struct pfq_sock *so = pfq_sk(sock->sk);
        int len;

        if (so == NULL)
                return -EFAULT;

        if (get_user(len, optlen))
                return -EFAULT;

        if (len < 0)
                return -EINVAL;

        switch(optname)
        {

        case Q_SO_GROUP_JOIN:
        {
                struct pfq_group_join group;

                if (len != sizeof(group))
                        return -EINVAL;

                if (copy_from_user(&group, optval, sizeof(group)))
                        return -EFAULT;

                if (group.class_mask == 0) {
                        printk(KERN_INFO "[PFQ|%d] join error: bad class_mask (%lx)!\n", so->id.value, group.class_mask);
                        return -EINVAL;
                }

                if (group.gid == Q_ANY_GROUP) {

                        group.gid = pfq_join_free_group(so->id, group.class_mask, group.policy);
                        if (group.gid < 0)
                                return -EFAULT;
                        if (copy_to_user(optval, &group, len))
                                return -EFAULT;
                }
                else {
                	pfq_gid_t gid = { group.gid };

                	if (!pfq_get_group(gid)) {
                        	printk(KERN_INFO "[PFQ|%d] group error: invalid group id %d!\n", so->id.value, gid.value);
                        	return -EFAULT;
                	}

                        if (pfq_join_group(gid, so->id, group.class_mask, group.policy) < 0) {
                                printk(KERN_INFO "[PFQ|%d] join error: permission denied (gid=%d)!\n", so->id.value, group.gid);
                                return -EACCES;
                        }
                }

                pr_devel("[PFQ|%d] join: gid=%d class_mask=%lx\n", so->id.value, group.gid, group.class_mask);
        } break;

        case Q_SO_GET_ID:
        {
                if (len != sizeof(so->id.value))
                        return -EINVAL;
                if (copy_to_user(optval, &so->id.value, sizeof(so->id.value)))
                        return -EFAULT;
        } break;

        case Q_SO_GET_STATUS:
        {
                int enabled;
                if (len != sizeof(int))
                        return -EINVAL;

                enabled = so->shmem.addr == NULL ? 0 : 1;

                if (copy_to_user(optval, &enabled, sizeof(enabled)))
                        return -EFAULT;

        } break;

        case Q_SO_GET_STATS:
        {
                struct pfq_stats stat;
                if (len != sizeof(struct pfq_stats))
                        return -EINVAL;

                stat.recv = sparse_read(&so->rx_opt.stats.recv);
                stat.lost = sparse_read(&so->rx_opt.stats.lost);
                stat.drop = sparse_read(&so->rx_opt.stats.drop);

		stat.frwd = 0;
		stat.kern = 0;

                stat.sent = sparse_read(&so->tx_opt.stats.sent);
                stat.disc = sparse_read(&so->tx_opt.stats.disc);

                if (copy_to_user(optval, &stat, sizeof(stat)))
                        return -EFAULT;
        } break;

        case Q_SO_GET_RX_TSTAMP:
        {
                if (len != sizeof(so->rx_opt.tstamp))
                        return -EINVAL;
                if (copy_to_user(optval, &so->rx_opt.tstamp, sizeof(so->rx_opt.tstamp)))
                        return -EFAULT;
        } break;

        case Q_SO_GET_SHMEM_SIZE:
        {
        	size_t size = pfq_shared_memory_size(so);

                if (len != sizeof(size))
                        return -EINVAL;

                if (copy_to_user(optval, &size, sizeof(size)))
                        return -EFAULT;
        } break;

        case Q_SO_GET_RX_CAPLEN:
        {
                if (len != sizeof(so->rx_opt.caplen))
                        return -EINVAL;
                if (copy_to_user(optval, &so->rx_opt.caplen, sizeof(so->rx_opt.caplen)))
                        return -EFAULT;
        } break;

        case Q_SO_GET_TX_MAXLEN:
        {
                if (len != sizeof(max_len))
                        return -EINVAL;
                if (copy_to_user(optval, &max_len, sizeof(max_len)))
                        return -EFAULT;
        } break;

        case Q_SO_GET_RX_SLOTS:
        {
                if (len != sizeof(so->rx_opt.queue_size))
                        return -EINVAL;
                if (copy_to_user(optval, &so->rx_opt.queue_size, sizeof(so->rx_opt.queue_size)))
                        return -EFAULT;
        } break;

        case Q_SO_GET_TX_SLOTS:
        {
                if (len != sizeof(so->tx_opt.queue_size))
                        return -EINVAL;
                if (copy_to_user(optval, &so->tx_opt.queue_size, sizeof(so->tx_opt.queue_size)))
                        return -EFAULT;
        } break;

        case Q_SO_GET_GROUPS:
        {
                unsigned long grps;
                if(len != sizeof(unsigned long))
                        return -EINVAL;
                grps = pfq_get_groups(so->id);
                if (copy_to_user(optval, &grps, sizeof(grps)))
                        return -EFAULT;
        } break;

        case Q_SO_GET_GROUP_STATS:
        {
                struct pfq_group *g;
                struct pfq_stats stat;
                pfq_gid_t gid;

                if (len != sizeof(stat))
                        return -EINVAL;

                if (copy_from_user(&stat, optval, sizeof(stat)))
                        return -EFAULT;

                gid.value = (int)stat.recv;

                g = pfq_get_group(gid);
                if (g == NULL) {
                        printk(KERN_INFO "[PFQ|%d] group error: invalid group id %d!\n", so->id.value, gid.value);
                        return -EFAULT;
                }

		if (pfq_group_is_free(gid)) {
                        printk(KERN_INFO "[PFQ|%d] group stats error: gid=%d is a free group!\n", so->id.value, gid.value);
                        return -EACCES;
		}

                if (!pfq_group_access(gid, so->id)) {
                        printk(KERN_INFO "[PFQ|%d] group stats error: gid=%d permission denied!\n", so->id.value, gid.value);
                        return -EACCES;
                }


                stat.recv = sparse_read(&g->stats.recv);
                stat.drop = sparse_read(&g->stats.drop);
                stat.frwd = sparse_read(&g->stats.frwd);
                stat.kern = sparse_read(&g->stats.kern);

                stat.lost = 0;
                stat.sent = 0;
                stat.disc = 0;

                if (copy_to_user(optval, &stat, sizeof(stat)))
                        return -EFAULT;
        } break;

        case Q_SO_GET_GROUP_COUNTERS:
        {
                struct pfq_group *g;
                struct pfq_counters cs;
                pfq_gid_t gid;
                int i;

                if (len != sizeof(cs))
                        return -EINVAL;

                if (copy_from_user(&cs, optval, sizeof(cs)))
                        return -EFAULT;

                gid.value = (int)cs.counter[0];

                g = pfq_get_group(gid);
                if (g == NULL) {
                        printk(KERN_INFO "[PFQ|%d] group error: invalid group id %d!\n", so->id.value, gid.value);
                        return -EFAULT;
                }

                /* check whether the group is joinable.. */

                if (!pfq_group_policy_access(gid, so->id, Q_POLICY_GROUP_UNDEFINED)) {
                        printk(KERN_INFO "[PFQ|%d] group error: permission denied (gid=%d)!\n", so->id.value, gid.value);
                        return -EACCES;
                }

                for(i = 0; i < Q_MAX_COUNTERS; i++)
                {
                        cs.counter[i] = sparse_read(&g->context.counter[i]);
                }

                if (copy_to_user(optval, &cs, sizeof(cs)))
                        return -EFAULT;
        } break;

        default:
                return -EFAULT;
        }

        return 0;
}
Пример #12
0
int pfq_getsockopt(struct socket *sock,
                int level, int optname,
                char __user * optval, int __user * optlen)
{
        struct pfq_sock *so = pfq_sk(sock->sk);
        int len;

        if (so == NULL)
                return -EFAULT;

        if (get_user(len, optlen))
                return -EFAULT;

        if (len < 0)
                return -EINVAL;

        switch(optname)
        {

        case Q_SO_GROUP_JOIN:
        {
                struct pfq_group_join group;

                if (len != sizeof(group))
                        return -EINVAL;

                if (copy_from_user(&group, optval, sizeof(group)))
                        return -EFAULT;

                if (group.class_mask == 0) {
                        printk(KERN_INFO "[PFQ|%d] join group error: bad class_mask (%lx)!\n",
                               so->id, group.class_mask);
                        return -EINVAL;
                }

                if (group.gid == Q_ANY_GROUP) {

                        group.gid = pfq_join_free_group(so->id, group.class_mask, group.policy);
                        if (group.gid < 0)
                                return -EFAULT;
                        if (copy_to_user(optval, &group, len))
                                return -EFAULT;
                }
                else {
			pfq_gid_t gid = (__force pfq_gid_t)group.gid;

			if (!pfq_get_group(gid)) {
				printk(KERN_INFO "[PFQ|%d] join group error: invalid group id %d!\n",
				       so->id, gid);
				return -EFAULT;
			}

                        if (pfq_join_group(gid, so->id, group.class_mask, group.policy) < 0) {
                                printk(KERN_INFO "[PFQ|%d] join group error: permission denied (gid=%d)!\n",
                                       so->id, group.gid);
                                return -EACCES;
                        }
                }

                pr_devel("[PFQ|%d] join group: gid=%d class_mask=%lx policy=%d\n",
				so->id, group.gid, group.class_mask, group.policy);
        } break;

        case Q_SO_GET_ID:
        {
		int ver;

                if (len != sizeof(so->id))
                        return -EINVAL;

                if (copy_from_user(&ver, optval, sizeof(ver)))
                        return -EFAULT;

		if (ver != PFQ_VERSION_CODE) {
			printk(KERN_INFO "[PFQ] version mismatch: kernel version %d.%d.%d, library version = %d.%d.%d!\n",
			       PFQ_MAJOR(PFQ_VERSION_CODE),
			       PFQ_MINOR(PFQ_VERSION_CODE),
			       PFQ_PATCHLEVEL(PFQ_VERSION_CODE),
			       PFQ_MAJOR(ver),
			       PFQ_MINOR(ver),
			       PFQ_PATCHLEVEL(ver));

			return -EPERM;
		}

                if (copy_to_user(optval, &so->id, sizeof(so->id)))
                        return -EFAULT;
        } break;

        case Q_SO_GET_STATUS:
        {
                int enabled;
                if (len != sizeof(int))
                        return -EINVAL;

                enabled = so->shmem.addr == NULL ? 0 : 1;

                if (copy_to_user(optval, &enabled, sizeof(enabled)))
                        return -EFAULT;

        } break;

        case Q_SO_GET_STATS:
        {
                struct pfq_stats stat;
                if (len != sizeof(struct pfq_stats))
                        return -EINVAL;

                stat.recv = sparse_read(so->stats, recv);
                stat.lost = sparse_read(so->stats, lost);
                stat.drop = sparse_read(so->stats, drop);

		stat.frwd = 0;
		stat.kern = 0;

                stat.sent = sparse_read(so->stats, sent);
                stat.disc = sparse_read(so->stats, disc);

                if (copy_to_user(optval, &stat, sizeof(stat)))
                        return -EFAULT;
        } break;

        case Q_SO_GET_RX_TSTAMP:
        {
                if (len != sizeof(so->opt.tstamp))
                        return -EINVAL;
                if (copy_to_user(optval, &so->opt.tstamp, sizeof(so->opt.tstamp)))
                        return -EFAULT;
        } break;

        case Q_SO_GET_SHMEM_SIZE:
	{
		size_t size = pfq_shared_memory_size(so);

                if (len != sizeof(size))
                        return -EINVAL;

                if (copy_to_user(optval, &size, sizeof(size)))
                        return -EFAULT;
        } break;

        case Q_SO_GET_RX_CAPLEN:
        {
                if (len != sizeof(so->opt.caplen))
                        return -EINVAL;
                if (copy_to_user(optval, &so->opt.caplen, sizeof(so->opt.caplen)))
                        return -EFAULT;
        } break;

        case Q_SO_GET_TX_MAXLEN:
        {
                if (len != sizeof(xmit_slot_size))
                        return -EINVAL;
                if (copy_to_user(optval, &xmit_slot_size, sizeof(xmit_slot_size)))
                        return -EFAULT;
        } break;

        case Q_SO_GET_RX_SLOTS:
        {
                if (len != sizeof(so->opt.rx_queue_len))
                        return -EINVAL;
                if (copy_to_user(optval, &so->opt.rx_queue_len, sizeof(so->opt.rx_queue_len)))
                        return -EFAULT;
        } break;

        case Q_SO_GET_TX_SLOTS:
        {
                if (len != sizeof(so->opt.tx_queue_len))
                        return -EINVAL;
                if (copy_to_user(optval, &so->opt.tx_queue_len, sizeof(so->opt.tx_queue_len)))
                        return -EFAULT;
        } break;

        case Q_SO_GET_GROUPS:
        {
                unsigned long grps;
                if(len != sizeof(unsigned long))
                        return -EINVAL;
                grps = pfq_get_groups(so->id);
                if (copy_to_user(optval, &grps, sizeof(grps)))
                        return -EFAULT;
        } break;

        case Q_SO_GET_GROUP_STATS:
        {
                struct pfq_group *group;
                struct pfq_stats stat;
                pfq_gid_t gid;

                if (len != sizeof(stat))
                        return -EINVAL;

                if (copy_from_user(&stat, optval, sizeof(stat)))
                        return -EFAULT;

                gid = (__force pfq_gid_t)stat.recv;

                group = pfq_get_group(gid);
                if (group == NULL) {
                        printk(KERN_INFO "[PFQ|%d] group error: invalid group id %d!\n", so->id, gid);
                        return -EFAULT;
                }

		if (pfq_group_is_free(gid)) {
                        printk(KERN_INFO "[PFQ|%d] group stats error: gid=%d is a free group!\n",
                               so->id, gid);
                        return -EACCES;
		}

                if (!pfq_group_access(gid, so->id)) {
                        printk(KERN_INFO "[PFQ|%d] group stats error: gid=%d permission denied!\n",
                               so->id, gid);
                        return -EACCES;
                }


                stat.recv = sparse_read(group->stats, recv);
                stat.drop = sparse_read(group->stats, drop);
                stat.frwd = sparse_read(group->stats, frwd);
                stat.kern = sparse_read(group->stats, kern);

                stat.lost = 0;
                stat.sent = 0;
                stat.disc = 0;

                if (copy_to_user(optval, &stat, sizeof(stat)))
                        return -EFAULT;
        } break;

        case Q_SO_GET_GROUP_COUNTERS:
        {
                struct pfq_group *group;
                struct pfq_counters cs;
                pfq_gid_t gid;
                int i;

                if (len != sizeof(cs))
                        return -EINVAL;

                if (copy_from_user(&cs, optval, sizeof(cs)))
                        return -EFAULT;

                gid = (__force pfq_gid_t)cs.counter[0];

                group = pfq_get_group(gid);
                if (group == NULL) {
                        printk(KERN_INFO "[PFQ|%d] group error: invalid group id %d!\n", so->id, gid);
                        return -EFAULT;
                }

                /* check whether the group is joinable.. */

                if (!pfq_group_policy_access(gid, so->id, Q_POLICY_GROUP_UNDEFINED)) {
                        printk(KERN_INFO "[PFQ|%d] group error: permission denied (gid=%d)!\n",
                               so->id, gid);
                        return -EACCES;
                }

                for(i = 0; i < Q_MAX_COUNTERS; i++)
                {
                        cs.counter[i] = sparse_read(group->counters, value[i]);
                }

                if (copy_to_user(optval, &cs, sizeof(cs)))
                        return -EFAULT;
        } break;

        case Q_SO_GET_WEIGHT:
        {
                if (len != sizeof(so->weight))
                        return -EINVAL;

                if (copy_to_user(optval, &so->weight, sizeof(so->weight)))
                        return -EFAULT;
        } break;

        default:
                return -EFAULT;
        }

        return 0;
}