OpenSS7
SS7 for the
Common Man

© Copyright 1997-2004,OpenSS7 Corporation, All Rights Reserved.
Last modified:

Home Overview Status News Documentation Resources About
   
 Overview
 Status
 News
 Documentation
 Resources
 About

   
Home Index Prev Next More Download Info FAQ Mail   Home -> Resources -> Browse Source -> strss7/drivers/sctp/sctp_output.c


File /code/strss7/drivers/sctp/sctp_output.c


#ident "@(#) $RCSfile: sctp_output.c,v $ $Name:  $($Revision: 0.8.2.10 $) $Date: 2003/05/30 21:15:53 $"

static char const ident[] =
    "$RCSfile: sctp_output.c,v $ $Name:  $($Revision: 0.8.2.10 $) $Date: 2003/05/30 21:15:53 $";

#define __NO_VERSION__

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

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

#include "../debug.h"
#include "../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__
#define sctp_dup   sctp_dup__
#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
#undef sctp_dup
#ifdef ASSERT
#undef ASSERT
#endif

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

#include <linux/skbuff.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>

/* 
 *  QUEUE XMIT (Queue for transmission)
 *  -------------------------------------------------------------------------
 *  We need this broken out so that we can use the netfilter hooks.
 */
static inline int sctp_queue_xmit(struct sk_buff *skb)
{
	struct rtable *rt = (struct rtable *) skb->dst;
	struct iphdr *iph = skb->nh.iph;
	if (skb->len > rt->u.dst.pmtu) {
		rare();
		return ip_fragment(skb, skb->dst->output);
	} else {
		iph->frag_off |= __constant_htons(IP_DF);
		ip_send_check(iph);
		return skb->dst->output(skb);
	}
}

/* 
 *  XMIT OOTB (Disconnect Send with no Listening STREAM).
 *  -------------------------------------------------------------------------
 *  This sends disconnected without a STREAM.  All that is needed is a
 *  destination address and a message block.  The only time that we use this
 *  is for responding to OOTB packets with ABORT or SHUTDOWN COMPLETE.
 */
void sctp_xmit_ootb(daddr, saddr, mp)
	uint32_t daddr;
	uint32_t saddr;
	mblk_t *mp;
{
	struct rtable *rt = NULL;
	ensure(mp, return);
	if (!ip_route_output(&rt, daddr, 0, 0, 0)) {
		struct sk_buff *skb;
		struct net_device *dev = rt->u.dst.dev;
		size_t hlen = (dev->hard_header_len + 15) & ~15;
		size_t plen = msgdsize(mp);
		size_t tlen = plen + sizeof(struct iphdr);

		usual(hlen);
		usual(plen);

		if ((skb = alloc_skb(hlen + tlen, GFP_ATOMIC))) {
			mblk_t *bp;
			struct iphdr *iph;
			struct sctphdr *sh;
			unsigned char *data;

			skb_reserve(skb, hlen);
			iph = (struct iphdr *) __skb_put(skb, tlen);
			sh = (struct sctphdr *) (iph + 1);
			data = (unsigned char *) (sh);

			skb->dst = &rt->u.dst;
			skb->priority = 0;

			iph->version = 4;
			iph->ihl = 5;
			iph->tos = 0;
			iph->frag_off = 0;
			iph->ttl = 0;
			iph->daddr = rt->rt_dst;
			iph->saddr = saddr;
			iph->protocol = 132;
			iph->tot_len = htons(tlen);
			skb->nh.iph = iph;
			__ip_select_ident(iph, &rt->u.dst);

			for (bp = mp; bp; bp = bp->b_cont) {
				int blen = bp->b_wptr - bp->b_rptr;
				if (blen > 0) {
					bcopy(bp->b_rptr, data, blen);
					data += blen;
				} else
					rare();
			}
			sh->check = 0;
			sh->check = htonl(crc32c(~0UL, (unsigned char *) sh, plen));

			NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, dev, sctp_queue_xmit);
		} else
			rare();
	} else
		rare();
	/* sending OOTB reponses are one time events, if we can't send the message we just drop it, the peer will 
	   probably come back again later */
	freemsg(mp);
	return;
}

/* 
 *
 *  XMIT MSG  (Disconnected Send)
 *  -------------------------------------------------------------------------
 *  This sends disconnected sends.  All that is needed is a destination
 *  address and a message block.  The only time that we use this is for
 *  sending INIT ACKs (because we have not built a complete stream yet,
 *  however we do at least have a Listening STREAM or possibly an established
 *  STREAM that is partially qualitfied and can provide some TOS and other
 *  information for the packet.
 */
void sctp_xmit_msg(daddr, mp, sp)
	uint32_t daddr;
	mblk_t *mp;
	sctp_t *sp;
{
	struct rtable *rt = NULL;
	ensure(mp, return);
	if (!ip_route_output(&rt, daddr, 0, RT_TOS(sp->ip_tos) | sp->ip_dontroute, 0)) {
		struct sk_buff *skb;
		struct net_device *dev = rt->u.dst.dev;
		size_t hlen = (dev->hard_header_len + 15) & ~15;
		size_t plen = msgdsize(mp);
		size_t tlen = plen + sizeof(struct iphdr);
		size_t alen = 0;

		usual(hlen);
		usual(plen);

		if ((skb = alloc_skb(hlen + tlen, GFP_ATOMIC))) {
			mblk_t *bp;
			struct iphdr *iph;
			struct sctphdr *sh;
			unsigned char *data;

			skb_reserve(skb, hlen);
			iph = (struct iphdr *) __skb_put(skb, tlen);
			sh = (struct sctphdr *) (iph + 1);
			data = (unsigned char *) (sh);

			skb->dst = &rt->u.dst;
			skb->priority = sp->ip_priority;

			iph->version = 4;
			iph->ihl = 5;
			iph->tos = sp->ip_tos;
			iph->frag_off = 0;
			iph->ttl = sp->ip_ttl;
			iph->daddr = rt->rt_dst;
			iph->saddr = rt->rt_src;
			iph->protocol = sp->ip_proto;
			iph->tot_len = htons(tlen);
			skb->nh.iph = iph;
			__ip_select_ident(iph, &rt->u.dst);

			for (bp = mp; bp; bp = bp->b_cont) {
				int blen = bp->b_wptr - bp->b_rptr;
				if (blen > 0) {
					bcopy(bp->b_rptr, data, blen);
					data += blen;
					alen += blen;
				} else
					rare();
			}
			sh->check = 0;
			sh->check = htonl(crc32c(~0UL, (unsigned char *) sh, plen));

			NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, dev, sctp_queue_xmit);
		} else
			rare();
	} else
		rare();
	/* sending INIT ACKs are one time events, if we can't get the response off, we just drop the INIT ACK:
	   the peer should send us another INIT * in a short while... */
	freemsg(mp);
	return;
}

/* 
 *  SEND MSG  (Connected Send)
 *  -------------------------------------------------------------------------
 *  This sends connected sends.  It requires a STREAM a desination address
 *  structure and a message block.  This function does not free the message
 *  block.  The caller is responsible for the message block.
 */
STATIC int break_packets = 0;
STATIC int seed = 152;

STATIC unsigned char random(void)
{
	return (unsigned char) (seed = seed * 60691 + 1);
}

