summaryrefslogtreecommitdiff
path: root/pfinet/linux-inet/datagram.c
diff options
context:
space:
mode:
authorMichael I. Bushnell <mib@gnu.org>1995-07-12 15:42:50 +0000
committerMichael I. Bushnell <mib@gnu.org>1995-07-12 15:42:50 +0000
commita81cd86c8d93236ffccfbee44b5818ba21523463 (patch)
treef098368d79e9ed9b39907730c28ed3182b69519d /pfinet/linux-inet/datagram.c
parentc7923f6aa252a29ccb4f16bd91469c9000a2bd94 (diff)
entered into RCS
Diffstat (limited to 'pfinet/linux-inet/datagram.c')
-rw-r--r--pfinet/linux-inet/datagram.c210
1 files changed, 210 insertions, 0 deletions
diff --git a/pfinet/linux-inet/datagram.c b/pfinet/linux-inet/datagram.c
new file mode 100644
index 00000000..cd248cfb
--- /dev/null
+++ b/pfinet/linux-inet/datagram.c
@@ -0,0 +1,210 @@
+/*
+ * SUCS NET3:
+ *
+ * Generic datagram handling routines. These are generic for all protocols. Possibly a generic IP version on top
+ * of these would make sense. Not tonight however 8-).
+ * This is used because UDP, RAW, PACKET and the to be released IPX layer all have identical select code and mostly
+ * identical recvfrom() code. So we share it here. The select was shared before but buried in udp.c so I moved it.
+ *
+ * Authors: Alan Cox <iiitac@pyr.swan.ac.uk>. (datagram_select() from old udp.c code)
+ *
+ * Fixes:
+ * Alan Cox : NULL return from skb_peek_copy() understood
+ * Alan Cox : Rewrote skb_read_datagram to avoid the skb_peek_copy stuff.
+ * Alan Cox : Added support for SOCK_SEQPACKET. IPX can no longer use the SO_TYPE hack but
+ * AX.25 now works right, and SPX is feasible.
+ * Alan Cox : Fixed write select of non IP protocol crash.
+ * Florian La Roche: Changed for my new skbuff handling.
+ *
+ * Note:
+ * A lot of this will change when the protocol/socket separation
+ * occurs. Using this will make things reasonably clean.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include "ip.h"
+#include "protocol.h"
+#include "route.h"
+#include "tcp.h"
+#include "udp.h"
+#include <linux/skbuff.h>
+#include "sock.h"
+
+
+/*
+ * Get a datagram skbuff, understands the peeking, nonblocking wakeups and possible
+ * races. This replaces identical code in packet,raw and udp, as well as the yet to
+ * be released IPX support. It also finally fixes the long standing peek and read
+ * race for datagram sockets. If you alter this routine remember it must be
+ * re-entrant.
+ */
+
+struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, int noblock, int *err)
+{
+ struct sk_buff *skb;
+ unsigned long intflags;
+
+ /* Socket is inuse - so the timer doesn't attack it */
+ save_flags(intflags);
+restart:
+ sk->inuse = 1;
+ while(skb_peek(&sk->receive_queue) == NULL) /* No data */
+ {
+ /* If we are shutdown then no more data is going to appear. We are done */
+ if (sk->shutdown & RCV_SHUTDOWN)
+ {
+ release_sock(sk);
+ *err=0;
+ return NULL;
+ }
+
+ if(sk->err)
+ {
+ release_sock(sk);
+ *err=-sk->err;
+ sk->err=0;
+ return NULL;
+ }
+
+ /* Sequenced packets can come disconnected. If so we report the problem */
+ if(sk->type==SOCK_SEQPACKET && sk->state!=TCP_ESTABLISHED)
+ {
+ release_sock(sk);
+ *err=-ENOTCONN;
+ return NULL;
+ }
+
+ /* User doesn't want to wait */
+ if (noblock)
+ {
+ release_sock(sk);
+ *err=-EAGAIN;
+ return NULL;
+ }
+ release_sock(sk);
+
+ /* Interrupts off so that no packet arrives before we begin sleeping.
+ Otherwise we might miss our wake up */
+ cli();
+ if (skb_peek(&sk->receive_queue) == NULL)
+ {
+ interruptible_sleep_on(sk->sleep);
+ /* Signals may need a restart of the syscall */
+ if (current->signal & ~current->blocked)
+ {
+ restore_flags(intflags);;
+ *err=-ERESTARTSYS;
+ return(NULL);
+ }
+ if(sk->err != 0) /* Error while waiting for packet
+ eg an icmp sent earlier by the
+ peer has finally turned up now */
+ {
+ *err = -sk->err;
+ sk->err=0;
+ restore_flags(intflags);
+ return NULL;
+ }
+ }
+ sk->inuse = 1;
+ restore_flags(intflags);
+ }
+ /* Again only user level code calls this function, so nothing interrupt level
+ will suddenly eat the receive_queue */
+ if (!(flags & MSG_PEEK))
+ {
+ skb=skb_dequeue(&sk->receive_queue);
+ if(skb!=NULL)
+ skb->users++;
+ else
+ goto restart; /* Avoid race if someone beats us to the data */
+ }
+ else
+ {
+ cli();
+ skb=skb_peek(&sk->receive_queue);
+ if(skb!=NULL)
+ skb->users++;
+ restore_flags(intflags);
+ if(skb==NULL) /* shouldn't happen but .. */
+ *err=-EAGAIN;
+ }
+ return skb;
+}
+
+void skb_free_datagram(struct sk_buff *skb)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ skb->users--;
+ if(skb->users>0)
+ {
+ restore_flags(flags);
+ return;
+ }
+ /* See if it needs destroying */
+ if(!skb->next && !skb->prev) /* Been dequeued by someone - ie it's read */
+ kfree_skb(skb,FREE_READ);
+ restore_flags(flags);
+}
+
+void skb_copy_datagram(struct sk_buff *skb, int offset, char *to, int size)
+{
+ /* We will know all about the fraglist options to allow >4K receives
+ but not this release */
+ memcpy_tofs(to,skb->h.raw+offset,size);
+}
+
+/*
+ * Datagram select: Again totally generic. Moved from udp.c
+ * Now does seqpacket.
+ */
+
+int datagram_select(struct sock *sk, int sel_type, select_table *wait)
+{
+ select_wait(sk->sleep, wait);
+ switch(sel_type)
+ {
+ case SEL_IN:
+ if (sk->type==SOCK_SEQPACKET && sk->state==TCP_CLOSE)
+ {
+ /* Connection closed: Wake up */
+ return(1);
+ }
+ if (skb_peek(&sk->receive_queue) != NULL || sk->err != 0)
+ { /* This appears to be consistent
+ with other stacks */
+ return(1);
+ }
+ return(0);
+
+ case SEL_OUT:
+ if (sk->prot && sk->prot->wspace(sk) >= MIN_WRITE_SPACE)
+ {
+ return(1);
+ }
+ if (sk->prot==NULL && sk->sndbuf-sk->wmem_alloc >= MIN_WRITE_SPACE)
+ {
+ return(1);
+ }
+ return(0);
+
+ case SEL_EX:
+ if (sk->err)
+ return(1); /* Socket has gone into error state (eg icmp error) */
+ return(0);
+ }
+ return(0);
+}