patch-2.3.4 linux/net/decnet/dn_raw.c

Next file: linux/net/decnet/dn_route.c
Previous file: linux/net/decnet/dn_nsp_out.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.3/linux/net/decnet/dn_raw.c linux/net/decnet/dn_raw.c
@@ -0,0 +1,383 @@
+/*
+ * DECnet       An implementation of the DECnet protocol suite for the LINUX
+ *              operating system.  DECnet is implemented using the  BSD Socket
+ *              interface as the means of communication with the user level.
+ *
+ *              DECnet Raw Sockets Interface
+ *
+ * Author:      Steve Whitehouse <SteveW@ACM.org>
+ *
+ *
+ * Changes:
+ *           Steve Whitehouse - connect() function.
+ */
+
+#include <linux/config.h>
+#include <linux/net.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <net/sock.h>
+#include <net/dn.h>
+#include <net/dn_raw.h>
+
+static struct sock *dn_raw_nsp_sklist = NULL;
+static struct sock *dn_raw_routing_sklist = NULL;
+#ifdef CONFIG_DECNET_MOP
+static struct sock *dn_raw_mop_sklist = NULL;
+#endif /* CONFIG_DECNET_MOP */
+
+static void dn_raw_autobind(struct sock *sk)
+{
+
+	switch(sk->protocol) {
+		case DNPROTO_NSP:
+			sklist_insert_socket(&dn_raw_nsp_sklist, sk);
+			break;
+		case DNPROTO_ROU:
+			sklist_insert_socket(&dn_raw_routing_sklist, sk);
+			break;
+#ifdef CONFIG_DECNET_MOP
+		case DNPROTO_MOP:
+			sklist_insert_socket(&dn_raw_mop_sklist, sk);
+#endif /* CONFIG_DECNET_MOP */
+		default:
+			printk(KERN_DEBUG "dn_raw_autobind: Unknown protocol\n");
+			return;
+	}
+
+	sk->zapped = 0;
+}
+
+static int dn_raw_release(struct socket *sock, struct socket *peer)
+{
+	struct sock *sk = sock->sk;
+
+	if (sk == NULL)
+		return 0;
+
+	if (!sk->dead) sk->state_change(sk);
+
+	sk->dead = 1;
+	sk->socket = NULL;
+	sock->sk = NULL;
+
+	switch(sk->protocol) {
+		case DNPROTO_NSP:
+			sklist_destroy_socket(&dn_raw_nsp_sklist, sk);
+			break;
+		case DNPROTO_ROU:
+			sklist_destroy_socket(&dn_raw_routing_sklist, sk);
+			break;
+#ifdef CONFIG_DECNET_MOP
+		case DNPROTO_MOP:
+			sklist_destroy_socket(&dn_raw_mop_sklist, sk);
+			break;
+#endif /* CONFIG_DECNET_MOP */
+	}
+
+	return 0;
+}
+
+/*
+ * Bind does odd things with raw sockets. Its basically used to filter
+ * the incomming packets, but this differs with the different layers
+ * at which you extract packets.
+ *
+ * For Routing layer sockets, the object name is a host ordered unsigned
+ * short which is a mask for the 16 different types of possible routing
+ * packet. I'd like to also select by destination address of the packets
+ * but alas, this is rather too dificult to do at the moment.
+ */
+static int dn_raw_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
+{
+	struct sock *sk = sock->sk;
+	struct sockaddr_dn *addr = (struct sockaddr_dn *)uaddr;
+
+	if (addr_len != sizeof(struct sockaddr_dn))
+		return -EINVAL;
+
+	if (sk->zapped == 0)
+		return -EINVAL;
+
+	switch(sk->protocol) {
+		case DNPROTO_ROU:
+			if (addr->sdn_objnamel && (addr->sdn_objnamel != 2))
+				return -EINVAL;
+			/* Fall through here */
+		case DNPROTO_NSP:
+			if (addr->sdn_add.a_len && (addr->sdn_add.a_len != 2))
+				return -EINVAL;
+			break;
+		default:
+			return -EPROTONOSUPPORT;
+	}
+
+	if (addr->sdn_objnamel > (DN_MAXOBJL-1))
+		return -EINVAL;
+
+	if (addr->sdn_add.a_len > DN_MAXADDL)
+		return -EINVAL;
+
+
+	memcpy(&sk->protinfo.dn.addr, addr, sizeof(struct sockaddr_dn));
+
+	dn_raw_autobind(sk);
+
+	return 0;
+}
+
+/*
+ * This is to allow send() and write() to work. You set the destination address
+ * with this function.
+ */
+static int dn_raw_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags)
+{
+	struct sock *sk = sock->sk;
+	struct dn_scp *scp = &sk->protinfo.dn;
+	struct sockaddr_dn *saddr = (struct sockaddr_dn *)uaddr;
+
+	if (addr_len != sizeof(struct sockaddr_dn))
+		return -EINVAL;
+
+	if (saddr->sdn_family != AF_DECnet)
+		return -EINVAL;
+
+	if (saddr->sdn_objnamel > (DN_MAXOBJL-1))
+		return -EINVAL;
+
+	if (saddr->sdn_add.a_len > DN_MAXADDL)
+		return -EINVAL;
+
+	if (sk->zapped)
+		dn_raw_autobind(sk);
+
+	memcpy(&scp->peer, saddr, sizeof(struct sockaddr_dn));
+
+	return 0;
+}
+
+/*
+ * TBD.
+ */
+static int dn_raw_sendmsg(struct socket *sock, struct msghdr *hdr, int size,
+			struct scm_cookie *scm)
+{
+	struct sock *sk = sock->sk;
+
+	if (sk->zapped)
+		dn_raw_autobind(sk);
+
+	if (sk->protocol != DNPROTO_NSP)
+		return -EOPNOTSUPP;
+
+	return 0;
+}
+
+/*
+ * This works fine, execpt that it doesn't report the originating address
+ * or anything at the moment.
+ */
+static int dn_raw_recvmsg(struct socket *sock, struct msghdr *msg, int size,
+			int flags, struct scm_cookie *scm)
+{
+	struct sock *sk = sock->sk;
+	struct sk_buff *skb;
+	int err = 0;
+	int copied = 0;
+
+	if (sk->zapped)
+		dn_raw_autobind(sk);
+
+	if ((skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &err)) == NULL)
+		goto out;
+
+	copied = skb->len;
+
+	if (copied > size) {
+		copied = size;
+		msg->msg_flags |= MSG_TRUNC;
+	}
+
+	if ((err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied)) != 0) {
+		if (flags & MSG_PEEK)
+			atomic_dec(&skb->users);
+		else
+			skb_queue_head(&sk->receive_queue, skb);
+
+		goto out;
+	}
+
+	skb_free_datagram(sk, skb);
+
+out:
+	return copied ? copied : err;
+}
+
+struct proto_ops dn_raw_proto_ops = {
+	AF_DECnet,
+
+	sock_no_dup,
+	dn_raw_release,
+	dn_raw_bind,
+	dn_raw_connect,
+	sock_no_socketpair,
+	sock_no_accept,
+	sock_no_getname,
+	datagram_poll,
+	sock_no_ioctl,
+	sock_no_listen,
+	sock_no_shutdown,
+	sock_no_setsockopt,
+	sock_no_getsockopt,
+	sock_no_fcntl,
+	dn_raw_sendmsg,
+	dn_raw_recvmsg
+};
+
+#ifdef CONFIG_PROC_FS
+int dn_raw_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+	int len = 0;
+	off_t pos = 0;
+	off_t begin = 0;
+	struct sock *sk;
+
+	cli();	
+	for(sk = dn_raw_nsp_sklist; sk; sk = sk->next) {	
+		len += sprintf(buffer+len, "NSP\n");
+
+		pos = begin + len;
+
+		if (pos < offset) {
+			len = 0;
+			begin = pos;
+		}
+
+		if (pos > offset + length)
+			goto all_done;
+
+	}
+
+	for(sk = dn_raw_routing_sklist; sk; sk = sk->next) {
+		len += sprintf(buffer+len, "ROU\n");
+
+		pos = begin + len;
+
+		if (pos < offset) {
+			len = 0;
+			begin = pos;
+		}
+
+		if (pos > offset + length)
+			goto all_done;
+	}
+
+#ifdef CONFIG_DECNET_MOP
+	for(sk = dn_raw_mop_sklist; sk; sk = sk->next) {
+		len += sprintf(buffer+len, "MOP\n");
+
+		pos = begin + len;
+
+		if (pos < offset) {
+			len = 0;
+			begin = pos;
+		}
+
+		if (pos > offset + length)
+			goto all_done;
+	}
+#endif /* CONFIG_DECNET_MOP */
+
+all_done:
+	sti();
+
+	*start = buffer + (offset - begin);
+	len -= (offset - begin);
+
+	if (len > length) len = length;
+
+	return(len);
+}
+#endif /* CONFIG_PROC_FS */
+
+void dn_raw_rx_nsp(struct sk_buff *skb)
+{
+	struct sock *sk;
+	struct sk_buff *skb2;
+	unsigned long cpuflags;
+
+	save_flags(cpuflags);
+	cli();
+	for(sk = dn_raw_nsp_sklist; sk != NULL; sk = sk->next) {
+		if (skb->len > sock_rspace(sk))
+			continue;
+		if (sk->dead)
+			continue;
+		if ((skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) {
+			skb_set_owner_r(skb2, sk);
+			__skb_queue_tail(&sk->receive_queue, skb2);
+			sk->data_ready(sk, skb->len);	
+		}
+	}
+	restore_flags(cpuflags);
+}
+
+void dn_raw_rx_routing(struct sk_buff *skb)
+{
+	struct sock *sk;
+	struct sk_buff *skb2;
+	struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+	unsigned long cpuflags;
+	unsigned short rt_flagmask;
+	unsigned short objnamel;
+	struct dn_scp *scp;
+
+	save_flags(cpuflags);
+	cli();
+	for(sk = dn_raw_routing_sklist; sk != NULL; sk = sk->next) {
+		if (skb->len > sock_rspace(sk))
+			continue;
+		if (sk->dead)
+			continue;
+		scp = &sk->protinfo.dn;
+
+		rt_flagmask = *(unsigned short *)scp->addr.sdn_objname;
+		objnamel = dn_ntohs(scp->addr.sdn_objnamel);
+
+		if ((objnamel == 2) && (!((1 << (cb->rt_flags & 0x0f)) & rt_flagmask)))
+			continue;
+
+		if ((skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) {
+			skb_set_owner_r(skb2, sk);
+			__skb_queue_tail(&sk->receive_queue, skb2);
+			sk->data_ready(sk, skb->len);
+		}
+	}
+	restore_flags(cpuflags);
+}
+
+#ifdef CONFIG_DECNET_MOP
+void dn_raw_rx_mop(struct sk_buff *skb)
+{
+	struct sock *sk;
+	struct sk_buff *skb2;
+	unsigned long cpuflags;
+
+	save_flags(cpuflags);
+	cli();
+	for(sk = dn_raw_mop_sklist; sk != NULL; sk = sk->next) {
+		if (skb->len > sock_rspace(sk))
+			continue;
+		if (sk->dead)
+			continue;
+		if ((skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) {
+			skb_set_owner_r(skb2, sk);
+			__skb_queue_tail(&sk->receive_queue, skb2);
+			sk->data_ready(sk, skb->len);
+		}
+	}
+	restore_flags(cpuflags);
+}
+#endif /* CONFIG_DECNET_MOP */

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