void sctp_send_msg(sp, sd, mp)
	sctp_t *sp;
	sctp_daddr_t *sd;
	mblk_t *mp;
{
	int tried_update;

	ensure(sp, return);
	ensure(mp, return);

	for (tried_update = 0; !tried_update; sctp_update_routes(sp, 1), tried_update++) {
		if (sd && sd->dst_cache &&
		    (!sd->dst_cache->obsolete || sd->dst_cache->ops->check(sd->dst_cache, 0))) {

			struct sk_buff *skb;
			struct net_device *dev;
			size_t hlen, plen, tlen;

#ifdef ERROR_GENERATOR
			if ((sp->options & SCTP_OPTION_DBREAK)
			    && sd->daddr == 0x010000ff && ++break_packets > BREAK_GENERATOR_LEVEL) {
				if (break_packets > BREAK_GENERATOR_LIMIT)
					break_packets = 0;
				return;
			}
			if ((sp->options & SCTP_OPTION_BREAK)
			    && (sd == sp->daddr || sd == sp->daddr->next)
			    && ++sd->packets > BREAK_GENERATOR_LEVEL) {
				return;
			}
			if ((sp->options & SCTP_OPTION_DROPPING)
			    && ++sd->packets > ERROR_GENERATOR_LEVEL) {
				if (sd->packets > ERROR_GENERATOR_LIMIT)
					sd->packets = 0;
				return;
			}
			if ((sp->options & SCTP_OPTION_RANDOM)
			    && ++sd->packets > 2 * ERROR_GENERATOR_LEVEL) {
				if (!(random() & 0x03))
					return;
			}
#endif
			dev = sd->dst_cache->dev;
			plen = SCTP_TCB(mp)->dlen;
			hlen = (dev->hard_header_len + 15) & ~15;
			tlen = sizeof(struct iphdr) + plen;

			unusual(plen == 0 || plen > 1 << 14);

			if ((skb = alloc_skb(hlen + tlen, GFP_ATOMIC))) {
				mblk_t *bp;
				struct iphdr *iph;
				struct sctphdr *sh;
				unsigned char *head, *data;
				size_t alen = 0;

				skb_reserve(skb, hlen);
				iph = (struct iphdr *) __skb_put(skb, tlen);
				sh = (struct sctphdr *) (iph + 1);
				data = (unsigned char *) (sh);
				head = data;

				skb->dst = dst_clone(sd->dst_cache);
				skb->priority = sp->ip_priority;

				iph->version = 4;
				iph->ihl = 5;
				iph->tos = sp->ip_tos;
				iph->frag_off = 0;
				iph->ttl = sp->ip_ttl;
				iph->daddr = sd->daddr;
				iph->saddr = sd->saddr;
				iph->protocol = sp->ip_proto;
				iph->tot_len = htons(tlen);
				skb->nh.iph = iph;
				__ip_select_ident(iph, sd->dst_cache);

				for (bp = mp; bp; bp = bp->b_next) {
					mblk_t *db;
					size_t clen = 0;
					sctp_tcb_t *cb = SCTP_TCB(bp);

					cb->daddr = sd;
					cb->when = jiffies;
					cb->flags |= SCTPCB_FLAG_SENT;
					cb->trans++;

					for (db = bp; db; db = db->b_cont) {
						int blen = db->b_wptr - db->b_rptr;
						normal(db->b_datap->db_type == M_DATA);
						if (db->b_datap->db_type == M_DATA) {
							normal(blen > 0);
							if (blen > 0) {
								ensure(head + plen >= data + blen, kfree_skb(skb);
								       return);
								bcopy(db->b_rptr, data, blen);
								data += blen;
								clen += blen;
							}
						}
					}
					{
						/* pad each chunk if not padded already */
						size_t pad = PADC(clen) - clen;
						ensure(head + plen >= data + pad, kfree_skb(skb);
						       return);
						bzero(data, pad);
						data += pad;
						alen += clen + pad;
					}
				}
				if (alen != plen) {
					usual(alen == plen);
					ptrace(("alen = %u, plen = %u discarding\n", alen, plen));
					kfree_skb(skb);
					return;
				}

				sh->check = 0;
				sh->check = htonl(crc32c(~0UL, (unsigned char *) sh, plen));

				NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, dev, sctp_queue_xmit);
				/* 
				 *  Whenever we transmit something, we expect a reply to our v_tag, so
				 *  we put ourselves in the 1st level vtag caches expecting a quick
				 *  reply.
				 */
				if (!((1 << sp->s_state) & (SCTPF_CLOSED | SCTPF_LISTEN)))
					sctp_cache[sp->hashent] = sp;
				break;

			} else
				rare();
		} else
			usual(sd && sd->dst_cache &&
			      (!sd->dst_cache->obsolete || sd->dst_cache->ops->check(sd->dst_cache, 0)));
	}
}


Home Index Prev Next More Download Info FAQ Mail   Home -> Resources -> Browse Source -> strss7/drivers/sctp/sctp_output.c

OpenSS7
SS7 for the
Common Man
Home Overview Status News Documentation Resources About

© Copyright 1997-2004,OpenSS7 Corporation, All Rights Reserved.
Last modified: