patch-2.4.0-test7 linux/net/packet/af_packet.c

Next file: linux/net/sched/cls_tcindex.c
Previous file: linux/net/netsyms.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.0-test6/linux/net/packet/af_packet.c linux/net/packet/af_packet.c
@@ -5,7 +5,7 @@
  *
  *		PACKET - implements raw packet sockets.
  *
- * Version:	$Id: af_packet.c,v 1.39 2000/08/09 08:04:45 davem Exp $
+ * Version:	$Id: af_packet.c,v 1.41 2000/08/10 01:21:14 davem Exp $
  *
  * Authors:	Ross Biro, <bir7@leland.Stanford.Edu>
  *		Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
@@ -75,11 +75,6 @@
 extern int dlci_ioctl(unsigned int, void*);
 #endif
 
-/*
-   Old SOCK_PACKET. Do exist programs, which use it?
-   (not counting tcpdump) - lots of them yes - AC. 
-   
- */
 #define CONFIG_SOCK_PACKET	1
 
 /*
@@ -89,22 +84,10 @@
    It is more expensive, but I believe,
    it is really correct solution: reentereble, safe and fault tolerant.
 
-   Differences:
-   - Changing IFF_ALLMULTI from user level is disabled.
-     It could only confused multicast routing daemons, not more.
-   - IFF_PROMISC is faked by keeping reference count and
-     global flag, so that real IFF_PROMISC == (gflag|(count != 0))
-     I'd remove it too, but it would require recompilation tcpdump
-     and another applications, using promiscuous mode.
-   - SIOC{ADD/DEL}MULTI are moved to deprecated state,
-     they work, but complain. I do know who uses them.
-     
- 
-*************FIXME***************
-  Alexey : This doesnt cook Im afraid. We need the low level SIOCADD/DELMULTI
-  and also IFF_ALLMULTI for DECNET, Appletalk and other stuff as well as
-  BSD compatibility issues.
-  
+   IFF_PROMISC/IFF_ALLMULTI/SIOC{ADD/DEL}MULTI are faked by keeping
+   reference count and global flag, so that real status is
+   (gflag|(count != 0)), so that we can use obsolete faulty interface
+   not harming clever users.
  */
 #define CONFIG_PACKET_MULTICAST	1
 
@@ -206,6 +189,7 @@
 	unsigned int		frame_size;
 	unsigned int		iovmax;
 	unsigned int		head;
+	int			copy_thresh;
 #endif
 };
 
@@ -537,7 +521,9 @@
 	struct tpacket_hdr *h;
 	u8 * skb_head = skb->data;
 	unsigned snaplen;
-	unsigned long losing;
+	unsigned long status = TP_STATUS_LOSING|TP_STATUS_USER;
+	unsigned short macoff, netoff;
+	struct sk_buff *copy_skb = NULL;
 
 	if (skb->pkt_type == PACKET_LOOPBACK)
 		goto drop;
@@ -572,38 +558,55 @@
 			snaplen = res;
 	}
 #endif
-	spin_lock(&sk->receive_queue.lock);
-	h = po->iovec[po->head];
-
-	if (h->tp_status)
-		goto ring_is_full;
-	po->head = po->head != po->iovmax ? po->head+1 : 0;
-	po->stats.tp_packets++;
-	losing = TP_STATUS_LOSING;
-	if (!po->stats.tp_drops)
-		losing = 0;
-	spin_unlock(&sk->receive_queue.lock);
 
 	if (sk->type == SOCK_DGRAM) {
-		h->tp_mac = h->tp_net = TPACKET_ALIGN(TPACKET_HDRLEN) + 16;
+		macoff = netoff = TPACKET_ALIGN(TPACKET_HDRLEN) + 16;
 	} else {
 		unsigned maclen = skb->nh.raw - skb->data;
-		h->tp_net = TPACKET_ALIGN(TPACKET_HDRLEN + (maclen < 16 ? 16 : maclen));
-		h->tp_mac = h->tp_net - maclen;
+		netoff = TPACKET_ALIGN(TPACKET_HDRLEN + (maclen < 16 ? 16 : maclen));
+		macoff = netoff - maclen;
 	}
 
-	if (h->tp_mac + snaplen > po->frame_size) {
-		snaplen = po->frame_size - h->tp_mac;
+	if (macoff + snaplen > po->frame_size) {
+		if (po->copy_thresh &&
+		    atomic_read(&sk->rmem_alloc) + skb->truesize < (unsigned)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;
 	}
 
-	memcpy((u8*)h + h->tp_mac, skb->data, snaplen);
+	spin_lock(&sk->receive_queue.lock);
+	h = po->iovec[po->head];
+
+	if (h->tp_status)
+		goto ring_is_full;
+	po->head = po->head != po->iovmax ? po->head+1 : 0;
+	po->stats.tp_packets++;
+	if (copy_skb) {
+		status |= TP_STATUS_COPY;
+		__skb_queue_tail(&sk->receive_queue, copy_skb);
+	}
+	if (!po->stats.tp_drops)
+		status &= ~TP_STATUS_LOSING;
+	spin_unlock(&sk->receive_queue.lock);
+
+	memcpy((u8*)h + macoff, skb->data, snaplen);
 
-	h->tp_sec = skb->stamp.tv_sec;
-	h->tp_usec = skb->stamp.tv_usec;
 	h->tp_len = skb->len;
 	h->tp_snaplen = snaplen;
+	h->tp_mac = macoff;
+	h->tp_net = netoff;
+	h->tp_sec = skb->stamp.tv_sec;
+	h->tp_usec = skb->stamp.tv_usec;
 
 	sll = (struct sockaddr_ll*)((u8*)h + TPACKET_ALIGN(sizeof(*h)));
 	sll->sll_halen = 0;
@@ -615,7 +618,7 @@
 	sll->sll_pkttype = skb->pkt_type;
 	sll->sll_ifindex = dev->ifindex;
 
-	h->tp_status = losing|TP_STATUS_USER;
+	h->tp_status = status;
 	mb();
 
 	sk->data_ready(sk, 0);
@@ -634,6 +637,8 @@
 	spin_unlock(&sk->receive_queue.lock);
 
 	sk->data_ready(sk, 0);
+	if (copy_skb)
+		kfree_skb(copy_skb);
 	goto drop_n_restore;
 }
 
@@ -1286,6 +1291,18 @@
 			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;
+
+		sk->protinfo.af_packet->copy_thresh = val;
+		return 0;
+	}
 #endif
 	default:
 		return -ENOPROTOOPT;
@@ -1814,8 +1831,8 @@
 			     s->protinfo.af_packet->ifindex,
 			     s->protinfo.af_packet->running,
 			     atomic_read(&s->rmem_alloc),
-			     s->socket->inode->i_uid,
-			     s->socket->inode->i_ino
+			     sock_i_uid(s),
+			     sock_i_ino(s)
 			     );
 
 		buffer[len++]='\n';

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)