📄 af_packet.c
字号:
{ struct sock *sk = sock->sk; struct packet_sock *po = pkt_sk(sk); int ret; if (level != SOL_PACKET) return -ENOPROTOOPT; switch(optname) { case PACKET_ADD_MEMBERSHIP: case PACKET_DROP_MEMBERSHIP: { struct packet_mreq_max mreq; int len = optlen; memset(&mreq, 0, sizeof(mreq)); if (len < sizeof(struct packet_mreq)) return -EINVAL; if (len > sizeof(mreq)) len = sizeof(mreq); if (copy_from_user(&mreq,optval,len)) return -EFAULT; if (len < (mreq.mr_alen + offsetof(struct packet_mreq, mr_address))) return -EINVAL; if (optname == PACKET_ADD_MEMBERSHIP) ret = packet_mc_add(sk, &mreq); else ret = packet_mc_drop(sk, &mreq); return ret; }#ifdef CONFIG_PACKET_MMAP case PACKET_RX_RING: { struct tpacket_req req; if (optlen<sizeof(req)) return -EINVAL; if (copy_from_user(&req,optval,sizeof(req))) return -EFAULT; return packet_set_ring(sk, &req, 0); } case PACKET_COPY_THRESH: { int val; if (optlen!=sizeof(val)) return -EINVAL; if (copy_from_user(&val,optval,sizeof(val))) return -EFAULT; pkt_sk(sk)->copy_thresh = val; return 0; }#endif case PACKET_AUXDATA: { int val; if (optlen < sizeof(val)) return -EINVAL; if (copy_from_user(&val, optval, sizeof(val))) return -EFAULT; po->auxdata = !!val; return 0; } case PACKET_ORIGDEV: { int val; if (optlen < sizeof(val)) return -EINVAL; if (copy_from_user(&val, optval, sizeof(val))) return -EFAULT; po->origdev = !!val; return 0; } default: return -ENOPROTOOPT; }}static int packet_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen){ int len; int val; struct sock *sk = sock->sk; struct packet_sock *po = pkt_sk(sk); void *data; struct tpacket_stats st; if (level != SOL_PACKET) return -ENOPROTOOPT; if (get_user(len, optlen)) return -EFAULT; if (len < 0) return -EINVAL; switch(optname) { case PACKET_STATISTICS: if (len > sizeof(struct tpacket_stats)) len = sizeof(struct tpacket_stats); spin_lock_bh(&sk->sk_receive_queue.lock); st = po->stats; memset(&po->stats, 0, sizeof(st)); spin_unlock_bh(&sk->sk_receive_queue.lock); st.tp_packets += st.tp_drops; data = &st; break; case PACKET_AUXDATA: if (len > sizeof(int)) len = sizeof(int); val = po->auxdata; data = &val; break; case PACKET_ORIGDEV: if (len > sizeof(int)) len = sizeof(int); val = po->origdev; data = &val; break; default: return -ENOPROTOOPT; } if (put_user(len, optlen)) return -EFAULT; if (copy_to_user(optval, data, len)) return -EFAULT; return 0;}static int packet_notifier(struct notifier_block *this, unsigned long msg, void *data){ struct sock *sk; struct hlist_node *node; struct net_device *dev = data; if (dev->nd_net != &init_net) return NOTIFY_DONE; read_lock(&packet_sklist_lock); sk_for_each(sk, node, &packet_sklist) { struct packet_sock *po = pkt_sk(sk); switch (msg) { case NETDEV_UNREGISTER: if (po->mclist) packet_dev_mclist(dev, po->mclist, -1); /* fallthrough */ case NETDEV_DOWN: if (dev->ifindex == po->ifindex) { spin_lock(&po->bind_lock); if (po->running) { __dev_remove_pack(&po->prot_hook); __sock_put(sk); po->running = 0; sk->sk_err = ENETDOWN; if (!sock_flag(sk, SOCK_DEAD)) sk->sk_error_report(sk); } if (msg == NETDEV_UNREGISTER) { po->ifindex = -1; po->prot_hook.dev = NULL; } spin_unlock(&po->bind_lock); } break; case NETDEV_UP: spin_lock(&po->bind_lock); if (dev->ifindex == po->ifindex && po->num && !po->running) { dev_add_pack(&po->prot_hook); sock_hold(sk); po->running = 1; } spin_unlock(&po->bind_lock); break; } } read_unlock(&packet_sklist_lock); return NOTIFY_DONE;}static int packet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg){ struct sock *sk = sock->sk; switch(cmd) { case SIOCOUTQ: { int amount = atomic_read(&sk->sk_wmem_alloc); return put_user(amount, (int __user *)arg); } case SIOCINQ: { struct sk_buff *skb; int amount = 0; spin_lock_bh(&sk->sk_receive_queue.lock); skb = skb_peek(&sk->sk_receive_queue); if (skb) amount = skb->len; spin_unlock_bh(&sk->sk_receive_queue.lock); return put_user(amount, (int __user *)arg); } case SIOCGSTAMP: return sock_get_timestamp(sk, (struct timeval __user *)arg); case SIOCGSTAMPNS: return sock_get_timestampns(sk, (struct timespec __user *)arg);#ifdef CONFIG_INET case SIOCADDRT: case SIOCDELRT: case SIOCDARP: case SIOCGARP: case SIOCSARP: case SIOCGIFADDR: case SIOCSIFADDR: case SIOCGIFBRDADDR: case SIOCSIFBRDADDR: case SIOCGIFNETMASK: case SIOCSIFNETMASK: case SIOCGIFDSTADDR: case SIOCSIFDSTADDR: case SIOCSIFFLAGS: return inet_dgram_ops.ioctl(sock, cmd, arg);#endif default: return -ENOIOCTLCMD; } return 0;}#ifndef CONFIG_PACKET_MMAP#define packet_mmap sock_no_mmap#define packet_poll datagram_poll#elsestatic unsigned int packet_poll(struct file * file, struct socket *sock, poll_table *wait){ struct sock *sk = sock->sk; struct packet_sock *po = pkt_sk(sk); unsigned int mask = datagram_poll(file, sock, wait); spin_lock_bh(&sk->sk_receive_queue.lock); if (po->pg_vec) { unsigned last = po->head ? po->head-1 : po->frame_max; struct tpacket_hdr *h; h = packet_lookup_frame(po, last); if (h->tp_status) mask |= POLLIN | POLLRDNORM; } spin_unlock_bh(&sk->sk_receive_queue.lock); return mask;}/* Dirty? Well, I still did not learn better way to account * for user mmaps. */static void packet_mm_open(struct vm_area_struct *vma){ struct file *file = vma->vm_file; struct socket * sock = file->private_data; struct sock *sk = sock->sk; if (sk) atomic_inc(&pkt_sk(sk)->mapped);}static void packet_mm_close(struct vm_area_struct *vma){ struct file *file = vma->vm_file; struct socket * sock = file->private_data; struct sock *sk = sock->sk; if (sk) atomic_dec(&pkt_sk(sk)->mapped);}static struct vm_operations_struct packet_mmap_ops = { .open = packet_mm_open, .close =packet_mm_close,};static void free_pg_vec(char **pg_vec, unsigned int order, unsigned int len){ int i; for (i = 0; i < len; i++) { if (likely(pg_vec[i])) free_pages((unsigned long) pg_vec[i], order); } kfree(pg_vec);}static inline char *alloc_one_pg_vec_page(unsigned long order){ return (char *) __get_free_pages(GFP_KERNEL | __GFP_COMP | __GFP_ZERO, order);}static char **alloc_pg_vec(struct tpacket_req *req, int order){ unsigned int block_nr = req->tp_block_nr; char **pg_vec; int i; pg_vec = kzalloc(block_nr * sizeof(char *), GFP_KERNEL); if (unlikely(!pg_vec)) goto out; for (i = 0; i < block_nr; i++) { pg_vec[i] = alloc_one_pg_vec_page(order); if (unlikely(!pg_vec[i])) goto out_free_pgvec; }out: return pg_vec;out_free_pgvec: free_pg_vec(pg_vec, order, block_nr); pg_vec = NULL; goto out;}static int packet_set_ring(struct sock *sk, struct tpacket_req *req, int closing){ char **pg_vec = NULL; struct packet_sock *po = pkt_sk(sk); int was_running, order = 0; __be16 num; int err = 0; if (req->tp_block_nr) { int i, l; /* Sanity tests and some calculations */ if (unlikely(po->pg_vec)) return -EBUSY; if (unlikely((int)req->tp_block_size <= 0)) return -EINVAL; if (unlikely(req->tp_block_size & (PAGE_SIZE - 1))) return -EINVAL; if (unlikely(req->tp_frame_size < TPACKET_HDRLEN)) return -EINVAL; if (unlikely(req->tp_frame_size & (TPACKET_ALIGNMENT - 1))) return -EINVAL; po->frames_per_block = req->tp_block_size/req->tp_frame_size; if (unlikely(po->frames_per_block <= 0)) return -EINVAL; if (unlikely((po->frames_per_block * req->tp_block_nr) != req->tp_frame_nr)) return -EINVAL; err = -ENOMEM; order = get_order(req->tp_block_size); pg_vec = alloc_pg_vec(req, order); if (unlikely(!pg_vec)) goto out; l = 0; for (i = 0; i < req->tp_block_nr; i++) { char *ptr = pg_vec[i]; struct tpacket_hdr *header; int k; for (k = 0; k < po->frames_per_block; k++) { header = (struct tpacket_hdr *) ptr; header->tp_status = TP_STATUS_KERNEL; ptr += req->tp_frame_size; } } /* Done */ } else { if (unlikely(req->tp_frame_nr)) return -EINVAL; } lock_sock(sk); /* Detach socket from network */ spin_lock(&po->bind_lock); was_running = po->running; num = po->num; if (was_running) { __dev_remove_pack(&po->prot_hook); po->num = 0; po->running = 0; __sock_put(sk); } spin_unlock(&po->bind_lock); synchronize_net(); err = -EBUSY; if (closing || atomic_read(&po->mapped) == 0) { err = 0;#define XC(a, b) ({ __typeof__ ((a)) __t; __t = (a); (a) = (b); __t; }) spin_lock_bh(&sk->sk_receive_queue.lock); pg_vec = XC(po->pg_vec, pg_vec); po->frame_max = (req->tp_frame_nr - 1); po->head = 0; po->frame_size = req->tp_frame_size; spin_unlock_bh(&sk->sk_receive_queue.lock); order = XC(po->pg_vec_order, order); req->tp_block_nr = XC(po->pg_vec_len, req->tp_block_nr); po->pg_vec_pages = req->tp_block_size/PAGE_SIZE; po->prot_hook.func = po->pg_vec ? tpacket_rcv : packet_rcv; skb_queue_purge(&sk->sk_receive_queue);#undef XC if (atomic_read(&po->mapped)) printk(KERN_DEBUG "packet_mmap: vma is busy: %d\n", atomic_read(&po->mapped)); } spin_lock(&po->bind_lock); if (was_running && !po->running) { sock_hold(sk); po->running = 1; po->num = num; dev_add_pack(&po->prot_hook); } spin_unlock(&po->bind_lock); release_sock(sk); if (pg_vec) free_pg_vec(pg_vec, order, req->tp_block_nr);out: return err;}static int packet_mmap(struct file *file, struct socket *sock, struct vm_area_struct *vma){ struct sock *sk = sock->sk; struct packet_sock *po = pkt_sk(sk); unsigned long size; unsigned long start; int err = -EINVAL; int i; if (vma->vm_pgoff) return -EINVAL; size = vma->vm_end - vma->vm_start; lock_sock(sk); if (po->pg_vec == NULL) goto out; if (size != po->pg_vec_len*po->pg_vec_pages*PAGE_SIZE) goto out; start = vma->vm_start; for (i = 0; i < po->pg_vec_len; i++) { struct page *page = virt_to_page(po->pg_vec[i]); int pg_num; for (pg_num = 0; pg_num < po->pg_vec_pages; pg_num++, page++) { err = vm_insert_page(vma, start, page); if (unlikely(err)) goto out; start += PAGE_SIZE; } } atomic_inc(&po->mapped); vma->vm_ops = &packet_mmap_ops; err = 0;out: release_sock(sk); return err;}#endifstatic const struct proto_ops packet_ops_spkt = { .family = PF_PACKET, .owner = THIS_MODULE, .release = packet_release, .bind = packet_bind_spkt, .connect = sock_no_connect, .socketpair = sock_no_socketpair, .accept = sock_no_accept, .getname = packet_getname_spkt, .poll = datagram_poll, .ioctl = packet_ioctl, .listen = sock_no_listen, .shutdown = sock_no_shutdown, .setsockopt = sock_no_setsockopt, .getsockopt = sock_no_getsockopt, .sendmsg = packet_sendmsg_spkt, .recvmsg = packet_recvmsg, .mmap = sock_no_mmap, .sendpage = sock_no_sendpage,};static const struct proto_ops packet_ops = { .family = PF_PACKET, .owner = THIS_MODULE, .release = packet_release, .bind = packet_bind, .connect = sock_no_connect, .socketpair = sock_no_socketpair, .accept = sock_no_accept, .getname = packet_getname, .poll = packet_poll, .ioctl = packet_ioctl, .listen = sock_no_listen, .shutdown = sock_no_shutdown, .setsockopt = packet_setsockopt, .getsockopt = packet_getsockopt, .sendmsg = packet_sendmsg, .recvmsg = packet_recvmsg, .mmap = packet_mmap, .sendpage = sock_no_sendpage,};static struct net_proto_family packet_family_ops = { .family = PF_PACKET, .create = packet_create, .owner = THIS_MODULE,};static struct notifier_block packet_netdev_notifier = { .notifier_call =packet_notifier,};#ifdef CONFIG_PROC_FSstatic inline struct sock *packet_seq_idx(loff_t off){ struct sock *s; struct hlist_node *node; sk_for_each(s, node, &packet_sklist) { if (!off--) return s; } return NULL;}static void *packet_seq_start(struct seq_file *seq, loff_t *pos){ read_lock(&packet_sklist_lock); return *pos ? packet_seq_idx(*pos - 1) : SEQ_START_TOKEN;}static void *packet_seq_next(struct seq_file *seq, void *v, loff_t *pos){ ++*pos; return (v == SEQ_START_TOKEN) ? sk_head(&packet_sklist) : sk_next((struct sock*)v) ;}static void packet_seq_stop(struct seq_file *seq, void *v){ read_unlock(&packet_sklist_lock);}static int packet_seq_show(struct seq_file *seq, void *v){ if (v == SEQ_START_TOKEN) seq_puts(seq, "sk RefCnt Type Proto Iface R Rmem User Inode\n"); else { struct sock *s = v; const struct packet_sock *po = pkt_sk(s); seq_printf(seq, "%p %-6d %-4d %04x %-5d %1d %-6u %-6u %-6lu\n", s, atomic_read(&s->sk_refcnt), s->sk_type, ntohs(po->num), po->ifindex, po->running, atomic_read(&s->sk_rmem_alloc), sock_i_uid(s), sock_i_ino(s) ); } return 0;}static const struct seq_operations packet_seq_ops = { .start = packet_seq_start, .next = packet_seq_next, .stop = packet_seq_stop, .show = packet_seq_show,};static int packet_seq_open(struct inode *inode, struct file *file){ return seq_open(file, &packet_seq_ops);}static const struct file_operations packet_seq_fops = { .owner = THIS_MODULE, .open = packet_seq_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release,};#endifstatic void __exit packet_exit(void){ proc_net_remove(&init_net, "packet"); unregister_netdevice_notifier(&packet_netdev_notifier); sock_unregister(PF_PACKET); proto_unregister(&packet_proto);}static int __init packet_init(void){ int rc = proto_register(&packet_proto, 0); if (rc != 0) goto out; sock_register(&packet_family_ops); register_netdevice_notifier(&packet_netdev_notifier); proc_net_fops_create(&init_net, "packet", 0, &packet_seq_fops);out: return rc;}module_init(packet_init);module_exit(packet_exit);MODULE_LICENSE("GPL");MODULE_ALIAS_NETPROTO(PF_PACKET);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -