summaryrefslogtreecommitdiffstats
path: root/net/decnet/dn_nsp_in.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-11-28 03:58:46 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-11-28 03:58:46 +0000
commitb63ad0882a16a5d28003e57f2b0b81dee3fb322b (patch)
tree0a343ce219e2b8b38a5d702d66032c57b83d9720 /net/decnet/dn_nsp_in.c
parenta9d7bff9a84dba79609a0002e5321b74c4d64c64 (diff)
Merge with 2.4.0-test11.
Diffstat (limited to 'net/decnet/dn_nsp_in.c')
-rw-r--r--net/decnet/dn_nsp_in.c179
1 files changed, 159 insertions, 20 deletions
diff --git a/net/decnet/dn_nsp_in.c b/net/decnet/dn_nsp_in.c
index 6155ebccf..4754cd850 100644
--- a/net/decnet/dn_nsp_in.c
+++ b/net/decnet/dn_nsp_in.c
@@ -1,4 +1,3 @@
-
/*
* DECnet An implementation of the DECnet protocol suite for the LINUX
* operating system. DECnet is implemented using the BSD Socket
@@ -23,6 +22,9 @@
* Steve Whitehouse: Fixed lockup when socket filtering was enabled.
* Paul Koning: Fix to push CC sockets into RUN when acks are
* received.
+ * Steve Whitehouse:
+ * Patrick Caulfield: Checking conninits for correctness & sending of error
+ * responses.
*/
/******************************************************************************
@@ -71,6 +73,16 @@
#include <net/dn_dev.h>
#include <net/dn_route.h>
+extern int decnet_log_martians;
+
+static void dn_log_martian(struct sk_buff *skb, const char *msg)
+{
+ if (decnet_log_martians && net_ratelimit()) {
+ char *devname = skb->rx_dev ? skb->rx_dev->name : "???";
+ struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
+ printk(KERN_INFO "DECnet: Martian packet (%s) rx_dev=%s src=0x%04hx dst=0x%04hx srcport=0x%04hx dstport=0x%04hx\n", msg, devname, cb->src, cb->dst, cb->src_port, cb->dst_port);
+ }
+}
/*
* For this function we've flipped the cross-subchannel bit
@@ -83,8 +95,6 @@ static void dn_ack(struct sock *sk, struct sk_buff *skb, unsigned short ack)
unsigned short type = ((ack >> 12) & 0x0003);
int wakeup = 0;
- /* printk(KERN_DEBUG "dn_ack: %hd 0x%04hx\n", type, ack); */
-
switch(type) {
case 0: /* ACK - Data */
if (after(ack, scp->ackrcv_dat)) {
@@ -148,50 +158,172 @@ static int dn_process_ack(struct sock *sk, struct sk_buff *skb, int oth)
}
+/**
+ * dn_check_idf - Check an image data field format is correct.
+ * @pptr: Pointer to pointer to image data
+ * @len: Pointer to length of image data
+ * @max: The maximum allowed length of the data in the image data field
+ * @follow_on: Check that this many bytes exist beyond the end of the image data
+ *
+ * Returns: 0 if ok, -1 on error
+ */
+static inline int dn_check_idf(unsigned char **pptr, int *len, unsigned char max, unsigned char follow_on)
+{
+ unsigned char *ptr = *pptr;
+ unsigned char flen = *ptr++;
+
+ (*len)--;
+ if (flen > max)
+ return -1;
+ if ((flen + follow_on) > *len)
+ return -1;
+
+ *len -= flen;
+ *pptr = ptr + flen;
+ return 0;
+}
+
+/*
+ * Table of reason codes to pass back to node which sent us a badly
+ * formed message, plus text messages for the log. A zero entry in
+ * the reason field means "don't reply" otherwise a disc init is sent with
+ * the specified reason code.
+ */
+static struct {
+ unsigned short reason;
+ const char *text;
+} ci_err_table[] = {
+ { 0, "CI: Truncated message" },
+ { NSP_REASON_ID, "CI: Destination username error" },
+ { NSP_REASON_ID, "CI: Destination username type" },
+ { NSP_REASON_US, "CI: Source username error" },
+ { 0, "CI: Truncated at menuver" },
+ { 0, "CI: Truncated before access or user data" },
+ { NSP_REASON_IO, "CI: Access data format error" },
+ { NSP_REASON_IO, "CI: User data format error" }
+};
+
/*
* This function uses a slightly different lookup method
* to find its sockets, since it searches on object name/number
- * rather than port numbers
+ * rather than port numbers. Various tests are done to ensure that
+ * the incoming data is in the correct format before it is queued to
+ * a socket.
*/
-static struct sock *dn_find_listener(struct sk_buff *skb)
+static struct sock *dn_find_listener(struct sk_buff *skb, unsigned short *reason)
{
struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
struct nsp_conn_init_msg *msg = (struct nsp_conn_init_msg *)skb->data;
- struct sockaddr_dn addr;
+ struct sockaddr_dn dstaddr;
+ struct sockaddr_dn srcaddr;
unsigned char type = 0;
+ int dstlen;
+ int srclen;
+ unsigned char *ptr;
+ int len;
+ int err = 0;
+ unsigned char menuver;
- memset(&addr, 0, sizeof(struct sockaddr_dn));
+ memset(&dstaddr, 0, sizeof(struct sockaddr_dn));
+ memset(&srcaddr, 0, sizeof(struct sockaddr_dn));
+ /*
+ * 1. Decode & remove message header
+ */
cb->src_port = msg->srcaddr;
cb->dst_port = msg->dstaddr;
cb->services = msg->services;
cb->info = msg->info;
cb->segsize = dn_ntohs(msg->segsize);
+ if (skb->len < sizeof(*msg))
+ goto err_out;
+
skb_pull(skb, sizeof(*msg));
- /* printk(KERN_DEBUG "username2sockaddr 1\n"); */
- if (dn_username2sockaddr(skb->data, skb->len, &addr, &type) < 0)
+ len = skb->len;
+ ptr = skb->data;
+
+ /*
+ * 2. Check destination end username format
+ */
+ dstlen = dn_username2sockaddr(ptr, len, &dstaddr, &type);
+ err++;
+ if (dstlen < 0)
goto err_out;
+ err++;
if (type > 1)
goto err_out;
- /* printk(KERN_DEBUG "looking for listener...\n"); */
- return dn_sklist_find_listener(&addr);
+ len -= dstlen;
+ ptr += dstlen;
+
+ /*
+ * 3. Check source end username format
+ */
+ srclen = dn_username2sockaddr(ptr, len, &srcaddr, &type);
+ err++;
+ if (srclen < 0)
+ goto err_out;
+
+ len -= srclen;
+ ptr += srclen;
+ err++;
+ if (len < 1)
+ goto err_out;
+
+ menuver = *ptr;
+ ptr++;
+ len--;
+
+ /*
+ * 4. Check that optional data actually exists if menuver says it does
+ */
+ err++;
+ if ((menuver & (DN_MENUVER_ACC | DN_MENUVER_USR)) && (len < 1))
+ goto err_out;
+
+ /*
+ * 5. Check optional access data format
+ */
+ err++;
+ if (menuver & DN_MENUVER_ACC) {
+ if (dn_check_idf(&ptr, &len, 39, 1))
+ goto err_out;
+ if (dn_check_idf(&ptr, &len, 39, 1))
+ goto err_out;
+ if (dn_check_idf(&ptr, &len, 39, (menuver & DN_MENUVER_USR) ? 1 : 0))
+ goto err_out;
+ }
+
+ /*
+ * 6. Check optional user data format
+ */
+ err++;
+ if (menuver & DN_MENUVER_USR) {
+ if (dn_check_idf(&ptr, &len, 16, 0))
+ goto err_out;
+ }
+
+ /*
+ * 7. Look up socket based on destination end username
+ */
+ return dn_sklist_find_listener(&dstaddr);
err_out:
+ dn_log_martian(skb, ci_err_table[err].text);
+ *reason = ci_err_table[err].reason;
return NULL;
}
+
static void dn_nsp_conn_init(struct sock *sk, struct sk_buff *skb)
{
- /* printk(KERN_DEBUG "checking backlog...\n"); */
if (sk->ack_backlog >= sk->max_ack_backlog) {
kfree_skb(skb);
return;
}
- /* printk(KERN_DEBUG "waking up socket...\n"); */
sk->ack_backlog++;
skb_queue_tail(&sk->receive_queue, skb);
sk->state_change(sk);
@@ -528,13 +660,20 @@ static void dn_returned_conn_init(struct sock *sk, struct sk_buff *skb)
kfree_skb(skb);
}
-static void dn_nsp_no_socket(struct sk_buff *skb)
+static void dn_nsp_no_socket(struct sk_buff *skb, unsigned short reason)
{
struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
- switch(cb->nsp_flags) {
- case 0x28: /* Connect Confirm */
- dn_nsp_return_disc(skb, NSP_DISCCONF, NSP_REASON_NL);
+ if ((reason != NSP_REASON_OK) && ((cb->nsp_flags & 0x0c) == 0x08)) {
+ switch(cb->nsp_flags & 0x70) {
+ case 0x10:
+ case 0x60: /* (Retransmitted) Connect Init */
+ dn_nsp_return_disc(skb, NSP_DISCINIT, reason);
+ break;
+ case 0x20: /* Connect Confirm */
+ dn_nsp_return_disc(skb, NSP_DISCCONF, reason);
+ break;
+ }
}
kfree_skb(skb);
@@ -545,6 +684,7 @@ static int dn_nsp_rx_packet(struct sk_buff *skb)
struct dn_skb_cb *cb = (struct dn_skb_cb *)skb->cb;
struct sock *sk = NULL;
unsigned char *ptr = (unsigned char *)skb->data;
+ unsigned short reason = NSP_REASON_NL;
skb->h.raw = skb->data;
cb->nsp_flags = *ptr++;
@@ -584,7 +724,7 @@ static int dn_nsp_rx_packet(struct sk_buff *skb)
goto free_out;
case 0x10:
case 0x60:
- sk = dn_find_listener(skb);
+ sk = dn_find_listener(skb, &reason);
goto got_it;
}
}
@@ -632,7 +772,7 @@ got_it:
return ret;
}
- dn_nsp_no_socket(skb);
+ dn_nsp_no_socket(skb, reason);
return 1;
free_out:
@@ -664,7 +804,6 @@ int dn_nsp_backlog_rcv(struct sock *sk, struct sk_buff *skb)
* Control packet.
*/
if ((cb->nsp_flags & 0x0c) == 0x08) {
- /* printk(KERN_DEBUG "control type\n"); */
switch(cb->nsp_flags & 0x70) {
case 0x10:
case 0x60: