Release 4.11 net/decnet/dn_nsp_in.c
/*
* 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 Network Services Protocol (Input)
*
* Author: Eduardo Marcelo Serrat <emserrat@geocities.com>
*
* Changes:
*
* Steve Whitehouse: Split into dn_nsp_in.c and dn_nsp_out.c from
* original dn_nsp.c.
* Steve Whitehouse: Updated to work with my new routing architecture.
* Steve Whitehouse: Add changes from Eduardo Serrat's patches.
* Steve Whitehouse: Put all ack handling code in a common routine.
* Steve Whitehouse: Put other common bits into dn_nsp_rx()
* Steve Whitehouse: More checks on skb->len to catch bogus packets
* Fixed various race conditions and possible nasties.
* Steve Whitehouse: Now handles returned conninit frames.
* David S. Miller: New socket locking
* 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.
* Steve Whitehouse: Added backlog congestion level return codes.
* Patrick Caulfield:
* Steve Whitehouse: Added flow control support (outbound)
* Steve Whitehouse: Prepare for nonlinear skbs
*/
/******************************************************************************
(c) 1995-1998 E.M. Serrat emserrat@geocities.com
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
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*******************************************************************************/
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/string.h>
#include <linux/sockios.h>
#include <linux/net.h>
#include <linux/netdevice.h>
#include <linux/inet.h>
#include <linux/route.h>
#include <linux/slab.h>
#include <net/sock.h>
#include <net/tcp_states.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/termios.h>
#include <linux/interrupt.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/poll.h>
#include <linux/netfilter_decnet.h>
#include <net/neighbour.h>
#include <net/dst.h>
#include <net/dn.h>
#include <net/dn_nsp.h>
#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) {
char *devname = skb->dev ? skb->dev->name : "???";
struct dn_skb_cb *cb = DN_SKB_CB(skb);
net_info_ratelimited("DECnet: Martian packet (%s) dev=%s src=0x%04hx dst=0x%04hx srcport=0x%04hx dstport=0x%04hx\n",
msg, devname,
le16_to_cpu(cb->src),
le16_to_cpu(cb->dst),
le16_to_cpu(cb->src_port),
le16_to_cpu(cb->dst_port));
}
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds (pre-git) | 69 | 81.18% | 2 | 33.33% |
Steven Whitehouse | 8 | 9.41% | 1 | 16.67% |
Harvey Harrison | 4 | 4.71% | 1 | 16.67% |
Linus Torvalds | 3 | 3.53% | 1 | 16.67% |
Joe Perches | 1 | 1.18% | 1 | 16.67% |
Total | 85 | 100.00% | 6 | 100.00% |
/*
* For this function we've flipped the cross-subchannel bit
* if the message is an otherdata or linkservice message. Thus
* we can use it to work out what to update.
*/
static void dn_ack(struct sock *sk, struct sk_buff *skb, unsigned short ack)
{
struct dn_scp *scp = DN_SK(sk);
unsigned short type = ((ack >> 12) & 0x0003);
int wakeup = 0;
switch (type) {
case 0: /* ACK - Data */
if (dn_after(ack, scp->ackrcv_dat)) {
scp->ackrcv_dat = ack & 0x0fff;
wakeup |= dn_nsp_check_xmit_queue(sk, skb,
&scp->data_xmit_queue,
ack);
}
break;
case 1: /* NAK - Data */
break;
case 2: /* ACK - OtherData */
if (dn_after(ack, scp->ackrcv_oth)) {
scp->ackrcv_oth = ack & 0x0fff;
wakeup |= dn_nsp_check_xmit_queue(sk, skb,
&scp->other_xmit_queue,
ack);
}
break;
case 3: /* NAK - OtherData */
break;
}
if (wakeup && !sock_flag(sk, SOCK_DEAD))
sk->sk_state_change(sk);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds (pre-git) | 157 | 93.45% | 1 | 16.67% |
Arnaldo Carvalho de Melo | 4 | 2.38% | 2 | 33.33% |
Linus Torvalds | 3 | 1.79% | 1 | 16.67% |
James Morris | 2 | 1.19% | 1 | 16.67% |
David S. Miller | 2 | 1.19% | 1 | 16.67% |
Total | 168 | 100.00% | 6 | 100.00% |
/*
* This function is a universal ack processor.
*/
static int dn_process_ack(struct sock *sk, struct sk_buff *skb, int oth)
{
__le16 *ptr = (__le16 *)skb->data;
int len = 0;
unsigned short ack;
if (skb->len < 2)
return len;
if ((ack = le16_to_cpu(*ptr)) & 0x8000) {
skb_pull(skb, 2);
ptr++;
len += 2;
if ((ack & 0x4000) == 0) {
if (oth)
ack ^= 0x2000;
dn_ack(sk, skb, ack);
}
}
if (skb->len < 2)
return len;
if ((ack = le16_to_cpu(*ptr)) & 0x8000) {
skb_pull(skb, 2);
len += 2;
if ((ack & 0x4000) == 0) {
if (oth)
ack ^= 0x2000;
dn_ack(sk, skb, ack);
}
}
return len;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds (pre-git) | 176 | 97.78% | 1 | 33.33% |
Steven Whitehouse | 2 | 1.11% | 1 | 33.33% |
Harvey Harrison | 2 | 1.11% | 1 | 33.33% |
Total | 180 | 100.00% | 3 | 100.00% |
/**
* 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;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds (pre-git) | 87 | 100.00% | 1 | 100.00% |
Total | 87 | 100.00% | 1 | 100.00% |
/*
* 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. 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, unsigned short *reason)
{
struct dn_skb_cb *cb = DN_SKB_CB(skb);
struct nsp_conn_init_msg *msg = (struct nsp_conn_init_msg *)skb->data;
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(&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 = le16_to_cpu(msg->segsize);
if (!pskb_may_pull(skb, sizeof(*msg)))
goto err_out;
skb_pull(skb, sizeof(*msg));
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;
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;
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds (pre-git) | 453 | 98.05% | 3 | 50.00% |
Steven Whitehouse | 5 | 1.08% | 1 | 16.67% |
Linus Torvalds | 3 | 0.65% | 1 | 16.67% |
Harvey Harrison | 1 | 0.22% | 1 | 16.67% |
Total | 462 | 100.00% | 6 | 100.00% |
static void dn_nsp_conn_init(struct sock *sk, struct sk_buff *skb)
{
if (sk_acceptq_is_full(sk)) {
kfree_skb(skb);
return;
}
sk->sk_ack_backlog++;
skb_queue_tail(&sk->sk_receive_queue, skb);
sk->sk_state_change(sk);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds (pre-git) | 47 | 88.68% | 2 | 50.00% |
Arnaldo Carvalho de Melo | 6 | 11.32% | 2 | 50.00% |
Total | 53 | 100.00% | 4 | 100.00% |
static void dn_nsp_conn_conf(struct sock *sk, struct sk_buff *skb)
{
struct dn_skb_cb *cb = DN_SKB_CB(skb);
struct dn_scp *scp = DN_SK(sk);
unsigned char *ptr;
if (skb->len < 4)
goto out;
ptr = skb->data;
cb->services = *ptr++;
cb->info = *ptr++;
cb->segsize = le16_to_cpu(*(__le16 *)ptr);
if ((scp->state == DN_CI) || (scp->state == DN_CD)) {
scp->persist = 0;
scp->addrrem = cb->src_port;
sk->sk_state = TCP_ESTABLISHED;
scp->state = DN_RUN;
scp->services_rem = cb->services;
scp->info_rem = cb->info;
scp->segsize_rem = cb->segsize;
if ((scp->services_rem & NSP_FC_MASK) == NSP_FC_NONE)
scp->max_window = decnet_no_fc_max_cwnd;
if (skb->len > 0) {
u16 dlen = *skb->data;
if ((dlen <= 16) && (dlen <= skb->len)) {
scp->conndata_in.opt_optl = cpu_to_le16(dlen);
skb_copy_from_linear_data_offset(skb, 1,
scp->conndata_in.opt_data, dlen);
}
}
dn_nsp_send_link(sk, DN_NOCHANGE, 0);
if (!sock_flag(sk, SOCK_DEAD))
sk->sk_state_change(sk);
}
out:
kfree_skb(skb);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds (pre-git) | 199 | 73.43% | 1 | 10.00% |
Linus Torvalds | 54 | 19.93% | 2 | 20.00% |
Arnaldo Carvalho de Melo | 10 | 3.69% | 3 | 30.00% |
Steven Whitehouse | 4 | 1.48% | 2 | 20.00% |
Harvey Harrison | 2 | 0.74% | 1 | 10.00% |
James Morris | 2 | 0.74% | 1 | 10.00% |
Total | 271 | 100.00% | 10 | 100.00% |
static void dn_nsp_conn_ack(struct sock *sk, struct sk_buff *skb)
{
struct dn_scp *scp = DN_SK(sk);
if (scp->state == DN_CI) {
scp->state = DN_CD;
scp->persist = 0;
}
kfree_skb(skb);
}
Contributors
Person | Tokens | Prop | Commits | CommitProp |
Linus Torvalds (pre-git) | 50 | 94.34% | 1 | 50.00% |
Linus Torvalds | 3 | 5.66% | 1 | 50.00% |
Total | 53 | 100.00% | 2 | 100.00% |
static void dn_nsp_disc_init(struct sock *sk, struct sk_buff *skb)
{
struct dn_scp *scp = DN_SK(sk);
struct dn_skb_cb *cb = DN_SKB_CB(skb);
unsigned short reason;
if (skb->len < 2)
goto out;
reason = le16_to_cpu(*(__le16 *)skb->data);
skb_pull(skb, 2);
scp->discdata_in.opt_status = cpu_to_le16(reason);
scp->discdata_in.opt_optl = 0;
memset(scp->discdata_in.opt_data, 0, 16);
if (skb->len > 0) {
u16 dlen = *skb->data;
if ((dlen <= 16) && (dlen <= skb->len)) {
scp->discdata_in.opt_optl = cpu_to_le16(dlen);
skb_copy_from_linear_data_offset(skb, 1, scp->discdata_in.opt_data, dlen);
}
}
scp->addrrem = cb->src_port;
sk->sk_state = TCP_CLOSE;
switch (scp->state) {
case DN_CI