|
OpenSS7 SS7 for the Common Man |
© Copyright 1997-2004,OpenSS7 Corporation, All Rights Reserved. |
||||||||||||||||||||||||||
| Home | Overview | Status | News | Documentation | Resources | About | |||||||||||||||||||||
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)));
}
}
|
|||||||||||||||||||||||||||
|
OpenSS7 SS7 for the Common Man |
Home | Overview | Status | News | Documentation | Resources | About | ||||||||||||||||||||
|
© Copyright 1997-2004,OpenSS7 Corporation, All Rights Reserved. |
|||||||||||||||||||||||||||