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