/*****************************************************************************

 @(#) sctp_input.c,v 0.7.8.1 2001/12/11 13:26:33 brian Exp

 -----------------------------------------------------------------------------

 Copyright (C) 2001  OpenSS7 Corporation <http://www.openss7.com>

 All Rights Reserved.

 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 (at your option) 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.

 You should have received a copy of the GNU General Public License along with
 this program; if not, write to the Free Software Foundation, Inc., 675 Mass
 Ave, Cambridge, MA 02139, USA.

 -----------------------------------------------------------------------------

 U.S. GOVERNMENT RESTRICTED RIGHTS.  If you are licensing this Software on
 behalf of the U.S. Government ("Government"), the following provisions apply
 to you.  If the Software is supplied by the Department of Defense ("DoD"), it
 is classified as "Commercial Computer Software" under paragraph 252.227-7014
 of the DoD Supplement to the Federal Acquisition Regulations ("DFARS") (or any
 successor regulations) and the Government is acquiring only the license rights
 granted herein (the license rights customarily provided to non-Government
 users).  If the Software is supplied to any unit or agency of the Government
 other than DoD, it is classified as "Restricted Computer Software" and the
 Government's rights in the Software are defined in paragraph 52.227-19 of the
 Federal Acquisition Regulations ("FAR") (or any success regulations) or, in
 the cases of NASA, in paragraph 18.52.227-86 of the NASA Supplement to the FAR
 (or any successor regulations).

 -----------------------------------------------------------------------------

 Last Modified 2001/12/11 13:26:33 by brian

 *****************************************************************************/

#ident "@(#) sctp_input.c,v 0.7.8.1 2001/12/11 13:26:33 brian Exp"

static char const ident[] = "SS7AlphaRelease(0.7.8.1) 2001/12/11 13:26:33";

#define __NO_VERSION__

#include <linux/config.h>
#include <linux/version.h>
#include <linux/modversions.h>
#include <linux/module.h>

#include <sys/stream.h>
#include <sys/stropts.h>
#include <sys/cmn_err.h>
#include <sys/dki.h>

#include "sctp_debug.h"
#include "sctp_bufq.h"
#include "sctp.h"
#include "sctp_defs.h"
#include "sctp_hash.h"
#include "sctp_cache.h"
#include "sctp_route.h"

#undef min  /* LiS should not have defined these */
#undef max  /* LiS should not have defined these */

#define sctp_daddr sctp_daddr__
#define sctp_saddr sctp_saddr__
#define sctp_strm  sctp_strm__
#ifdef ASSERT
#undef ASSERT
#endif

#include <net/ip.h>
#include <net/icmp.h>
#include <net/route.h>

#undef sctp_daddr
#undef sctp_saddr
#undef sctp_strm
#ifdef ASSERT
#undef ASSERT
#endif

#include "sctp_msg.h"
#include "sctp_input.h"
#include "sctp_output.h"

#include <linux/skbuff.h>

/*
 *  =========================================================================
 *
 *  IP Packet Handling
 *
 *  =========================================================================
 */

static int sctp_rcv_ootb(mblk_t *mp)
{
	struct iphdr *iph = (struct iphdr *)mp->b_datap->db_base;
	struct sctphdr *sh = (struct sctphdr *)(mp->b_datap->db_base + (iph->ihl<<2));
	struct sctpchdr *ch = (struct sctpchdr *)mp->b_rptr;
	int sat = inet_addr_type(iph->saddr);

	seldom();
	ensure( mp, return(-EFAULT) );

	if ( sat != RTN_UNICAST && sat != RTN_LOCAL )
		/* RFC 2960 8.4(1). */
		return(0);

	switch ( ch->type )
	{
		case SCTP_CTYPE_COOKIE_ACK:		/* RFC 2960 8.4(7).		*/
		case SCTP_CTYPE_ERROR:			/* RFC 2960 8.4(7).		*/
		case SCTP_CTYPE_ABORT:			/* RFC 2960 8.4(2).		*/
		case SCTP_CTYPE_SHUTDOWN_COMPLETE:	/* RFC 2960 8.4(6).		*/

		case SCTP_CTYPE_INIT:			/* RFC 2960 8.4(3) and (8).	*/
		case SCTP_CTYPE_INIT_ACK:		/* RFC 2960 8.4(8).		*/
		case SCTP_CTYPE_COOKIE_ECHO:		/* RFC 2960 8.4(4) and (8).	*/

		default:
			seldom();
			break;

		case SCTP_CTYPE_SHUTDOWN:		/* RFC 2960 8.4(8).		*/
		case SCTP_CTYPE_SACK:			/* RFC 2960 8.4(8).		*/
		case SCTP_CTYPE_HEARTBEAT:		/* RFC 2960 8.4(8).		*/
		case SCTP_CTYPE_HEARTBEAT_ACK:		/* RFC 2960 8.4(8).		*/
		case SCTP_CTYPE_DATA:			/* RFC 2960 8.4(8).		*/
			seldom(); sctp_send_abort_ootb(iph->saddr, iph->daddr, sh);
			break;

		case SCTP_CTYPE_SHUTDOWN_ACK:
			seldom(); sctp_send_shutdown_complete_ootb(iph->saddr, iph->daddr, sh);
			break;
	}
	freemsg(mp);
	return(0);
}

