cregit-Linux how code gets into the kernel

Release 4.11 net/decnet/dn_nsp_in.c

Directory: net/decnet
/*
 * 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

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)6981.18%233.33%
Steven Whitehouse89.41%116.67%
Harvey Harrison44.71%116.67%
Linus Torvalds33.53%116.67%
Joe Perches11.18%116.67%
Total85100.00%6100.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

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)15793.45%116.67%
Arnaldo Carvalho de Melo42.38%233.33%
Linus Torvalds31.79%116.67%
James Morris21.19%116.67%
David S. Miller21.19%116.67%
Total168100.00%6100.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

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)17697.78%133.33%
Steven Whitehouse21.11%133.33%
Harvey Harrison21.11%133.33%
Total180100.00%3100.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

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)87100.00%1100.00%
Total87100.00%1100.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

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)45398.05%350.00%
Steven Whitehouse51.08%116.67%
Linus Torvalds30.65%116.67%
Harvey Harrison10.22%116.67%
Total462100.00%6100.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

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)4788.68%250.00%
Arnaldo Carvalho de Melo611.32%250.00%
Total53100.00%4100.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

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)19973.43%110.00%
Linus Torvalds5419.93%220.00%
Arnaldo Carvalho de Melo103.69%330.00%
Steven Whitehouse41.48%220.00%
Harvey Harrison20.74%110.00%
James Morris20.74%110.00%
Total271100.00%10100.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

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)5094.34%150.00%
Linus Torvalds35.66%150.00%
Total53100.00%2100.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