⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 af_packet.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * INET		An implementation of the TCP/IP protocol suite for the LINUX *		operating system.  INET is implemented using the  BSD Socket *		interface as the means of communication with the user level. * *		PACKET - implements raw packet sockets. * * Version:	$Id: af_packet.c,v 1.61 2002/02/08 03:57:19 davem Exp $ * * Authors:	Ross Biro *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> *		Alan Cox, <gw4pts@gw4pts.ampr.org> * * Fixes: *		Alan Cox	:	verify_area() now used correctly *		Alan Cox	:	new skbuff lists, look ma no backlogs! *		Alan Cox	:	tidied skbuff lists. *		Alan Cox	:	Now uses generic datagram routines I *					added. Also fixed the peek/read crash *					from all old Linux datagram code. *		Alan Cox	:	Uses the improved datagram code. *		Alan Cox	:	Added NULL's for socket options. *		Alan Cox	:	Re-commented the code. *		Alan Cox	:	Use new kernel side addressing *		Rob Janssen	:	Correct MTU usage. *		Dave Platt	:	Counter leaks caused by incorrect *					interrupt locking and some slightly *					dubious gcc output. Can you read *					compiler: it said _VOLATILE_ *	Richard Kooijman	:	Timestamp fixes. *		Alan Cox	:	New buffers. Use sk->mac.raw. *		Alan Cox	:	sendmsg/recvmsg support. *		Alan Cox	:	Protocol setting support *	Alexey Kuznetsov	:	Untied from IPv4 stack. *	Cyrus Durgin		:	Fixed kerneld for kmod. *	Michal Ostrowski        :       Module initialization cleanup. *         Ulises Alonso        :       Frame number limit removal and *                                      packet_set_ring memory leak. *		Eric Biederman	:	Allow for > 8 byte hardware addresses. *					The convention is that longer addresses *					will simply extend the hardware address *					byte arrays at the end of sockaddr_ll *					and packet_mreq. * *		This program is free software; you can redistribute it and/or *		modify it under the terms of the GNU General Public License *		as published by the Free Software Foundation; either version *		2 of the License, or (at your option) any later version. * */#include <linux/types.h>#include <linux/mm.h>#include <linux/capability.h>#include <linux/fcntl.h>#include <linux/socket.h>#include <linux/in.h>#include <linux/inet.h>#include <linux/netdevice.h>#include <linux/if_packet.h>#include <linux/wireless.h>#include <linux/kernel.h>#include <linux/kmod.h>#include <net/net_namespace.h>#include <net/ip.h>#include <net/protocol.h>#include <linux/skbuff.h>#include <net/sock.h>#include <linux/errno.h>#include <linux/timer.h>#include <asm/system.h>#include <asm/uaccess.h>#include <asm/ioctls.h>#include <asm/page.h>#include <asm/cacheflush.h>#include <asm/io.h>#include <linux/proc_fs.h>#include <linux/seq_file.h>#include <linux/poll.h>#include <linux/module.h>#include <linux/init.h>#ifdef CONFIG_INET#include <net/inet_common.h>#endif/*   Assumptions:   - if device has no dev->hard_header routine, it adds and removes ll header     inside itself. In this case ll header is invisible outside of device,     but higher levels still should reserve dev->hard_header_len.     Some devices are enough clever to reallocate skb, when header     will not fit to reserved space (tunnel), another ones are silly     (PPP).   - packet socket receives packets with pulled ll header,     so that SOCK_RAW should push it back.On receive:-----------Incoming, dev->hard_header!=NULL   mac_header -> ll header   data       -> dataOutgoing, dev->hard_header!=NULL   mac_header -> ll header   data       -> ll headerIncoming, dev->hard_header==NULL   mac_header -> UNKNOWN position. It is very likely, that it points to ll		 header.  PPP makes it, that is wrong, because introduce		 assymetry between rx and tx paths.   data       -> dataOutgoing, dev->hard_header==NULL   mac_header -> data. ll header is still not built!   data       -> dataResume  If dev->hard_header==NULL we are unlikely to restore sensible ll header.On transmit:------------dev->hard_header != NULL   mac_header -> ll header   data       -> ll headerdev->hard_header == NULL (ll header is added by device, we cannot control it)   mac_header -> data   data       -> data   We should set nh.raw on output to correct posistion,   packet classifier depends on it. *//* List of all packet sockets. */static HLIST_HEAD(packet_sklist);static DEFINE_RWLOCK(packet_sklist_lock);/* Private packet socket structures. */struct packet_mclist{	struct packet_mclist	*next;	int			ifindex;	int			count;	unsigned short		type;	unsigned short		alen;	unsigned char		addr[MAX_ADDR_LEN];};/* identical to struct packet_mreq except it has * a longer address field. */struct packet_mreq_max{	int		mr_ifindex;	unsigned short	mr_type;	unsigned short	mr_alen;	unsigned char	mr_address[MAX_ADDR_LEN];};#ifdef CONFIG_PACKET_MMAPstatic int packet_set_ring(struct sock *sk, struct tpacket_req *req, int closing);#endifstatic void packet_flush_mclist(struct sock *sk);struct packet_sock {	/* struct sock has to be the first member of packet_sock */	struct sock		sk;	struct tpacket_stats	stats;#ifdef CONFIG_PACKET_MMAP	char *			*pg_vec;	unsigned int		head;	unsigned int            frames_per_block;	unsigned int		frame_size;	unsigned int		frame_max;	int			copy_thresh;#endif	struct packet_type	prot_hook;	spinlock_t		bind_lock;	unsigned int		running:1,	/* prot_hook is attached*/				auxdata:1,				origdev:1;	int			ifindex;	/* bound device		*/	__be16			num;	struct packet_mclist	*mclist;#ifdef CONFIG_PACKET_MMAP	atomic_t		mapped;	unsigned int            pg_vec_order;	unsigned int		pg_vec_pages;	unsigned int		pg_vec_len;#endif};struct packet_skb_cb {	unsigned int origlen;	union {		struct sockaddr_pkt pkt;		struct sockaddr_ll ll;	} sa;};#define PACKET_SKB_CB(__skb)	((struct packet_skb_cb *)((__skb)->cb))#ifdef CONFIG_PACKET_MMAPstatic inline struct tpacket_hdr *packet_lookup_frame(struct packet_sock *po, unsigned int position){	unsigned int pg_vec_pos, frame_offset;	pg_vec_pos = position / po->frames_per_block;	frame_offset = position % po->frames_per_block;	return (struct tpacket_hdr *)(po->pg_vec[pg_vec_pos] + (frame_offset * po->frame_size));}#endifstatic inline struct packet_sock *pkt_sk(struct sock *sk){	return (struct packet_sock *)sk;}static void packet_sock_destruct(struct sock *sk){	BUG_TRAP(!atomic_read(&sk->sk_rmem_alloc));	BUG_TRAP(!atomic_read(&sk->sk_wmem_alloc));	if (!sock_flag(sk, SOCK_DEAD)) {		printk("Attempt to release alive packet socket: %p\n", sk);		return;	}	sk_refcnt_debug_dec(sk);}static const struct proto_ops packet_ops;static const struct proto_ops packet_ops_spkt;static int packet_rcv_spkt(struct sk_buff *skb, struct net_device *dev,  struct packet_type *pt, struct net_device *orig_dev){	struct sock *sk;	struct sockaddr_pkt *spkt;	if (dev->nd_net != &init_net)		goto out;	/*	 *	When we registered the protocol we saved the socket in the data	 *	field for just this event.	 */	sk = pt->af_packet_priv;	/*	 *	Yank back the headers [hope the device set this	 *	right or kerboom...]	 *	 *	Incoming packets have ll header pulled,	 *	push it back.	 *	 *	For outgoing ones skb->data == skb_mac_header(skb)	 *	so that this procedure is noop.	 */	if (skb->pkt_type == PACKET_LOOPBACK)		goto out;	if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL)		goto oom;	/* drop any routing info */	dst_release(skb->dst);	skb->dst = NULL;	/* drop conntrack reference */	nf_reset(skb);	spkt = &PACKET_SKB_CB(skb)->sa.pkt;	skb_push(skb, skb->data - skb_mac_header(skb));	/*	 *	The SOCK_PACKET socket receives _all_ frames.	 */	spkt->spkt_family = dev->type;	strlcpy(spkt->spkt_device, dev->name, sizeof(spkt->spkt_device));	spkt->spkt_protocol = skb->protocol;	/*	 *	Charge the memory to the socket. This is done specifically	 *	to prevent sockets using all the memory up.	 */	if (sock_queue_rcv_skb(sk,skb) == 0)		return 0;out:	kfree_skb(skb);oom:	return 0;}/* *	Output a raw packet to a device layer. This bypasses all the other *	protocol layers and you must therefore supply it with a complete frame */static int packet_sendmsg_spkt(struct kiocb *iocb, struct socket *sock,			       struct msghdr *msg, size_t len){	struct sock *sk = sock->sk;	struct sockaddr_pkt *saddr=(struct sockaddr_pkt *)msg->msg_name;	struct sk_buff *skb;	struct net_device *dev;	__be16 proto=0;	int err;	/*	 *	Get and verify the address.	 */	if (saddr)	{		if (msg->msg_namelen < sizeof(struct sockaddr))			return(-EINVAL);		if (msg->msg_namelen==sizeof(struct sockaddr_pkt))			proto=saddr->spkt_protocol;	}	else		return(-ENOTCONN);	/* SOCK_PACKET must be sent giving an address */	/*	 *	Find the device first to size check it	 */	saddr->spkt_device[13] = 0;	dev = dev_get_by_name(&init_net, saddr->spkt_device);	err = -ENODEV;	if (dev == NULL)		goto out_unlock;	err = -ENETDOWN;	if (!(dev->flags & IFF_UP))		goto out_unlock;	/*	 *	You may not queue a frame bigger than the mtu. This is the lowest level	 *	raw protocol and you must do your own fragmentation at this level.	 */	err = -EMSGSIZE;	if (len > dev->mtu + dev->hard_header_len)		goto out_unlock;	err = -ENOBUFS;	skb = sock_wmalloc(sk, len + LL_RESERVED_SPACE(dev), 0, GFP_KERNEL);	/*	 *	If the write buffer is full, then tough. At this level the user gets to	 *	deal with the problem - do your own algorithmic backoffs. That's far	 *	more flexible.	 */	if (skb == NULL)		goto out_unlock;	/*	 *	Fill it in	 */	/* FIXME: Save some space for broken drivers that write a	 * hard header at transmission time by themselves. PPP is the	 * notable one here. This should really be fixed at the driver level.	 */	skb_reserve(skb, LL_RESERVED_SPACE(dev));	skb_reset_network_header(skb);	/* Try to align data part correctly */	if (dev->header_ops) {		skb->data -= dev->hard_header_len;		skb->tail -= dev->hard_header_len;		if (len < dev->hard_header_len)			skb_reset_network_header(skb);	}	/* Returns -EFAULT on error */	err = memcpy_fromiovec(skb_put(skb,len), msg->msg_iov, len);	skb->protocol = proto;	skb->dev = dev;	skb->priority = sk->sk_priority;	if (err)		goto out_free;	/*	 *	Now send it	 */	dev_queue_xmit(skb);	dev_put(dev);	return(len);out_free:	kfree_skb(skb);out_unlock:	if (dev)		dev_put(dev);	return err;}static inline unsigned int run_filter(struct sk_buff *skb, struct sock *sk,				      unsigned int res){	struct sk_filter *filter;	rcu_read_lock_bh();	filter = rcu_dereference(sk->sk_filter);	if (filter != NULL)		res = sk_run_filter(skb, filter->insns, filter->len);	rcu_read_unlock_bh();	return res;}/*   This function makes lazy skb cloning in hope that most of packets   are discarded by BPF.   Note tricky part: we DO mangle shared skb! skb->data, skb->len   and skb->cb are mangled. It works because (and until) packets   falling here are owned by current CPU. Output packets are cloned   by dev_queue_xmit_nit(), input packets are processed by net_bh   sequencially, so that if we return skb to original state on exit,   we will not harm anyone. */static int packet_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev){	struct sock *sk;	struct sockaddr_ll *sll;	struct packet_sock *po;	u8 * skb_head = skb->data;	int skb_len = skb->len;	unsigned int snaplen, res;	if (dev->nd_net != &init_net)		goto drop;	if (skb->pkt_type == PACKET_LOOPBACK)		goto drop;	sk = pt->af_packet_priv;	po = pkt_sk(sk);	skb->dev = dev;	if (dev->header_ops) {		/* The device has an explicit notion of ll header,		   exported to higher levels.		   Otherwise, the device hides datails of it frame		   structure, so that corresponding packet head		   never delivered to user.		 */		if (sk->sk_type != SOCK_DGRAM)			skb_push(skb, skb->data - skb_mac_header(skb));		else if (skb->pkt_type == PACKET_OUTGOING) {			/* Special case: outgoing packets have ll header at head */			skb_pull(skb, skb_network_offset(skb));		}	}	snaplen = skb->len;	res = run_filter(skb, sk, snaplen);	if (!res)		goto drop_n_restore;	if (snaplen > res)		snaplen = res;	if (atomic_read(&sk->sk_rmem_alloc) + skb->truesize >=	    (unsigned)sk->sk_rcvbuf)		goto drop_n_acct;	if (skb_shared(skb)) {		struct sk_buff *nskb = skb_clone(skb, GFP_ATOMIC);		if (nskb == NULL)			goto drop_n_acct;		if (skb_head != skb->data) {			skb->data = skb_head;			skb->len = skb_len;		}		kfree_skb(skb);		skb = nskb;	}	BUILD_BUG_ON(sizeof(*PACKET_SKB_CB(skb)) + MAX_ADDR_LEN - 8 >		     sizeof(skb->cb));	sll = &PACKET_SKB_CB(skb)->sa.ll;	sll->sll_family = AF_PACKET;	sll->sll_hatype = dev->type;	sll->sll_protocol = skb->protocol;	sll->sll_pkttype = skb->pkt_type;	if (unlikely(po->origdev))		sll->sll_ifindex = orig_dev->ifindex;	else		sll->sll_ifindex = dev->ifindex;	sll->sll_halen = dev_parse_header(skb, sll->sll_addr);	PACKET_SKB_CB(skb)->origlen = skb->len;	if (pskb_trim(skb, snaplen))		goto drop_n_acct;	skb_set_owner_r(skb, sk);	skb->dev = NULL;	dst_release(skb->dst);	skb->dst = NULL;	/* drop conntrack reference */	nf_reset(skb);	spin_lock(&sk->sk_receive_queue.lock);	po->stats.tp_packets++;	__skb_queue_tail(&sk->sk_receive_queue, skb);	spin_unlock(&sk->sk_receive_queue.lock);	sk->sk_data_ready(sk, skb->len);	return 0;drop_n_acct:	spin_lock(&sk->sk_receive_queue.lock);	po->stats.tp_drops++;	spin_unlock(&sk->sk_receive_queue.lock);drop_n_restore:	if (skb_head != skb->data && skb_shared(skb)) {		skb->data = skb_head;		skb->len = skb_len;	}drop:	kfree_skb(skb);	return 0;}#ifdef CONFIG_PACKET_MMAPstatic int tpacket_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev){	struct sock *sk;	struct packet_sock *po;	struct sockaddr_ll *sll;	struct tpacket_hdr *h;	u8 * skb_head = skb->data;	int skb_len = skb->len;	unsigned int snaplen, res;	unsigned long status = TP_STATUS_LOSING|TP_STATUS_USER;	unsigned short macoff, netoff;	struct sk_buff *copy_skb = NULL;	struct timeval tv;	if (dev->nd_net != &init_net)		goto drop;	if (skb->pkt_type == PACKET_LOOPBACK)		goto drop;	sk = pt->af_packet_priv;	po = pkt_sk(sk);	if (dev->header_ops) {		if (sk->sk_type != SOCK_DGRAM)			skb_push(skb, skb->data - skb_mac_header(skb));		else if (skb->pkt_type == PACKET_OUTGOING) {			/* Special case: outgoing packets have ll header at head */			skb_pull(skb, skb_network_offset(skb));		}	}	if (skb->ip_summed == CHECKSUM_PARTIAL)		status |= TP_STATUS_CSUMNOTREADY;	snaplen = skb->len;	res = run_filter(skb, sk, snaplen);	if (!res)		goto drop_n_restore;	if (snaplen > res)		snaplen = res;	if (sk->sk_type == SOCK_DGRAM) {		macoff = netoff = TPACKET_ALIGN(TPACKET_HDRLEN) + 16;	} else {		unsigned maclen = skb_network_offset(skb);		netoff = TPACKET_ALIGN(TPACKET_HDRLEN + (maclen < 16 ? 16 : maclen));		macoff = netoff - maclen;	}	if (macoff + snaplen > po->frame_size) {		if (po->copy_thresh &&		    atomic_read(&sk->sk_rmem_alloc) + skb->truesize <		    (unsigned)sk->sk_rcvbuf) {			if (skb_shared(skb)) {				copy_skb = skb_clone(skb, GFP_ATOMIC);			} else {				copy_skb = skb_get(skb);				skb_head = skb->data;			}			if (copy_skb)				skb_set_owner_r(copy_skb, sk);		}		snaplen = po->frame_size - macoff;		if ((int)snaplen < 0)			snaplen = 0;	}	spin_lock(&sk->sk_receive_queue.lock);	h = packet_lookup_frame(po, po->head);	if (h->tp_status)		goto ring_is_full;	po->head = po->head != po->frame_max ? po->head+1 : 0;	po->stats.tp_packets++;	if (copy_skb) {		status |= TP_STATUS_COPY;		__skb_queue_tail(&sk->sk_receive_queue, copy_skb);	}	if (!po->stats.tp_drops)		status &= ~TP_STATUS_LOSING;	spin_unlock(&sk->sk_receive_queue.lock);	skb_copy_bits(skb, 0, (u8*)h + macoff, snaplen);	h->tp_len = skb->len;	h->tp_snaplen = snaplen;	h->tp_mac = macoff;	h->tp_net = netoff;	if (skb->tstamp.tv64)		tv = ktime_to_timeval(skb->tstamp);	else		do_gettimeofday(&tv);	h->tp_sec = tv.tv_sec;	h->tp_usec = tv.tv_usec;	sll = (struct sockaddr_ll*)((u8*)h + TPACKET_ALIGN(sizeof(*h)));	sll->sll_halen = dev_parse_header(skb, sll->sll_addr);	sll->sll_family = AF_PACKET;	sll->sll_hatype = dev->type;	sll->sll_protocol = skb->protocol;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -