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); }
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); } }
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); }
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); }
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; }
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); }
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); }
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); }
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; }
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; }
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; }
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; }