/*
 *  -------------------------------------------------------------------------
 *
 *  SCTP ERR
 *
 *  -------------------------------------------------------------------------
 *  We have received an ICMP error for the protocol number.  Because we don't
 *  want an races here we place a M_ERROR message on the read queue of the
 *  stream to which the message applies.  This distinguishes it from M_DATA
 *  messages.  It is processed within the stream with queues locked.  We have
 *  to copy the information because the skb will go away after this call.
 */
static void sctp_err(struct sk_buff *skb, unsigned char *dp, int len)
{
	sctp_t *sp;
	struct sctphdr *sh;
	struct iphdr *iph = (struct iphdr *)dp;
	size_t ihl;

	ensure( skb, return );
	ensure(  dp, return );
#define ICMP_MIN_LENGTH 8
	if ( len < (ihl = iph->ihl<<2) + ICMP_MIN_LENGTH ) {
		seldom(); return;
	}
	sh = (struct sctphdr *)(dp + ihl);

	sp = sh->v_tag
		? sctp_lookup_ptag(sh->v_tag, sh->srce, sh->dest, iph->saddr, iph->daddr)
		: sctp_lookup_tcb(sh->srce, sh->dest, iph->saddr, iph->daddr);

	usual(sp);

	if ( sp )
	{
		mblk_t *mp;

		if ( (mp = allocb(sizeof(uint32_t)+sizeof(struct icmphdr *), BPRI_MED)) )
		{
			mp->b_datap->db_type = M_ERROR;
			*((uint32_t *)	    mp->b_wptr)++ = iph->daddr;
			*((struct icmphdr *)mp->b_wptr)++ = *(skb->h.icmph);

			if ( sp->rq->q_qinfo->qi_putp )
				sp->rq->q_qinfo->qi_putp(sp->rq, mp);
			else	putq(sp->rq, mp);
			return;
		}
		rare(); return;
	} else usual( sp );
	return;
}

/*
 *  -------------------------------------------------------------------------
 *
 *  SCTP RCV
 *
 *  -------------------------------------------------------------------------
 *  This is the received frame handler for SCTP IPv4.  All packets received by
 *  IPv4 with the protocol number IPPROTO_SCTP will arrive here first.  We
 *  should be performing the Adler-32 checksum on the packet.  If the Adler-32
 *  checksum fails, then we should silently discard per RFC 2960.
 */
static void sctp_free(char *data)
{
	struct sk_buff *skb = (struct sk_buff *)data;
	ensure( skb, return );
	skb->used = 1;
	kfree_skb(skb);
	return;
}
static int sctp_rcv(struct sk_buff *skb, unsigned short len)
{
	mblk_t *mp;
	sctp_t *sp;
	uint32_t csum0, csum1, csum2;
	struct sctphdr *sh;

	if ( skb->pkt_type == PACKET_HOST ) {
	frtn_t fr = { &sctp_free, (char *)skb };

	/* pull up the ip header */
	__skb_pull(skb, skb->h.raw - skb->data);
	sh = (struct sctphdr *)skb->h.raw;
	len = skb->len;
	/*
	 *  perform the adler32 checksum per RFC 2960 Appendix B.
	 */
	csum0 = sh->check;
	csum1 = ntohl(csum0);
	sh->check = 0;
	csum2 = adler32(0x1, sh, len);
	sh->check = csum0;
	if ( csum1 == csum2 ) {

	/* pull to the ip header */
	__skb_push(skb, skb->data - skb->nh.raw);
	if ( (mp = esballoc(skb->data, skb->len, BPRI_MED, &fr)) ) {
		skb->used = 0;
		mp->b_datap->db_type = M_DATA;
		mp->b_wptr = mp->b_rptr + skb->len;
		/* trim the ip header */
		mp->b_rptr += skb->h.raw - skb->nh.raw;
		mp->b_rptr += sizeof(struct sctphdr);

		if ( (sp = sctp_lookup(sh, skb->nh.iph->daddr, skb->nh.iph->saddr)) )
		{
			if ( sp->rq->q_qinfo->qi_putp )
				sp->rq->q_qinfo->qi_putp(sp->rq, mp);
			else	putq(sp->rq, mp);
			return(0);
		}
		return sctp_rcv_ootb(mp);
	} else rare();
	} else rare();
	} else rare();
	kfree_skb(skb);
	return(0);
}

/*
 *  =========================================================================
 *
 *  IP Protocol Registration
 *
 *  =========================================================================
 */
#ifndef IPPROTO_SCTP
#define IPPROTO_SCTP 132
#endif

static struct inet_protocol sctp_protocol =
{
	sctp_rcv,	/* SCTP data handler	    */
	sctp_err,	/* SCTP error control	    */
	NULL,		/* next			    */
	IPPROTO_SCTP,	/* protocol ID		    */
	0,		/* copy			    */
	NULL,		/* data			    */
	"SCTP"		/* name			    */
};

void sctp_init_proto(void)
{
	inet_add_protocol(&sctp_protocol);
}

void sctp_term_proto(void)
{
	inet_del_protocol(&sctp_protocol);
}

