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

 @(#) $Id: mtp_sm.c,v 0.7.2.12 2000/10/05 10:23:04 brian Exp $

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

 Copyright (C) 1997, 1998, 1999, 2000  Brian Bidulock <bidulock@dallas.net>

 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.

 Last Modified $Date: 2000/10/05 10:23:04 $ by $Author: brian $

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

 $Log: mtp_sm.c,v $
 Revision 0.7.2.12  2000/10/05 10:23:04  brian
 Some work on link management and test.

 Revision 0.7.2.12  2000/10/03 00:53:54  brian
 Some work on link management and test.

 Revision 0.7.2.11  2000/09/24 07:32:56  brian
 Added some link management.

 Revision 0.7.2.10  2000/09/24 06:30:55  brian
 Committing changes.

 Revision 0.7.2.9  2000/09/21 05:19:07  brian
 Removed some leftover files.  Added changeback to mtp_sm.c.

 Revision 0.7.2.8  2000/09/20 04:35:56  brian
 Lots of work in mtp_sm.c to get changeover worked in.

 Revision 0.7.2.7  2000/09/18 09:33:44  brian
 A bunch of work in state machines.

 Revision 0.7.2.6  2000/09/13 13:47:06  brian
 A lot of work in state machine: haven't check compile yet,
 just wanting to save work for later.

 Revision 0.7.2.5  2000/09/12 18:45:22  brian
 Lots of work within the MTP state machine files.

 Revision 0.7.2.4  2000/09/12 04:57:30  brian
 Still working on state machine: still compiles.

 Revision 0.7.2.3  2000/09/11 23:41:53  brian
 Working on MTP state machine: still compiles.

 Revision 0.7.2.2  2000/09/11 20:20:48  brian
 Cleaned up routing structure.

 Revision 0.7.2.1  2000/09/11 14:53:01  brian
 State machines compile.

 Revision 0.7  2000/09/11 14:07:08  brian
 Added files for socket stuff and routing.

 Revision 0.7  2000/09/11 07:20:41  brian
 Addeds MTP state machine and mtp and sccp socket glue code.

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

static char const ident[] = "$Name: Prerelease1 $($Revision: 0.7.2.12 $) $Date: 2000/10/05 10:23:04 $";

#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/pkt_sched.h>
#include <net/sock.h>
#include "mtp_parse.h"
#include "af_ss7.h"
#include "mtp_route.h"
#include "mtp_sm.h"
#include "../../include/linux/ss7link.h"

#ifdef pvar
#undef pvar
#endif

#define pvar    ansi   /* protocol variant: other choice: itu */
#define hdrlen  1+sizeof(struct __ansi_mtph)

static int mtp_output(struct sk_buff *skb, struct ss7mtp_cb *mtp, struct ss7_routeset *rt);
/*
 *  A slightly different approach to state machine desing: the brute force
 *  approach: take each MTP message and first determine it's routing by the
 *  routing label and route it either remotely or locally.  If the message is
 *  local, tear the message down and decide whether it is a system-wide,
 *  linkset-specific, or link-specific message and pass it to the function to
 *  handle that particular message with that level's control block.
 */

static struct ss7mtp_cb deflt_addr = {
    NULL, { 0 }, 0x1, NULL,
    {  NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
       NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL  },
    NULL, NULL, 0, 0,
    0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U,
    NULL, NULL, NULL
};

struct ss7mtp_cb *ss7mtp_all = &deflt_addr;

static __inline struct sk_buff *mtp_alloc_skb(int gfp_mask)
{
    struct sk_buff *skb;
    ss7mtp_msg *m = (ss7mtp_msg *)skb->nh.raw;
    if ( (skb = alloc_skb( 4 + hdrlen + sizeof(m->pvar.msg), gfp_mask )) )
    {
        memset(skb->head, 0x0, skb->end-skb->head);
        skb_reserve(skb, 3);    /* for L2 BSN/BIB FSN/FIB LI */
        skb->nh.raw = skb->data + 1;
        skb->h.raw = skb->data - 2;
        skb->protocol = AF_SS7;
        skb->pkt_type = ETH_P_SS7;
        skb->priority = TC_PRIO_CONTROL;
    }
    return (skb);
}

/*  4.1.2   The diversion of traffic in cases of unavailability or availability or
 *  restriction of signalling links and routes is typically made my means of the following
 *  basic procedures, included in the signalling traffic management function:
 *
 *  -   signalling link unavailability (failure, deactivation, blocking or inhibiting): the
 *      changeover procedures (see clause 5) is used to divert signalling traffic to one or
 *      more alternative links (if any);
 *
 *  -   signalling link availability (restoration, activation, unblocking or uninhibiting):
 *      the changeback procedure (see clause 6) is used to divert signalling traffic to the
 *      link made available;
 *
 *  -   signalling route uavailability: the forced rerouting procedure (see clause 7) is
 *      used to divert signalling traffic to an alternative route (if any);
 *
 *  -   signalling route availability: the controlled rerouting procedure (see clause 8) is
 *      used to divert signalling traffic to the route made available;
 *
 *  -   signalling route restricted: the controlled rerouting procedure (see clause 8) is
 *      used to diver signalling traffic to an alternative route (if any);
 *
 *  -   signalling point availability: the MTP restart procedure (see clause 9) is used to
 *      divert the signalling traffic to (or via) the point made available.
 */

/*  4.3     Signalling link unavailability
 *
 *  4.3.1   When a signalling link becomes unavailable (see 3.2) signalling traffic carried
 *  by the link is transferred to one or more alternative links by means of a changeover
 *  procedure.  The alternative link or links are determined in accordance with the
 *  following criteria.
 *
 *  4.3.2   In the case when there is one or more alternative signalling links available in
 *  the link set to which the unavailable link belongs, the signalling traffic is
 *  transferred within the linkset to:
 *
 *  a)  an active and unblocked signalling link, currently not carrying any traffic.  If no
 *      such signalling link exists, the signalling traffic is transferred to:
 *
 *  b)  one or possibly more than one signalling link currently carrying traffic.  In the
 *      case of transfer to one signalling link, the alternative signalling link is that
 *      having the highest priority of the signalling links in service.
 *
 *  4.3.3   In the case where there is no alternative signalling link within the link set
 *  to which the unavailable signalling link belongs, the signalling traffic is transferred
 *  to one or more alternative link sets (combined link sets) in accordance with the
 *  alternative routing detailed for each destination.  For a particular destination, the
 *  alternative link set (combined link set) is the link set (combined link set) in service
 *  having the highest priority.
 *
 *  Within a new link set, signalling traffic is distributed over the signalling links in
 *  accordance with the routing currently applicable for that links set; i.e., the
 *  transferred traffic is routed the same way as the traffic already using the link set.
 */

/*  4.4     Signalling link availability
 *
 *  4.4.1   When a previously unavailable signalling link becomes available (see 3.2),
 *  signalling traffic may be transferred to the available signalling link by means of the
 *  changeback procedure.  The traffic to be transferred is determined in accordance with
 *  the following criteria.
 *
 *  4.4.2   In the case when the link set, to whcih the available signalling link belongs,
 *  already carries signalling traffic on other signalling links in the link set, the
 *  traffic to be transferred includes the traffic for which the available signalling link
 *  is the normal one.  Note that the assignment of the normal traffic to a signalling link
 *  may be changed during the changeback process taking into account, for example, system
 *  performance.
 *
 *  The normal traffic is transferred from on or more signalling links, depending on the
 *  criteria applied when the signalling link became unavailable (see 4.3.2), and upon the
 *  criteria applied if any of the alternative siginalling link(s) themselves became
 *  unavailable, or available, in the meantime.
 *
 *  If signalling links in the linkset are still unavailable, and if it is required for
 *  load balancing purposes, signalling traffic extra to that normally carried by any link
 *  might also be identified for diversion to the signalling link made available, and to
 *  other available signalling links in the linkset.
 *
 *  The extra traffic is transferred from one or more signalling links.
 *
 *  4.4.3   In the case when the link set (combined link set) to which the available
 *  signalling links belong, does not carry any signalling traffic [i.e. a link set
 *  (combined link set) has become available], the traffic to be transferred is the traffic
 *  for which the avilable link set (combined link set) has higher priority than the linkk
 *  set (combined link set) currently used.
 *
 *  The traffic is transferred from one or more link sets (combined link sets) and from one
 *  or more signalling links within each link set.
 *
 *  4.5     Signalling route unavailability
 *
 *  When a signalling route becomes unavailable (see 3.4), signalling traffic currently
 *  carried by the unavailable route is transferred to an alternative route by means of
 *  forced re-routing procedure.  The alternative route (i.e. the alternative link set or
 *  link sets) is determined in accordance with the alternative routing defined for the
 *  concerned destination (see.4.3.3).
 *
 *  4.6     Signalling route availability
 *
 *  When a previously unavailable signalling route becomes available again (see 3.4),
 *  signalling traffic may be transferred to the available route by means of a controlled
 *  rerouting procedure.  This is applicable in the case when the available route (link
 *  set) has higher priority than the route (link set) currently used for traffic for the
 *  concerned destination (see 4.4.3).
 *
 *  The transferred traffic is distributed over the links of the new link set in accordance
 *  with the routing currently applicable for that link set.
 *
 *  4.7     Signalling route restriction
 *
 *  When a signalling route becomes restricted (see 3.4), signalling traffic carried by the
 *  restricted route is, if possible, transferred to an alternative route by means of the
 *  controlled rerouting procedure, if an equal priority alternative is available and not
 *  restricted.  The alternative route is determined in accordance with alternate routing
 *  detailed for the concerned destination (see 4.3.3).
 *
 *  4.8     Signalling point avialability
 *
 *  When a previous unavailable signalling point becomes available (see 3.6), signalling
 *  traffic may be transferred to the available point my means of the MTP restart procedure
 *  (see clause 9).
 */

/*
 *  Tell all bound mtp users of point code availability change.
 */
static __inline void mtp_resume(struct ss7mtp_cb *mtp, struct ss7_routeset *rs)
{
    struct sock *sk;
    int i;

    (void)rs;
    for ( i = 0 ; i < 16 ; i++ )
        if ( (sk = mtp->sks[i]) ) /* found bound user */
            ; /* FIXME: make me work! */
}

/*
 *  Tell all bound mtp users of point code availability change.
 */
static __inline void mtp_pause(struct ss7mtp_cb *mtp, struct ss7_routeset *rs)
{
    struct sock *sk;
    int i;

    (void)rs;
    for ( i = 0 ; i < 16 ; i++ )
        if ( (sk = mtp->sks[i]) ) /* found bound user */
            ; /* FIXME: make me work! */
}

/*
 *  Tell all bound mtp users of congestion status change.
 */
static __inline void mtp_status(struct ss7mtp_cb *mtp, struct ss7_routeset *rs)
{
    struct sock *sk;
    int i;

    (void)rs;
    for ( i = 0 ; i < 16 ; i++ )
        if ( (sk = mtp->sks[i]) ) /* found bound user */
            ; /* FIXME: make me work! */
}

/*
 *  Timeouts:
 */

static __inline void link_output(struct sk_buff *skb, struct ss7_link *link);
static __inline void link_out_of_service(struct ss7_link *link);
static void mtp_t2t_timeout(unsigned long data);
/*
 *  The remote MTP failed to respond to an SLTM.  Only give
 *  it two tries and then fail the link.
 */
static void mtp_t1t_timeout(unsigned long data)
{
    struct ss7_link *link = (struct ss7_link *)data;

    del_timer(&link->t2t);
    if ( link->flags & SS7_LINK_RETEST )
    {
        struct sk_buff *skb;
        if ( (skb = mtp_alloc_skb(GFP_KERNEL)) ) {
            *(skb->data) = SS7_L2_STOP;
            skb->priority = TC_PRIO_CONTROL;
            __skb_trim(skb, 1);
            link_output(skb, link);
        }
        link_out_of_service(link);
    } else {
        link->flags |= SS7_LINK_RETEST;
        mtp_t2t_timeout(data);
    }
}

/*
 *  Time to send another SLTM on the link.
 */
static __inline void send_sltm(struct ss7_link *link);

static void mtp_t2t_timeout(unsigned long data)
{
    struct ss7_link *link = (struct ss7_link *)data;

    if ( !(link->flags & SS7_LINK_DONTUSE) )
        send_sltm(link);
}

static __inline void changeover_divert(struct ss7_link *link);
static __inline void changeback_divert(struct ss7_link *link);

/*
 *  Completion of time-controlled changeover procedure on expiry of timer T1.
 *  Ignore buffer updating and retrieval (purge the retrieval buffer) and
 *  perform immediate changeover diversion, in accordance with Q.704(07/96)
 *  5.6.2.
 */
static void mtp_t1_timeout(unsigned long data)
{
    struct ss7_link *link = (struct ss7_link *)data;
    if ( link->cob_state != SS7_LK_COB_TIME_CONTROLLED ) return;
    skb_queue_purge(&link->rtrvb);
    changeover_divert(link);
}

/*
 *  Completion of normal changeover procedure in the event that an
 *  acknowledgement is not received, ignore buffer updating and retrieval
 *  (purge the retrieval buffer) and perform immediate changeover diversion,
 *  in accordance with Q.704 (07/96) 5.7.2.
 */
static void mtp_t2_timeout(unsigned long data)
{
    struct ss7_link *link = (struct ss7_link *)data;
    if ( link->cob_state != SS7_LK_COB_WAITING_ACK ) return;
    skb_queue_purge(&link->rtrvb);
    changeover_divert(link);
}

/*
 *  Completion of time-controlled changeback procedure on expiry of timer T3.
 *  Perform immediate changeback diversion, in accordance with Q.704 (07/96)
 *  6.4.2.
 */
static void mtp_t3_timeout(unsigned long data)
{
    struct ss7_link *link = (struct ss7_link *)data;
    if ( link->cob_state != SS7_LK_COB_TIME_CONTROLLED ) return;
    changeback_divert(link);
}

/*
 *  T4 Timeout waiting for COO/ECO ack.  Send a second time and wait T5 per
 *  Q.704 (07/96) 6.5.3.
 */
static __inline ss7_link *alt_link(struct ss7_link *link);
static void mtp_t5_timeout(unsigned long data);
static void mtp_t4_timeout(unsigned long data)
{
    struct ss7_link *link = (struct ss7_link *)data;
    struct ss7mtp_cb *mtp = link->linkset->local->cb;
    if ( link->cob_state != SS7_LK_COB_WAITING_ACK ) return;

    del_timer(&link->t);
    link->t.data = (unsigned long)link;
    link->t.function = &mtp_t5_timeout;
    link->t.expires = jiffies + mtp->t5_value;
    add_timer(&link->t);

    if ( link->skb ) {
        struct ss7_link *alt = alt_link(link);
        if ( alt ) link_output(link->skb, link->alt);
        else       kfree_skb(link->skb);
        link->skb = NULL;
    }
}

/*
 *  T5 timeout waiting second time for COO/ECO ack.  Inform maintenance and
 *  divert traffic to recovered link immediately, per Q.704 (07/96) 6.5.3.
 */
static void mtp_t5_timeout(unsigned long data)
{
    struct ss7_link *link = (struct ss7_link *)data;
    if ( link->cob_state != SS7_LK_COB_WAITING_ACK ) return;
    changeback_divert(link);
}

/*
 *  Completion of controlled re-routing procedure.  Restart traffic on
 *  alternative route per Q.704 (07/86) 8.2.1 (c).
 */
static void mtp_t6_timeout(unsigned long data)
{
    struct ss7_route *rt = (struct ss7_route *)data;
    struct sk_buff *skb;

    rt-> flags &= ~SS7_RT_CONTROLLEDRR;
    while ( skb_queue_len(&rt->ccrb) ) {
        skb = skb_dequeue(&rt->ccrb);
        mtp_output(skb, rt->linkset->local->cb, rt->routeset);
    }
}

/*
 *  TFP wait standoff timer.  Do not send responsive TFP during TFP wait
 *  period as per Q.704 (07/96) 13.2.2.
 */
static void mtp_t8_timeout(unsigned long data)
{
    struct ss7_routeset *rs = (struct ss7_routeset *)data;

    rs-> flags &= ~( SS7_RS_TFPWAIT );
}

/*
 *  Resend signalling-route-set-test message to prohibited or restricted
 *  destinations every T10 as per Q.704 (07/96) 13.5.2 and 13.5.3.
 */
static void mtp_t10_timeout(unsigned long data)
{
    struct ss7_route *rt = (struct ss7_route *)data;
    struct sk_buff *skb;
    struct ss7mtp_cb *mtp;

    if ( !rt->skb ) return;
    if ( !rt-> flags & SS7_RT_RSTEST ) /* test cancelled */
        goto t10_cut_and_run;

    if ( !( skb = skb_clone(rt->skb, GFP_KERNEL) ))
        goto t10_cut_and_run;

    if ( (mtp = rt->routeset->cb) )
    {
        mtp->output(skb, mtp, NULL);

        del_timer(&rt->t);    /* go around again */
        rt-> t.data = (unsigned long)rt;
        rt-> t.function = &mtp_t10_timeout;
        rt-> t.expires = jiffies + mtp->t10_value;
        add_timer(&rt->t);    /* go around again */
        return;
    }
t10_cut_and_run:
    if ( rt->skb ) kfree_skb( rt->skb );
    rt->skb = NULL;
    return;
}

/*
 *  Perform T15 timeout procedure for signalling-route-set- congestion-test as
 *  per Q.704 (07/96) 13.7.4, 13.7.5 and 13.9.4 (i).
 */
static void mtp_t16_timeout(unsigned long data);
static void mtp_t15_timeout(unsigned long data)
{
    struct ss7_routeset *rs = (struct ss7_routeset *)data;
    struct ss7mtp_cb *mtp = rs-> cb;

    if ( !rs->skb ) return;

    if ( !( rs-> flags & SS7_RS_CONGESTED ) || rs-> flags & SS7_RS_DONTUSE )
        goto t15_free_and_go;

    rs-> t.data = (unsigned long)rs;
    rs-> t.function = &mtp_t16_timeout;
    rs-> t.expires = jiffies + mtp->t16_value;

    mtp_t16_timeout(rs->t.data);  /* trigger t16 */
    return;

    /* message is prebuilt */
    
t15_free_and_go:
    if ( rs->skb ) kfree_skb( rs->skb );
    rs->skb = NULL;
    return;
}

/*
 *  Perform T16 timeout procedure for signaling-route-set- congestion-test as
 *  per Q.704 (07/96) 13.9.3, and 13.9.4 (ii).
 */
static void mtp_t16_timeout(unsigned long data)
{
    struct ss7_routeset *rs = (struct ss7_routeset *)data;
    struct sk_buff *skb0, *skb = rs-> skb;
    struct ss7mtp_cb *mtp = rs-> cb;
    ss7mtp_msg *m;

    if ( !skb ) return;
    if (( !rs-> flags & SS7_RS_CONGESTED )||( rs-> flags & SS7_RS_DONTUSE ))
        goto t16_free_and_go;

    m = (ss7mtp_msg *)skb->nh.raw;

    if ( rs-> cong_status > m-> pvar.mh.mp ) { /* message got thru at last mp */
         rs-> cong_status = m-> pvar.mh.mp;
         mtp_status(mtp,rs);  /* inform users */
    }

    if ( !( rs-> cong_status ) ) {  /* we're done */
        rs-> flags &= ~SS7_RS_CONGESTED;
        goto t16_free_and_go;
    }

    m-> pvar.mh.mp =  rs-> cong_status - 1; /* test next lower */

    if ( (skb0 = skb_clone(skb, GFP_KERNEL)) )
        mtp_output( skb0, mtp, rs );

    del_timer(&rs->t);
    rs-> t.data = (unsigned long)rs;
    rs-> t.function = &mtp_t16_timeout;
    rs-> t.expires = jiffies + mtp->t16_value;
    add_timer(&rs->t);    /* go around again */

    return;

t16_free_and_go:
    if ( rs->skb ) kfree_skb( rs->skb );
    rs->skb = NULL;
    return;
}

/*
 *  Perform T17 timeout procedure for signalling link, attempt to restore link
 *  again per Q.704 (07/96) 12.2.2.
 */
static void mtp_t17_timeout(unsigned long data)
{
    struct ss7_link *link = (struct ss7_link *)data;
    struct sk_buff *skb;
    if (!link-> flags & SS7_LINK_OUT_OF_SERVICE) return;
    if ( (skb = mtp_alloc_skb(GFP_KERNEL)) ) {
        /*
         *  Attempt restoration again per Q.704 (07/96) 12.2.2.
         */
        skb->data[0] = SS7_L2_START;
        skb->priority = TC_PRIO_CONTROL;
        __skb_trim(skb,1);
        link_output(skb, link);
    }
    else
    {
        /*
         *  Not much to do without buffer except restart T17 timer and attempt
         *  restoration on next timeout.
         */
        struct ss7mtp_cb *mtp = link->linkset->local->cb;

        del_timer(&link->t);
        link->t.data = data;
        link->t.function = &mtp_t17_timeout;
        link->t.expires = jiffies + mtp->t17_value;
        add_timer(&link->t);
    }
    return;
}

static void mtp_t18_timeout(unsigned long data)
{
    (void)data;
    (void)&mtp_t18_timeout;
}

static void mtp_t19_timeout(unsigned long data)
{
    (void)data;
    (void)&mtp_t19_timeout;
}

static void mtp_t20_timeout(unsigned long data)
{
    (void)data;
    (void)&mtp_t20_timeout;
}

/*
 *  Timer for adjacent MTP restart.  This timer is cancelled
 *  by the receipt of a TRA from adjacent signalling point.
 *  Upon expiry perform route allowed procedure for all
 *  routes which use linkset directly attached to adjacent
 *  signalling point.  Start time-controlled changeback
 *  procedure for all available links in the direct linkset
 *  to the restarting MTP.
 */
static void mtp_t21_timeout(unsigned long data)
{
    struct ss7_routeset *adj = (struct ss7_routeset *)data;

    (void)&mtp_t21_timeout;
    del_timer(&adj->tmr);
    if ( !(adj-> flags & SS7_RS_MTP_RESTART ) ) return;
    adj-> flags &= ~SS7_RS_MTP_RESTART;

    /* FIXME: for each link in each linkset terminating on
     * the restarting MTP which was restored during the MTP
     * Restart procedure, start time-controlled changeback.
     * This will broadcast TFA, TFP, and TFR as required.
     */
}

/*
 *  As a basis of the restart procedure it is assumed that most of the
 *  signalling points within the network are accessible.  Thus, at the
 *  beginning of the restart procedure, all concerned routes are considered to
 *  be allowed, and the update of the network status is performed by the
 *  exchange of transfer-prohibited (TFP) and/or transfer-restricted (TFR)
 *  messages.
 *
 *  The MTP restart procedure uses the Traffic Restart Allowed (TRA) message
 *  which contains:
 *
 *  -   the lable, indicating the originating singalling point and the
 *      adjacent destination signalling point
 *
 *  -   the traffic restart allowed signal
 *
 *  The format and coding of this message appear in clause 15.
 *
 *  When the adjacent node has finished sending all relevant TFP and/or TFR
 *  messages to the node with the restarting MTP, it finalling sends a TFA
 *  message which indicates that all relevant routing information has been
 *  transferred.  Thus, at the node with the restarting MTP, the number of
 *  received TRA messages is an indication of the completeness of the routing
 *  data.
 *
 *  When the restarting MTP has completed all actions or when the overall
 *  restart time is over, it sends TRA messages directly to all of its
 *  adjacent nodes acessible via a direct link set.  These messages indicate
 *  that the restart procedure is terminated and User traffic should be
 *  started.
 *
 *  9.2     Actions in a signalling point whose MTP is restarting
 *
 *  9.2.1   A signalling point starts the MTP restart procedure when its first
 *  link is in service at level 2.  The restarting MTP:
 *
 *  -   if it has the transfer function starts a timer T18;
 *
 *  -   starts overall restart timer T20; and,
 *
 *  -   continues activating or unblocking all of its signalling links by
 *      means of the basic signalling link management procedures (see 12.2)
 *
 *  NOTE - In order to use the overall restart time in an efficient way, it is
 *  preferrable to make all link sets available at nearly the same time, by
 *  activating first one link per link set, and by applying emergency
 *  alignment for at least the first link in each link set.  Because of this
 *  measure, the routing data update can be started for all rroutes at the
 *  very beginning of the restart procedure.
 *
 *  9.2.2   If the signalling point's restarting MTP has the transfer
 *  function, the MTP restart procedure consists of two phases.  Within the
 *  first phase, supervised by timer T18, links are activated and the routing
 *  tables within the restarting MTP are updated according to the transfer-
 *  prohibited, transfer-allowed and transfer-restricted messages (see clause
 *  15) received from the adjacent nodes.  in addition, the restarting MTP
 *  takes into account any traffic restart allowed messages received from
 *  adjacent routes.  Timer T18 is implementation and network dependent, and
 *  is stopped when:
 *
 *  1)  sufficient links and link sets are available to carry the expected
 *      signalling traffice; and,
 *
 *  2)  enough TRA messages (and therefore routing data) has been received to
 *      give a high level of confidence in the MTP routing tables.
 *
 *  NOTE - In normal circumstances the restarting MTP should wait fro TRA
 *  messages from all adjacent nodes.  There are, however, other situations
 *  where this might not be useful, e.g. for a long-term equipment failure.
 *
 *  When T18 stops or expires, the second phase begins, which includes as a
 *  major part a broadcast fo non-preventive transfer prohibited messages
 *  [i.e. those TFPs according to 13.2.2 v)] and transfer-restricted messages,
 *  taking into account signalling link sets which are not available and any
 *  TFP, TFA and TFR messages received during phase 1.  Note that timer T18 is
 *  determined such that during phase 2 the broadcast of TFP and TFR messages
 *  may be completed in normal situations.
 *
 *  TRA messages received during phase 2 should be ignored.  If during phase 2
 *  a destination has been declared to be inaccessible by sending of a TFP
 *  message, and afterwards, but still within phase 2, this destination
 *  becomes accessible to the restarting MTP by reception of a TFA or TFR
 *  message or the availability of a corresponding link , this new
 *  accessibility is a late event and should be treated outside the restart
 *  procedure.  The handling of new accessibility of the said destination
 *  before the sending of a TFP referring to that destination is an
 *  implementation dependent matter.
 *
 *  When all TFP and TFR messages have been sent, the overall restart timer
 *  T20 is stopped and phase 2 is finished.  Note that preventative TFP
 *  messages [i.e. those according to 13.2.2 i)], except possibly those for
 *  highest priority routes, must have been sent before normal User traffic is
 *  carried.  This migh be done during or after phase 2.
 *
 *  9.2.3   If the restarting MTP has no transfer function, phase 1 (see
 *  9.2.2) but not phase 2 is present.  In this case, the whole restart time
 *  is available for phase 1.  The overall restart timer T20 is stopped when:
 *
 *  1)  sufficient links and link sets are avialable to carry the expected
 *      signalling traffic; and,
 *
 *  2)  enough TRA messages (and therefore routing data) have been received to
 *      give a high level of confidence in the MTP routing tables.
 *
 *  9.2.4   When T20 is stopped or expires, the restarting MTP of the
 *  signalling point or signalling transfer point sends traffic restart
 *  allowed message to all adjacent signalling points via corresponding
 *  available direct link sets, and an indication of the end of the MTP
 *  restart is sent to all local MTP Users showing each signalling point's
 *  accessibility or inaccessibility.  The means of doing the later is
 *  implementation dependent.
 *
 *  In addition, timer T19 is started (see 9.5.2) for all signalling points to
 *  which a TRA message has just been sent.  Normal operation is then resumed.
 *
 *  When T20 expires the transmission of TFP and TFR messages is stopped.
 *  However, preventive TFP messages [i.e. those according to 13.2.2 i)]
 *  except possibly those for highest priority routes, must have been sent
 *  before MTP User traffic is restarted.
 *
 *  9.3     Actions in a signalling point X, adjacent to a signalling point Y
 *          whose MTP restarts
 *
 *  9.3.1   A signalling point X considers that the MTP of an inaccessible
 *  signalling point Y is restarting when:
 *
 *  -   the first link in a direct link set is in the "in service" state at
 *      level 2; or
 *
 *  -   another route becomes available due either to reception of a
 *      corresponding TFA, TFR or TRA message, or by the corresponding link
 *      set becoming available (see 3.6.2.2).
 *
 *  9.3.2   When the first link in a direct link set towards signalling point
 *  Y, whose MTP is restarting, is in the "in service" state a level 2,
 *  signalling point X restarts timer T21 and takes account of any TFP, TFA
 *  and TFR messages received from signalling point Y.  In addition X takes
 *  the following action:
 *
 *  -   if X has the transfer function, when the direct link set is available
 *      at level 3, X sends any necessary TFP and TFR messages to Y; then
 *
 *  -   X sends a traffic restart allowed message to signalling point Y.
 *
 *  If a signalling point, previously declared to be inaccessible, becomes
 *  available again before T21 is stopped or expires, a corresponding TFA or
 *  TFR message is sent to the signalling point Y whose MTP is restarting.
 *
 *  If a signalling point becomes prohibited or restricted to signalling point
 *  X after a TRA message has been sent by X to Y, X sends a corresponding TFP
 *  or TFR message to Y.
 *
 *  When a traffic restart allowed message has been received by X from
 *  signalling point Y, and a TRA message has been sent by X to Y, X stops
 *  timer T21.
 *
 *  Note that preventive TFP messages according to 13.2.2 i) must be sent
 *  before MTP User traffic is restarted.
 *
 *  NOTE - This includes the case where the MTP of Y is restarting as well as
 *  the case that both X and Y start the adjacent signalling point MTP restart
 *  procedure at the new availability of the incoming direct link set.  In the
 *  latter case, one side will received a TRA message from the other while
 *  still sending TFP and/or TFR messages, so that is has not yet sent its TRA
 *  messages.  The transmission of routing information should be completed
 *  before this TRA message is sent to the adjacent node and timer T21
 *  stopped.
 *
 *  When T21 is stopped or expires, signalling point X sends an MTP-RESUME
 *  primitive concerning Y, and all siginialling points made available via Y,
 *  to all local MTP Users.  If X has the transfer function, it broadcasts to
 *  adjacent signalling points transfer-allowed and/or transfer restricted
 *  messages concerning Y and all signalling points made accessible via Y.
 *
 *  Note that preventive TFPs according to 13.2.2 i) must be sent before MTP
 *  User traffic is restarted.
 *
 *  In the abnormal case where transfer prohibited and transfer restricted
 *  messages are still being sent to Y when T21 expires (and hence no TRA
 *  message has yet been sent to Y), such routing data transmission is stopped
 *  and no TRA message is sent to Y.  Note that preventive TFPs according to
 *  13.2.2 i) must still be sent during the changeback procedure.
 *
 *  9.3.3   When signalling point Y becomes accessible by means other than via
 *  a direct link set between X and Y, X sends an MTP-RESUME primitive
 *  concerning Y to all local MTP Users.  In addition, if signalling point X
 *  has the transfer function, X sends to Y any required transfer-prohibited
 *  and transfer-restricted emssages on the available route.  X then
 *  broadcasts TFA and/or TFR messages (see clause 13) concerning Y.  Note
 *  that X should not in this case alter any routing data other than that for
 *  Y.
 *
 *  9.4.    Short term isolations
 *
 *  9.4.1   In the case where a signalling point is isolated due to a short
 *  term processor outage [lasting less than T1 (see  16.8)] occuring on some
 *  or all of its links at nearly the same time, the restart procedure should
 *  not be started.
 *
 *  If an isolation lasts longer that T1, the restart procedure must be
 *  performed.
 *
 *  9.4.2   When a destination Y becomes inacessible, and routing control
 *  finds an inhibited link within the route set to Y, a signalling routing
 *  control initiated uninhibiting action is performed (see 10.3).  If at
 *  least one inhibited link is in the level 2 "in service" state, and
 *  uninhibiting is successful, the isolation will be of short term and no
 *  restart procedure should be performed on either side of the link.
 *
 *  9.5.    TRA message and timer T19
 *
 *  9.5.1   If a signalling point X receives an unexpected TRA message from an
 *  adjacent node Y and no associated T19 timer is running, X sends to Y any
 *  necessary TFP and TFR messages if X has the transfer function, and a TRA
 *  message to Y.  In addition, X starts a timer T19 associated with Y.
 *
 *  9.5.2   If a signalling point receives a TRA message from an adjacent node
 *  and an associated timer T19 is running, this TRA is discarded and no
 *  further action is necessary.
 *
 *  9.6     General rules
 *
 *  9.6.1   When the MTP of a signalling point restarts, it considers at the
 *  beginning of the MTP restart procedure all signalling routes to be
 *  allowed.
 *
 *  9.6.2   After the MTP of an adjacent node X has restarted, and if T21 has
 *  been started (see 9.3.2), all routes using X are considered to be
 *  available unless corresponding TFP or TFR messages have been received
 *  whilst T21 was running.
 *
 *  9.6.3   A signalling route set test message received in a restarting MTP
 *  is ignored during the MTP restart procedure.
 *
 *  Signalling route set test messages received in a signalling point adjacent
 *  to signalling point Y whose MTP is restarting before T21 expires are
 *  handled, but the relies assume that all signalling routes using Y are
 *  prohibited.
 *
 *  9.6.4   Late events, i.e. link restorations or reception of TFA or TFR
 *  messages, occuring in phase 2 at a node whose MTP is restarting after the
 *  node has send out TFPs or TFRs referring to the concerned signalling
 *  points, are treated outside the restart procedure as normal events.
 *
 *  Handling of late events in phase 2 before sending out TFPs or TFRs
 *  referring to the concerned signalling points is an implementation
 *  dependent matter.  In addition, it is an implementation dependent matter
 *  whether the reception of TFPs or link set failures during phase 2 are
 *  handled within or after the terminaiton of the restart procedure.
 *
 *  9.6.5   When an adjacent signalling point Y becomes accessible on receipt
 *  of a TFA, TFR or TRA message (see 3.6.2), the concerned signalling point
 *  peforms controlled rerouting towards Y.
 *
 *  9.6.6   All messages to another destination received at a signalling point
 *  whose MTP is restarting are discarded.
 *
 *  All messages received during the restart procedure concerning a local MTP
 *  User (servvice indication != 0000 and != 0001) are discarded.
 *
 *  All messages received with service indicator = 0000 in a restarting MTP
 *  for the signalling point itself are treated as described in the MTP
 *  restart procedure.  Those  messages not described elsewhere in the
 *  procedure are discarded and no further action is taken on them (message
 *  groups CHM, ECM, FCM, RSM, UFC, MIM and DLM).
 *
 *  9.6.7   In adjacent signalling points during the restart procedure,
 *  messages not part of the restart procedure but which are destined to or
 *  through the signalling point whose MTP is restarting, are discarded.
 *
 *  Messages received with service indicator = 0001 are handled normally
 *  during the restart procedure.
 *
 *  9.6.8   If a gateway node's MTPs are restarting in multiple networks, it
 *  may be of advantage to coordinate their restarting procedrues
 *  (implementation dependent).
 *
 *  9.7.1   SP with restarting MTP according to subclause 9.2
 *
 *      All links unavailable (e.g., LPO)
 *      time > T1
 *      First link in service at level 2.
 *      if (mtp->transfer) {
 *          start T18
 *      }
 *      start T20
 *      Accept TFP, TFR, TRA messages.
 *      Accept TFA or TFR negating previous TFP.
 *      Sufficient links available.
 *      Accept TFP, TFR, TRA messages.
 *      Accept TFA or TFR negating previous TFP.
 *      Enough TRA messages received or T18 empty.
 *      if (mtp->transfer)
 *          while (T20 still running) {
 *              TFP, TFR broadcast.
 *              Accept late availability of link, TFP, TFR or TFA received.
 *              Last TFP, TFR or T20 expiry, then break.
 *          }
 *      else
 *          while (T20 still running) {
 *              Accept late availability of link, TFP, TFR or TFA received.
 *              Enough TRAs received or T20 expiry, then break.
 *          }
 *      TRA broadcast over direct link sets
 *      Set T19 for each TRA broadcast
 *      MTP-RESUME indications to MTP Users.
 *      if (mtp->transfer)
 *          Send TFAs, TFPs, TFRs broadcast due to late events.
 *
 *
 *  9.7.2   Actions in SP X adjacent to SPY whose MTP restarts according to
 *          subclause 9.3
 *
 *      All links to Y unavailable (e.g. LPO)
 *      time > T1
 *      First link in direct link set to Y in service at level 2
 *      Start T21
 *      if (mtp->transfer) {
 *          When first link in direct link set is available at level 3, send
 *          TFP and TFR messages to Y.
 *      }
 *      Send TRA to Y.
 *      if (mtp->transfer) {
 *          while T21 still running, send TFA, TFP, TFR messages to Y because
 *          of recent events.
 *      }
 *      Accept TFP, TFR messages from Y.
 *      Receive TRA from Y or T21 expiry.
 *      send MTP-RESUME indications wrt. Y and all SPs now accessible due to Y
 *          to MTP Users.
 *      if (mtp->transfer) {
 *          broadcast TFA, TFR referring to Y and all SPs acessible via Y.
 *      }
 */

/*
 *  Link Output Function.
 *
 *  Sends buffer to link using link's output method (if any).  This function
 *  consumes the sk_buff.
 */
static __inline void link_output(struct sk_buff *skb, struct ss7_link *link)
{
    skb-> dev = link-> dev;
    if ( link->output ) link->output(skb); else dev_queue_xmit(skb);
}

/*
 *  Message Output Function.
 *
 *  This function is only invoked by the socket interface (and internal
 *  management sending management messages) and is used for sending messages
 *  outside the box.  The transfer function is separated because it responds
 *  on problems to the originator: this function responds to the local MTP.
 */
static int mtp_output(struct sk_buff *skb, struct ss7mtp_cb *mtp, struct ss7_routeset *rt)
{
    ss7mtp_msg *m = (ss7mtp_msg *)skb->nh.raw;
    struct ss7_addr dest = { m->pvar.mh.rl.dpc };
    unsigned mp = m->pvar.mh.mp;
    unsigned sls = m->pvar.mh.rl.sls;
    struct ss7_route *route;
    struct ss7_link *link;

    if ( !(rt) && !(rt = ss7_rt(dest)) ) { kfree_skb(skb); return -EHOSTUNREACH; }
    if ( rt-> type & SS7_RST_LOCAL     ) { kfree_skb(skb); return -ENODEV;       }
    if ( rt->flags & SS7_RS_DONTUSE    ) { kfree_skb(skb); return -EHOSTDOWN;    }
    if ( rt-> type & SS7_RST_INTERNAL  ) { rt->cb->input(rt->cb, skb); return 0; }

/*  13.7.2  A tranfser-controlled message relating to a given destination X is sent from a
 *  signalling transfer point Y in response to a received message originating from
 *  signalling point Z destined to signalling point X when the congestion priority of the
 *  concerned message is less than the current congestion status of the signalling link
 *  selected to transmit the concerned message from Y to X.
 *
 *  In this case, the transfer-controlled message is sent to the originating point Z with
 *  the congestion status filed set to the current congestion status of the signalling
 *  link.
 */
    route = ss7_select_rt( rt );

    if ( route-> flags & SS7_RT_CONTROLLEDRR ) {
        skb_queue_tail(&route->ccrb, skb);
        return(0);
    }
    link = ss7_select_link(route, sls);

    if ( link-> flags & ( SS7_LINK_CHANGEOVER | SS7_LINK_CHANGEBACK ) ) {
        skb_queue_tail(&link->cocbb, skb);
        return(0);
    }
    if ( link-> flags & SS7_LINK_CONGESTED && mp < link->cong_status )
        { kfree_skb(skb); return -EBUSY; } /* remind user */

    link_output(skb,link);
    return (0);
}

static __inline struct ss7_link *find_link(__u32 adjcaddr, __u8 slc)
{
    struct ss7_addr adjc =  { adjcaddr };
    struct ss7_routeset *adj;
    struct ss7_route *rt;
    struct ss7_linkset *ls;
    struct ss7_link *lc;

    if ( !(adj = ss7_rt(adjc))  ) return NULL; /* stray message */
    if ( !(rt = adj->routes)    ) return NULL; /* broken datastructure */
    if ( !(ls = rt->linkset)    ) return NULL; /* broken datastructure */
    if ( !(lc = ls->links[slc]) ) return NULL; /* non-existent link */
    return (lc);
}

/*
 *  Perform the actions and procedure necessary upon receipt of a changeover
 *  order (or emergency changeover order) in accordance with Q.704 (07/96) 5.4
 *  and 5.6.
 */
static __inline void send_changeover_ack(struct sk_buff *skb, struct ss7_link *link)
{
    struct ss7mtp_cb *mtp = link->linkset->local->cb;
    struct sk_buff *skb0 = NULL;
    ss7mtp_msg *m = (ss7mtp_msg *)skb->nh.raw;
    unsigned slc = m->pvar.mh.rl.sls;

    /*
     *  If we are already performing a changeover, either switch to an ack
     *  mode and perform BSNT retrieval or send an ECA if we have already sent
     *  an COO/ECO ourselves.  Roughly per Q.704 (07/96) 5.4.1 and 5.6.2.
     */
    if ( link->flags & ( SS7_LINK_CHANGEOVER | SS7_LINK_DONTUSE ) )
    {
        /*
         *  If we are performing a time-controlled changeover procedure,
         *  switch to the normal changeover procedure on reception of a
         *  changeover order in accordance with ``advantageous'' procedure in
         *  Q.704 (07/96) 5.6.2.
         */
        if ( link->cob_state == SS7_LK_COB_TIME_CONTROLLED )
            if ( link->t.next ) {
                del_timer(&link->t);
                goto send_coa;
            }
        /*
         *  If we are performing a normal or emergency changeover and have
         *  already issued the changover order, respond with an emergency
         *  changeover.  If we are performing BSNT retrieval, switch to the
         *  ack mode from the order mode and await the BSNT retrieval.  Not
         *  detailed in Q.704 but Q.704 (07/96) 5.4.1 and 5.6.2 state that we
         *  must ``always'' return an acknowledgement to an order to permit
         *  the remote end to complete.
         */
        if ( link->cob_state == SS7_LK_COB_BSNT_REQ_COO ) {
            link->cob_state = SS7_LK_COB_BSNT_REQ_COA;
            return;
        }
        if ( skb0 || (skb0 = skb_clone(skb, GFP_KERNEL)) ) {
            m = (ss7mtp_msg *)skb0->nh.raw;

            m->pvar.mh.si = 0x0; /* snmm */
            m->pvar.mh.ni = mtp->ni;
            m->pvar.mh.mp = 0x3;
            m->pvar.mh.rl.dpc = link->linkset->adjacent->addr;
            m->pvar.mh.rl.opc = link->linkset->local->addr;
            m->pvar.mh.rl.sls = slc;
            m->pvar.mh.h0 = 0x2; /* ecm */
            m->pvar.mh.h1 = 0x2; /* eca */

            *(skb0->data) = SS7_L2_PDU;
            skb0->priority = TC_PRIO_BESTEFFORT;
            __skb_trim(skb0, hdrlen + sizeof(m->pvar.msg.eca));

            mtp_output(skb0, mtp, NULL); /* send back eca */
        }
        return;
    }
    /*
     *  Peform normal buffer updating and retrieval in accordance with Q.704
     *  (07/96) 5.4.1 and 5.4.3.
     */
send_coa:
    link->flags |= SS7_LINK_CHANGEOVER;
    link->loc_bsnt = 0;
    skb_queue_purge(&link->rtrvb);
    skb_queue_purge(&link->cocbb);

    if ( skb0 || (skb0 = skb_clone(skb, GFP_KERNEL)) ) {
        m = (ss7mtp_msg *)skb0->nh.raw;

        m->pvar.mh.si = 0x0; /* snmm */
        m->pvar.mh.ni = mtp->ni;
        m->pvar.mh.mp = 0x3;
        m->pvar.mh.rl.dpc = link->linkset->adjacent->addr;
        m->pvar.mh.rl.opc = link->linkset->local->addr;
        m->pvar.mh.rl.sls = slc;
        m->pvar.mh.h0 = 0x1; /* chm */
        m->pvar.mh.h1 = 0x2; /* coa */
        m->pvar.msg.coa.fsnl = 0x0; /* for now */

        *(skb0->data) = SS7_L2_PDU;
        skb0->priority = TC_PRIO_BESTEFFORT;
        __skb_trim(skb0, hdrlen + sizeof(m->pvar.msg.coa));

        link->skb = skb;
        link->alt = NULL;

        link->cob_state = SS7_LK_COB_BSNT_REQ_COA;

        *(skb0->data) = SS7_L2_RETRIEVE_BSNT;
        skb0->priority = TC_PRIO_CONTROL;
        __skb_trim(skb, 1);

        link_output(skb0, link);
    }
}

static __inline void changeover_retrieval(struct sk_buff *skb, struct ss7_link* link)
{
    struct sk_buff *skb0 = skb_clone(skb, GFP_ATOMIC);

    if ( link->rem_bsnt && skb0 )
    {
        link->cob_state = SS7_LK_COB_RETRIEVING;
        skb0->data[0] = SS7_L2_RETRIEVAL_REQUEST_AND_FSNC;
        skb0->priority = TC_PRIO_CONTROL;
        skb0->data[1] = link->rem_bsnt & 0x7f;
        __skb_trim(skb0, 2);
        link_output(skb0, link);
    }
    else
    {
        skb_queue_purge(&link->rtrvb);
        changeover_divert(link);
    }
}





/*  13.5.4  At the reception of a signaling-route-set-test message, a signalling transfer
 *  point will compare the status of the destination in the received message with the
 *  actual status of the destination.  If they are the same, no further action is taken.
 *  If they are different, one of the following message is sent in repsonse, dictated by
 *  the actual status of the destination.
 *
 *  -   a transfer-allowed message, referring to the destination the accessibility of which
 *      is tested, if the signalling transfer point can reach the indicated destination via
 *      a signalling link not connected to the signalling point from which the signalling-
 *      route-set-test message was received (and if the transfer restricted procedure is
 *      used in the network, the signalling link is on the normal route or an equally
 *      effecient alternate route);
 *
 *  -   a transfer-restricted message when access to the destination is possible via an
 *      alternative to the normal routing which is less efficient, but still not via the
 *      signalling point from which the signalling-route-set-test was originated.
 *
 *  -   a transfer-prohibited message in all other cases (including inaccessibility of that
 *      destination).
 */
static void send_rte_status(struct sk_buff *skb, struct ss7mtp_cb *mtp, struct ss7_route *rt)
{
    struct ss7_routeset *rs;
    struct sk_buff *skb0;
    struct ss7_route *r;
    ss7mtp_msg *m;
    __u8 h1 = 0x1;

    if ( !(skb0 = skb_clone(skb, GFP_KERNEL)) ) return;

    rs = rt->routeset;

    if ( rs-> flags & SS7_RS_PROHIBITED ) { h1 = 0x1; /* tfp */ } else
    if ( rs-> flags & SS7_RS_RESTRICTED ) { h1 = 0x3; /* tfr */ } else
    if ( rs-> flags & SS7_RS_ALLOWED    ) { h1 = 0x5; /* tfa */ }
    /*
     *  Spec (Q.704) says that if the only accessible route to the destination
     *  is the source of the routeset test message then we are to sent a TFP
     *  back.
     */
    for ( r= rs-> routes; r; r = r-> rset )
        if (( r != rt ) && !( r-> flags & SS7_RT_DONTUSE ))
            break;
    if ( !r ) h1 = 0x1; /* tfp */

    if ( rs-> type & ( SS7_RST_CLUSTER | SS7_RST_NETWORK ) ) h1++; /* cluster version */

    m = (ss7mtp_msg *)skb0->nh.raw;

    m->pvar.mh.si = 0x0; /* snmm */
    m->pvar.mh.ni = mtp->ni;
    m->pvar.mh.mp = 0x3;
    m->pvar.mh.rl.dpc = rt->linkset->adjacent->addr;
    m->pvar.mh.rl.opc = rt->linkset->local->addr;
    m->pvar.mh.rl.sls = 0x0;
    m->pvar.mh.h0 = 0x4; /* tfm */
    m->pvar.mh.h1 = h1;
    m->pvar.msg.tfp.dest = rs->addr;

    *(skb0->data) = SS7_L2_PDU;
    skb0->priority = TC_PRIO_BESTEFFORT;
    __skb_trim(skb0, hdrlen + sizeof(m->pvar.msg.tfp));

    mtp->output(skb0, mtp, NULL);
    return;
}

static void stop_sig_rs_test(struct ss7mtp_cb *mtp, struct ss7_route *rt)
{
    rt->flags &= ~SS7_RT_RSTEST;
    del_timer(&rt->t);
    if ( rt->skb ) { kfree_skb( rt->skb ); rt->skb = NULL; }
}

/*  13.5.2  A signalling-route-set-test message is sent from a signalling point after
 *  transfer-prohibited or transfer-restricted message is received from an adjacent
 *  signalling transfer point (see 13.2.4 and 13.4.4).  In this case, a signalling-route-
 *  set-test message is sent to that signalling transfer point referring to the destination
 *  declared inaccessible or restricted by the transfer-prohibited or transfer-restricted
 *  message, every T10 period (see clause 16) until a transfer-allowed message indicating
 *  that the destination has become accessible is received.
 *
 *  The procedure is used to recover the signalling route availability information that may
 *  not have been received because of some signalling network failure.
 *
 *  13.5.3  A signalling-route-set-test message is sent to the adjacent signalling transfer
 *  point as an ordinary signalling network management message.
 */
static void sig_rte_set_test(struct ss7mtp_cb *mtp, struct ss7_route *rt, __u8 h1)
{
    struct ss7_routeset *rs;
    struct sk_buff *skb;
    ss7mtp_msg *m;

    stop_sig_rs_test(mtp, rt);

    rt->flags |= SS7_RT_RSTEST;

    if ( !(skb = mtp_alloc_skb(GFP_KERNEL)) ) return;

    rs = rt->routeset;
    if ( rs-> type & ( SS7_RST_NETWORK | SS7_RST_CLUSTER ) ) h1 += 2;

    m = (ss7mtp_msg *)skb->nh.raw;

    m->pvar.mh.si = 0x0; /* snmm */
    m->pvar.mh.ni = mtp->ni;
    m->pvar.mh.mp = 0x3;
    m->pvar.mh.rl.dpc = rt->linkset->adjacent->addr;
    m->pvar.mh.rl.opc = rt->linkset->local->addr;
    m->pvar.mh.rl.sls = 0x0;
    m->pvar.mh.h0 = 0x5; /* rsm */
    m->pvar.mh.h1 = h1; /* rsp, rsr, rcp, rcr */
    m->pvar.msg.rsp.dest = rs->addr;

    *(skb->data) = SS7_L2_PDU;
    skb->priority = TC_PRIO_BESTEFFORT;
    __skb_trim(skb, hdrlen + sizeof(m->pvar.msg.rsp));

    rt->skb = skb;

    rt-> t.data = (unsigned long)rt;
    rt-> t.function = &mtp_t10_timeout;
    rt-> t.expires = jiffies + mtp->t10_value;
    add_timer(&rt->t);
    return;
}

/*  13.9    Signalling-route-set-congestion-test (National Option)
 *
 *  13.9.1  The signalling-route-set-congestion-test procedure is used at an originating
 *  signalling point to update the congestion status associated with a route set towards a
 *  certain destination.  The purpose is to test whether or not signalling messages
 *  destined towards that destination with a given congestion priority or higher may be
 *  sent.
 *
 *  In the case of a processor restart the congestion status of all signalling route sets
 *  will be initialized to the zero value.  The response mechanism within the transfer-
 *  controlled procedure will correct signalling route sets whose congestion status does
 *  not have the zero value.
 *
 *  ...
 *
 *  13.9.2  The signalling-route-set-congestion-test message differs from other signalling
 *  network management messages in that it is not assigned the highest congestion priority.
 *  Instead, the congestion priority assigned to the signalling-route-set-congestion-test
 *  message to be sent to a given destination is equal to one less that the current
 *  congestion status associated with the signalling route set towards the destination.
 *
 *  13.9.3  If within T16 (see clause 16), after sending a signalling-route-set-congestion-
 *  test message, a transfer-controlled message relating to the concerned destination is
 *  received, the signalling point updates the congestion status of the signalling route
 *  set towards the concerned destination with the value of the congestion status carried
 *  in the transfer-controlled message.  Following this, the procedure specified in 13.9.4
 *  and 13.9.5 are performed.
 *
 *  If T16 (see clause 16) expires after sending a signalling-route-set-congestion-test
 *  message without a transfer-controlled message relating to the concerned destination
 *  having been received, the signalling point changes the congestion status associated
 *  with the signalling route set towards the concerned destination to the next lower
 *  status.
 *
 *  13.9.4  Provided that the signalling route set towards destination X is not in the
 *  "unavailable" state, a signalling-route-set-congestion-test message is sent from an
 *  originating signalling point to destination X in the following cases:
 *
 *  i)  When T15 (see clause 16) expires after the last update of the congestion status of
 *      the signalling route set toward destination X by a transfer-controlled message
 *      relating to the same destination.
 *
 *  ii) When T16 (see clause 16) expires after sending a signalling-route-set-congestion-
 *  test message to destination X without a transfer-controlled message relating to the
 *  same destination having been received.  After the congestion status has been
 *  decremented by one, the test is repeated, unless the congestion status is zero.
 *
 *  13.9.5  At the reception of a signalling-route-set-congestion-test message, a
 *  signalling transfer point will route it as an ordinary message, i.e. according to the
 *  procedure specified in 2.3.5.
 *
 *  13.9.6  When a signalling-route-set-congestion-test message reaches its destination, it
 *  is discarded.
 */
static void sig_rte_set_cong_test(struct sk_buff *skb, struct ss7mtp_cb *mtp, struct ss7_route *rt)
{
    struct ss7_routeset *rs;
    struct sk_buff *skb0;
    ss7mtp_msg *m;

    rs = rt->routeset;

    del_timer(&rs->t);
    if ( rs->skb ) { kfree_skb( rs->skb ); rs->skb = NULL; }

    rs-> flags |= SS7_RS_CONGESTED;

    if ( !(skb0 = skb_clone(skb, GFP_KERNEL)) )
        { if (rs->skb) goto sig_rte_cong_timer; else return; }

    m->pvar.mh.si = 0x0; /* snm */
    m->pvar.mh.ni = mtp->ni;
    m->pvar.mh.mp = 0x3; /* initially */
    m->pvar.mh.rl.dpc = rs->addr;
    m->pvar.mh.rl.opc = mtp->saddr.s_addr;
    m->pvar.mh.rl.sls = 0x0;
    m->pvar.mh.h0 = 0x3; /* fcm */
    m->pvar.mh.h1 = 0x1; /* rct */

    *(skb->data) = SS7_L2_PDU;
    skb->priority = TC_PRIO_BESTEFFORT;
    __skb_trim(skb, hdrlen + sizeof(m->pvar.msg.rct));

    rs->skb = skb0;

sig_rte_cong_timer:
    rs-> cb = mtp;
    rs-> t.data = (unsigned long)rs;
    rs-> t.function = &mtp_t15_timeout;
    rs-> t.expires = jiffies + mtp->t15_value;
    add_timer(&rs->t);
    return;
}

/*
 *  8.2.    Controlled rerouting initiation and actions
 *
 *  8.2.1   Controlled rerouting is initiated at a signalling point when a transfer-allowed
 *  message, indicating that the signalling route has become available, is received; also
 *  when a transfer-restricted message is received.
 *
 *  The following actions are then performed:
 *
 *  a)  transmission of signalling traffic towards the concerned destination on the link
 *      set belonging to the alternative route or the route over which the transfer-
 *      restricted message was received is stopped; such traffic is stored in a "controlled
 *      rerouting buffer"; a timer T6 (see 16.8), is started;
 *
 *  b)  if the signalling point serves as a signalling transfer point, a transfer-
 *      prohibited procedure is performed for the route made available (or the alternate
 *      route in the case of the reception of a transfer-restricted message, if the
 *      alternative route was not previously used), and a transfer-allowed procedure for
 *      the alternative one (or on the restricted route in the case of the reception of a
 *      transfer-restricted message)(see 13.2.2 and 13.3.2, respectively);
 *
 *  c)  at the expiry of T6, the concerned signalling traffic is restarted on an outgoing
 *      link set pertaining to the signalling route made available, or the alternative
 *      route in the case of reception of the transfer-restricted message, starting with
 *      the content of the controlled rerouting buffer; the aim of the time delay is to
 *      minimize the prabability of out-of-sequence deliver to the destination point(s).
 *
 *  8.2.2   In the case when there is not signalling traffic to be diverted fromt the route
 *  made available, only action b) in 8.2.1 applies.
 *
 *  8.2.3   If the destination was inaccessible or restricted, when the route is made
 *  available, then the destination is declared accessible and actions specified in 6.2.3
 *  and 6.2.4 apply (if appropriate).
 */

/*  6.2.3   In the case that the signalling link made available can be used to carry
 *  signalling traffic towards a non-adjacent destination which was previously declared
 *  inaccessible, the following actions apply:
 *
 *  i)  the routing of the concerned signalling traffic is unblocked and transmission of
 *      the concerned messages (if any) is started on the link made available;
 *
 *  ii) an indication is sent to the User Part(s)(if any) to restart the concerned
 *      signalling traffic;
 *
 * iii) the transfer-allowed procedure is performed, as specified in 13.3.  However, in
 *      national networks, when the recovered link is not on the normal route for that
 *      destination, the transfer-restricted procedure may be performed as specified in
 *      13.4.
 *
 *  iv) the transfer-prohibited procedure is performed as specified in 13.2.2 i).
 *  
 */

/*
 *  Build a txm ( tfp,tcp,tfr,tcr,tfa,tca ) message in a sk_buff copy with no
 *  dpc.
 */
static __inline struct sk_buff *build_tfm(struct ss7mtp_cb *mtp, struct ss7_routeset *rs, __u32 daddr, __u8 h1)
{
    struct sk_buff *skb;
    ss7mtp_msg *m;
    if ( rs-> type & ( SS7_RST_NETWORK | SS7_RST_CLUSTER ) ) h1++; /* cluster form */
    if ( !(skb = mtp_alloc_skb(GFP_KERNEL)) ) return NULL;
    m = (ss7mtp_msg *)skb-> nh.raw;
    m-> pvar.mh.si = 0x0; /* stm msg */
    m-> pvar.mh.ni = mtp->ni;
    m-> pvar.mh.mp = 0x3;
    m-> pvar.mh.rl.dpc = daddr;
    m-> pvar.mh.rl.opc = mtp->saddr.s_addr; /* from us */
    m-> pvar.mh.rl.sls = 0x0;  /* send TFA/TFP on fixed SLS */
    m-> pvar.mh.h0 = 0x4; /* tfm */
    m-> pvar.mh.h1 = h1; /* tfx or tcx */
    m-> pvar.msg.tfp.dest = rs->addr;
    *(skb->data) = SS7_L2_PDU;
    skb->priority = TC_PRIO_BESTEFFORT;
    __skb_trim(skb, hdrlen + sizeof(m->pvar.msg.tfa));
    return skb;
}
        
/*
 *  Broadcast to all adjacent destination point codes except point codes which
 *  are adjacent on a route in the set which matches `flags'.  This function
 *  consumes the skb provided.
 */
static __inline void broadcast(struct sk_buff *skb, struct ss7mtp_cb *mtp, struct ss7_routeset *rs, __u8 flags)
{
    struct ss7_linkset *ls;
    struct ss7_route *r;
    struct sk_buff *skb0;
    ss7mtp_msg *m = (ss7mtp_msg *)skb->nh.raw;
    for ( ls = ss7_all_linksets ; ls ; ls = ls-> next ) {
        struct ss7_routeset *ra = ls-> adjacent;
        if ( ra-> flags & SS7_RS_DONTUSE ) continue;
        /* dont send to inaccessible adjacent */
        for ( r = rs-> routes ; r ; r = r-> next )
            if ( r-> linkset == ls && r-> flags & flags )
                goto tfa_next_routeset;
        /* dont send already informed adjacent */
        m-> pvar.mh.rl.dpc = ra->addr;  /* send to adjacent */
        if ( (skb0 = skb_clone(skb, GFP_ATOMIC)) )
            mtp_output(skb0, mtp, ra);
tfa_next_routeset:
    }
    kfree_skb(skb);
}

static void broadcast_tfa(struct ss7mtp_cb *mtp, struct ss7_routeset *rs)
{
    struct sk_buff *skb;
    if ( rs-> flags & SS7_RS_PROHIBITED ) mtp_resume(mtp,rs);
    rs-> flags |= SS7_RS_ALLOWED;
    rs-> flags &= ~( SS7_RS_PROHIBITED | SS7_RS_RESTRICTED );
    if ( (skb = build_tfm(mtp, rs, 0U, 0x5)) ) /* tfa */
        broadcast(skb, mtp, rs, SS7_RS_ALLOWED);
}

static void broadcast_tfr(struct ss7mtp_cb *mtp, struct ss7_routeset *rs)
{
    struct sk_buff *skb;
    if ( rs-> flags & SS7_RS_PROHIBITED ) mtp_resume(mtp,rs);
    rs-> flags |= SS7_RS_RESTRICTED;
    rs-> flags &= ~( SS7_RS_PROHIBITED | SS7_RS_ALLOWED );
    if ( (skb = build_tfm(mtp, rs, 0U, 0x3)) ) /* tfr */
        broadcast(skb, mtp, rs, ( SS7_RS_ALLOWED | SS7_RS_RESTRICTED ));
}

static void broadcast_tfp(struct ss7mtp_cb *mtp, struct ss7_routeset *rs)
{
    struct sk_buff *skb;
    if ( !(rs-> flags & SS7_RS_PROHIBITED) ) mtp_pause(mtp,rs);
    rs-> flags |= SS7_RS_PROHIBITED;
    rs-> flags &= ~( SS7_RS_RESTRICTED | SS7_RS_ALLOWED );
    if ( (skb = build_tfm(mtp, rs, 0U, 0x1)) ) /* tfp */
        broadcast(skb, mtp, rs, SS7_RS_PROHIBITED);
}

/*
 *  Find an alternate usable route
 */
static __inline ss7_route *alt_route(struct ss7_route *rt)
{
    struct ss7_routeset *rs = rt->routeset;
    struct ss7_route *ru;
    for ( ru = rs-> routes ; ru ; ru = ru-> next )
        if ( ( ru != rt ) && !( ru-> flags & SS7_RT_DONTUSE ) )
            break;
    return ( ru );
}

/*  6.2.3   In the case that the signalling link made available can be used to carry
 *  signalling traffic towards a non-adjacent destination which was previously declared
 *  inacessible, the following actions apply:
 *
 *  i)  the routing of the concerned signalling traffic is unblocked and transmission of
 *      the concerned messages (if any) is started on the link made available;
 *  
 *  ii) an indication is sent to the User Part(s) (if any) to restart the concerned
 *      signalling traffic;
 *
 *  iii)the transfer-allowed  procedure is performed, as specified in 13.3.  However, in
 *      national networks, when the recovered link is not on the normal route for that
 *      destination, the transfer-restricted procedure may be performed as specified in
 *      13.4;
 *
 *  iv) the transfer-prohibited procedure is performed as specified in 13.2.2 i).
 */
static __inline void route_allow(struct ss7mtp_cb *mtp, struct ss7_route *ra)
{
    struct sk_buff *skb;
    struct ss7_routeset *rs;
    struct ss7_route *ru;

    if ( ra-> flags & SS7_RT_ALLOWED ) return;  /* already allowed */

    ra-> flags |= SS7_RT_ALLOWED;
    ra-> flags &= ~( SS7_RT_PROHIBITED | SS7_RT_RESTRICTED | SS7_RT_CONGESTED );

    rs = ra->routeset;
    ru = alt_route(ra); /* another usable route? */

    if ( ru ) {
        if ( ru-> priority < ra-> priority ) return; /* won't use ra */
        del_timer(&ra->t);
        ra-> flags |= SS7_RT_CONTROLLEDRR;
        ra-> t.data = (unsigned long)ra;
        ra-> t.function = &mtp_t6_timeout;
        ra-> t.expires = jiffies + mtp->t6_value;
        add_timer(&ra->t);
    }
    if ( ra-> priority > 1 ) /* will use ra as alternate */
        if ( (skb = build_tfm(mtp, rs, ra->linkset->adjacent->addr, 0x1)) ) /* tfp */
            mtp_output(skb, mtp, ra->linkset->adjacent);
    if ( ru ) {
        if ( ru->priority > ra-> priority || ru->flags & SS7_RT_RESTRICTED )
        {   /* won't use ru alternate */
            if ( (skb = build_tfm(mtp, rs, ru->linkset->adjacent->addr, 0x5)) ) /* tfa */
                mtp_output(skb, mtp, ru->linkset->adjacent);
        } else /* will use both load-shared */
            return;
    }
    /* will only use ra */
    if ( ra-> priority > 1 ) broadcast_tfr(mtp,rs); /* new alternate */
    else                     broadcast_tfa(mtp,rs); /* new primary */
}

/*
 *  13.4.2  A transfer-restricted message relating to a given destination X is sent from a
 *  signalling transfer point Y when the normal link set (combined link set) used by
 *  signalling point Y to route to destination X experiences long-term failure such as
 *  equipment failure, or there is congestion on an alternative link set currently being
 *  used to destination X.  In this case, a transfer-restricted message is sent to all
 *  accessible adjacent signalling points except those that receive TFP message according
 *  to 13.2.2 i), and except signalling point X if it is an adjacent point (Broadcast
 *  Method).
 *
 *  When an adjacent signalling point X become acessible, the STP Y sends to X transfer-
 *  restricted messages concerning destinations that are restricted from Y (see clause 9).
 *
 *  When a signalling point Y restarts, it broadcasts to all accessible adjacent signnlling
 *  points transfer-restricted messages concerning destinations that are restricted from Y
 *  (see clause 9).
 *
 *  13.4.3  When a signalling point receives a transfer-restricted message from signalling
 *  transfer point Y and has an alternative equal priority link set available and not
 *  restricted to destination X, it performs actions in 8.2.  In other words, it performs
 *  controlled re-routing to maintain the sequence of messages while diverting them to the
 *  alternate link set.  If it cannot perform alternate routing to destination X because no
 *  alternative link set is available, it may generate additional transfer-restricted
 *  messages.
 *
 *  13.4.4  In some circumstances, it may happen that a signalling point receives either a
 *  repeated transfer-restricted message or a transfer- restricted message relating to a
 *  non-existent route (i.e., there is no route from that signalling point to the concerned
 *  destination via signalling transfer point Y, according to the signalling network
 *  configuration); in this case, no actions are taken.
 *
 *  13.4.5  When a transfer-restricted message is received updating a transfer-prohibited
 *  status, signalling traffic management decides if an alternative route is available or
 *  restricted; if it is not (i.e., no alternative route exists), the concerned traffic is
 *  restarted towards the signalling point from which the transfer-restricted message was
 *  received.  Otherwise, no other actions are taken.
 */
static __inline void route_restrict(struct ss7mtp_cb *mtp, struct ss7_route *rr)
{
    struct sk_buff *skb;
    struct ss7_routeset *rs;
    struct ss7_route *ru;

    __u8 oldflags = rr->flags;

    if ( rr-> flags & SS7_RT_RESTRICTED ) return; /* already restricted */

    rr-> flags |= SS7_RT_RESTRICTED;
    rr-> flags &= ~( SS7_RT_PROHIBITED | SS7_RT_ALLOWED );

    rs = rr->routeset;
    ru = alt_route(rr); /* alternate usable route? */

    if ( oldflags & SS7_RT_PROHIBITED ) {
        /* route was prohibited, now it's restricted */
        rr-> flags &= ~( SS7_RT_CONGESTED );
        if ( ru ) {
            if ( ru-> priority < rr-> priority ) return; /* won't use rr */
            if ( ru-> priority == rr-> priority && ru->flags & SS7_RT_ALLOWED ) /* won't use rr */
                return;
            del_timer(&rr->t);
            rr-> flags |= SS7_RT_CONTROLLEDRR;
            rr-> t.data = (unsigned long)rr;
            rr-> t.function = &mtp_t6_timeout;
            rr-> t.expires = jiffies + mtp->t6_value;
            add_timer(&rr->t);
        }
        if ( rr-> priority > 1 ) /* will use rr as alternate */
            if ( (skb = build_tfm(mtp, rs, rr->linkset->adjacent->addr, 0x1)) ) /* tfp */
                mtp_output(skb, mtp, rr->linkset->adjacent);
        if ( ru ) {
            if ( ru->priority > rr-> priority )
            {   /* won't use ru alternate */
                if ( (skb = build_tfm(mtp, rs, ru->linkset->adjacent->addr, 0x5)) ) /* tfa */
                    mtp_output(skb, mtp, ru->linkset->adjacent);
            } else /* will use both load-shared */
                return;
        }
    } else {
        /* route was allowed, now it's restricted */
        if ( ru ) {
            if ( ru-> priority < rr-> priority ) return; /* wasn't using rr */
            if ( ru-> priority > rr-> priority || ru->flags & SS7_RT_RESTRICTED )
                return broadcast_tfr(mtp,rs); /* can't reroute */
            del_timer(&rr->t);
            rr-> flags |= SS7_RT_CONTROLLEDRR;
            rr-> t.data = (unsigned long)rr;
            rr-> t.function = &mtp_t6_timeout;
            rr-> t.expires = jiffies + mtp->t6_value;
            add_timer(&rr->t);
            return;
        }
    }
    broadcast_tfr(mtp,rs); /* can't reroute */
}

/*  7.2     Forced rerouting initiation and actions
 *
 *  7.2.1   Forced reouting is initiated at a signalling point when a transfer-prohibited
 *  message indicating a signalling route unavailability is received.
 *
 *  The following actions are then performed:
 *
 *  a)  transmission of signalling traffic towards the concerned destination on the link
 *      set(s) pertaining to the unavailable route is immediately stopped; such traffic is
 *      stored in a forced rerouting buffer;
 *
 *  b)  the alternative route is determined according to the rules specified in clause 4;
 *
 *  c)  as soon as action b) is completed, the concerned signalling traffic is restarted on
 *      a link set pertaining to the alternative route, starting with the content of the
 *      forced rerouting buffer;
 *
 *  d)  if appropriate, a transfer-prohibited procedure is performed (see 13.2.2).
 */
/*  5.3.3   If no alternative signalling link exists for signalling traffic towards on or
 *  more destinations, the concerned destination(s) are declared inaccessible and the
 *  following actions apply:
 *
 *  i)  the routing of the concerned signalling traffic is blocked and the concerned
 *      messages already stored in the transmission and retransmission buffers of the
 *      unavailable signalling link, as well as those received subsequently, are discarded;
 *
 *  ii) a command is sent to the User Part(s) (if any) in order to stop generating the
 *      concerned signalling traffic;
 *
 *  iii)the transfer-prohibited procedure is performed, as specified in 13.2;
 *
 *  iv) the appropriate signalling link management procedures are performed, as specified
 *      in clause 12.
 */

/*  13.2.3  When a signalling point receives a transfer-prohibited message from signalling
 *  transfer point Y, it peforms the actions specified in clause 7 (since reception of
 *  transfer-prohibited message indicates the unavailability fo the concerned signalling
 *  route, see 3.4.1).  In other words, it may perform forced re-routing and, if
 *  appropriate, generate additional transfer-prohibited messages.
 *
 *  13.2.4  In some circumstances it may happen that a signalling point receives either a
 *  repeated transfer-prohibited message relating to a non-existent route (i.e., there is
 *  no route from that signalling point to the concerned destination via sginalling
 *  transfer point Y, according to the signalling network configuration) or to a
 *  destination which is already inacccessible, due to previous failures, in this case no
 *  actions are taken.
 */

static __inline void route_prohibit(struct ss7mtp_cb *mtp, struct ss7_route *rp)
{
    struct ss7_routeset *rs;
    struct ss7_route *ru;

    __u8 oldflags = rp-> flags;

    if ( rp-> flags & SS7_RT_PROHIBITED ) return; /* already prohibited */

    rp-> flags |= SS7_RT_PROHIBITED;
    rp-> flags &= ~( SS7_RT_RESTRICTED | SS7_RT_ALLOWED | SS7_RT_CONGESTED );

    rs = rp->routeset;
    ru = alt_route(rp); /* alternate usable route? */

    if ( oldflags & SS7_RT_ALLOWED ) {
        /* route was allowed, now prohibited */
        if ( ru ) {
            if ( ru-> priority < rp-> priority ) return; /* wasn't using rp */
            if ( ru-> priority > rp-> priority || ru->flags & SS7_RT_RESTRICTED )
                return broadcast_tfr(mtp,rs); /* reroute to restricted route */
        }
    } else {
        /* route was restricted, now prohibited */
        if ( ru ) {
            if ( ru-> priority < rp-> priority ) return; /* wasn't using rp */
            if ( ru-> priority > rp-> priority || ru->flags & SS7_RT_RESTRICTED )
                return; /* I was already restricted */
        }
    }
    broadcast_tfp(mtp,rs); /* can't reroute */

    del_timer(&rs->t);
    rs->t.data = (unsigned long)rs;
    rs->t.function = &mtp_t8_timeout;
    rs->t.expires = jiffies + mtp->t8_value;
    add_timer(&rs->t);
    return;
}

/*
 *  Send an SLTM message on the specified link.  Set appropriate SLTM timers
 *  on the link.
 */
static __inline void send_sltm(struct ss7_link *link)
{
    struct ss7mtp_cb *mtp = link->linkset->local->cb;
    struct sk_buff *skb;
    ss7mtp_msg *m;

    del_timer(&link->t1t);
    del_timer(&link->t2t);

    if ( (skb = mtp_alloc_skb(GFP_ATOMIC)) )
    {
        m = (ss7mtp_msg *)skb->nh.raw;
        m->pvar.mh.si = 0x1; /* SNTM */
        m->pvar.mh.ni = mtp->ni;
        m->pvar.mh.mp = 0x3;
        m->pvar.mh.rl.opc = link->linkset->local->addr;
        m->pvar.mh.rl.dpc = link->linkset->adjacent->addr;
        m->pvar.mh.rl.sls = link->index;
        m->pvar.mh.h0 = 0x1; /* TM */
        m->pvar.mh.h1 = 0x1; /* SLTM */
        m->pvar.msg.sltm.slc = link->index;
        m->pvar.msg.sltm.tli = 0x0;
        /*
         *  FIXME: check to see if TLI=0 is ok here on test messages.
         */
        *(skb->data) = SS7_L2_PDU;
        skb->priority = TC_PRIO_BESTEFFORT;
        __skb_trim(skb, hdrlen + sizeof(m->pvar.msg.sltm) - 15);
        link_output(skb, link);
    }

    link->t1t.data = (unsigned long)link;
    link->t2t.data = (unsigned long)link;
    link->t1t.function = &mtp_t1t_timeout;
    link->t2t.function = &mtp_t2t_timeout;
    link->t1t.expires = jiffies + mtp->t1t_value;
    link->t2t.expires = jiffies + mtp->t2t_value;
    add_timer(&link->t1t);
    add_timer(&link->t2t);
}

static void link_up(struct ss7_link *link, __u8 reason);
/*
 *  Signalling Link Test Control messages.
 */
static void ss7mtp_sltc_msg(struct sk_buff *skb, struct ss7mtp_cb *mtp, struct ss7_routeset *rt)
{
    ss7mtp_msg *m = (ss7mtp_msg *)skb->nh.raw;
    struct ss7_link *link;    /* link we received message on */
    unsigned h0 = m->pvar.mh.h0;
    unsigned h1 = m->pvar.mh.h1;

    if (!(link = &((struct ss7_iface *)skb->dev->ss7_ptr)->link))
        return;

    switch (h0) {
        case 0x1: /* TM */
            switch (h1) {
                case 0x1: /* SLTM */ {
                    struct sk_buff *skb0;
                    int len;
                    if ( (skb0 = skb_clone(skb, GFP_ATOMIC) ) )
                    {
                        m = (ss7mtp_msg *)skb0->nh.raw;
                        m->pvar.mh.si = 0x1; /* SNTM */
                        m->pvar.mh.ni = mtp->ni;
                        m->pvar.mh.mp = 0x3;
                        m->pvar.mh.rl.opc = link->linkset->local->addr;
                        m->pvar.mh.rl.dpc = link->linkset->adjacent->addr;
                        m->pvar.mh.rl.sls = link->index;
                        m->pvar.mh.h0 = 0x1; /* TM */
                        m->pvar.mh.h1 = 0x2; /* SLTA */
                        m->pvar.msg.slta.slc = link->index;
                        len = m->pvar.msg.slta.tli;
                        *(skb0->data) = SS7_L2_PDU;
                        skb0->priority = TC_PRIO_BESTEFFORT;
                        __skb_trim(skb0, hdrlen + sizeof(m->pvar.msg.slta) - 15 + len);
                        link_output(skb0, link);
                    }
                    return;
                }
                case 0x2: /* SLTA */ {
                    /*  FIXME: we should actually check
                     *  contents of SLTA message... */
                    del_timer(&link->t1t);
                    if ( link-> flags & SS7_LINK_TEST )
                    {
                        /* First successful link test, bring
                         * link into service at L3. */
                        link-> flags &= ~(SS7_LINK_TEST|SS7_LINK_RETEST);
                        link_up(link, SS7_LINK_OUT_OF_SERVICE);
                        return;
                    }
                    link-> flags &= ~(SS7_LINK_TEST|SS7_LINK_RETEST);
                    return;
                }
            }
    }
    return;
}

/*
 *  Signalling Link Management Messages.
 */
static void ss7mtp_slm_msg(struct sk_buff *skb, struct ss7mtp_cb *mtp, struct ss7_routeset *rt)
{
}

/*
 *  Signalling Traffic Management Messages.
 */
static void ss7mtp_stm_msg(struct sk_buff *skb, struct ss7mtp_cb *mtp, struct ss7_routeset *rt)
{
    ss7mtp_msg *m = (ss7mtp_msg *)skb->nh.raw;
    struct ss7_link *lr;    /* link we received message on */
    struct ss7_link *lc;    /* concerned link */
    unsigned h0 = m->pvar.mh.h0;
    unsigned h1 = m->pvar.mh.h1;

    if (!(lr = &((struct ss7_iface *)skb->dev->ss7_ptr)->link)) return;

    switch (h0) {
        case 0x7: /* TRM */
            switch (h1) {
 /* STMM */     case 0x1: /* TRA - Traffic restart allowed signal */
                         /*
                          * This is the end of a MTP restart procedure and we are supposed
                          * to dump our TFP's and TFR's at the restarted signaling point.
                          * A restarting signaling point is one which is adjacent had has
                          * been inaccessible.
                          */
 /* STMM */     case 0x2: /* TRW - Traffic restart waiting signal */ /* ANSI */
                default: return;
            }
        case 0x1: /* CHM */
            switch (h1) {
 /* STMM */     case 0x1: /* COO - changeover order signal */
                    if ( !(lc = find_link( m->pvar.mh.rl.opc, m->pvar.mh.rl.sls )) ) return;
                    lc->rem_bsnt = m->pvar.msg.coo.fsnl | 0x80;
                    send_changeover_ack(skb,lc);
                    return;

 /* STMM */     case 0x2: /* COA - changeover acknowledgement signal */
                    if ( !(lc = find_link( m->pvar.mh.rl.opc, m->pvar.mh.rl.sls )) ) return;
                    if ( !(lc->flags & SS7_LINK_CHANGEOVER) ||
                           lc->cob_state != SS7_LK_COB_WAITING_ACK ) return;
                    lc->rem_bsnt = m->pvar.msg.coa.fsnl | 0x80;
                    changeover_retrieval(skb,lc);
                    return;

 /* STMM */     case 0x3: /* CBD - changeback declaration signal */
                    if ( !(lc = find_link( m->pvar.mh.rl.opc, m->pvar.mh.rl.sls )) ) return;
                    m->pvar.mh.rl.dpc = lc->linkset->adjacent->addr;
                    m->pvar.mh.rl.opc = lc->linkset->local->addr;
                    m->pvar.mh.h1 = 0x4; /* cba */
                    mtp_output(skb, mtp, NULL);
                    return;
 /* STMM */     case 0x4: /* CBA - changeback acknowledgement signal */
                    if ( !(lc = find_link( m->pvar.mh.rl.opc, m->pvar.mh.rl.sls )) ) return;
                    if ( !(lc->flags & SS7_LINK_CHANGEBACK) ||
                           lc->cob_state != SS7_LK_COB_WAITING_ACK ) return;
                    changeback_divert(lc);
                    return;
                default: return;
            }

        case 0x2: /* ECM */
            switch (h1) {
 /* STMM */     case 0x1: /* ECO - emergency changeover order signal */
                    if ( !(lc = find_link( m->pvar.mh.rl.opc, m->pvar.mh.rl.sls )) ) return;
                    lc->rem_bsnt = 0x00;
                    send_changeover_ack(skb,lc);
                    return;

 /* STMM */     case 0x2: /* ECA - emergency changeover ack signal */
                    if ( !(lc = find_link( m->pvar.mh.rl.opc, m->pvar.mh.rl.sls )) ) return;
                    if ( !(lc->flags & SS7_LINK_CHANGEOVER) ||
                           lc->cob_state != SS7_LK_COB_WAITING_ACK ) return;
                    lc->rem_bsnt = 0x00;
                    changeover_retrieval(skb,lc);
                    return;
                default: return;
            }
        case 0x6: /* MIM */
            switch (h1) {
 /* STMM */     case 0x1: /* LIN - link inhibit signal */
 /* STMM */     case 0x2: /* LUN - link uninhibit signal */
 /* STMM */     case 0x3: /* LIA - inhibit acknowledgement signal */
 /* STMM */     case 0x4: /* LUA - uninhibited acknowledgement signal */
 /* STMM */     case 0x5: /* LID - inhibit denied signal */
 /* STMM */     case 0x6: /* LFU - force inhibit signal */
                case 0x7: /* LLI - local inhibit test signal */
                case 0x8: /* LRI - remote inhibit test signal */
                default: return;
            }
    }
}

/*
 *  Signaling Route Management Messages.
 */
static void ss7mtp_srm_msg(struct sk_buff *skb, struct ss7mtp_cb *mtp, struct ss7_routeset *loc)
{
    struct ss7_route *r = NULL;
    ss7mtp_msg *m = (ss7mtp_msg *)skb->nh.raw;
    /*
     *  If TFA, TFR, TFP then dest/opc/dpc -> dest/adjacent/local = route This
     *  is the route for which the message applies.  If TCA, TCR, TCP, then
     *  dest is a cluster. we have local already
     */
    struct ss7_addr dest = { m->pvar.msg.tfa.dest };
    struct ss7_addr srce = { m->pvar.mh.rl.opc };
    struct ss7_routeset *adj = NULL;
    struct ss7_routeset *rem = NULL;

    unsigned h0 = m->pvar.mh.h0;
    unsigned h1 = m->pvar.mh.h1;

    if ( !(adj = ss7_rt_type(srce, 0, SS7_RST_ADJACENT)) )
        return;


    if ( h0 & 0x1 ) {
        if ( !(rem = ss7_rt_type(dest, 0, SS7_RST_REMOTE)) ) {
            rem = ss7_rs_add(dest, 0, SS7_RST_REMOTE);
            r = ss7_add_route(rem, loc, adj); } }
    else {
        if ( !(rem = ss7_rt_type(dest, 0, SS7_RST_CLUSTER)) ) {
            rem = ss7_rs_add(dest, 0,  SS7_RST_CLUSTER);
            r = ss7_add_route(rem, loc, adj); } }

    if ( !r ) return;

    switch ( h0 )
    {
        case 0x4: /* TFM */
            {
                kfree_skb(skb); 

                switch ( h1 )
                {
                    case 0x1: /* TFP - transfer prohibited signal */
                    case 0x2: /* TCP - transfer cluster prohibited signal */ /* ANSI */
                        route_prohibit(mtp, r);
                        sig_rte_set_test(mtp, r, 0x1); /* rsp */
                        return;

                    case 0x3: /* TFR - transfer restricted signal */
                    case 0x4: /* TCR - transfer cluster restricted signal */ /* ANSI */
                        route_restrict(mtp, r);
                        sig_rte_set_test(mtp, r, 0x2); /* rsr */
                        return;

                    case 0x5: /* TFA - trasnfer allowed signal */
                    case 0x6: /* TCA - transfer cluster allowed signal */ /* ANSI */
                        stop_sig_rs_test(mtp, r);
                        route_allow(mtp, r);
                        return;

                    default:
                        return;
                }
            }

        case 0x3: /* FCM */
            {
                switch ( h1 ) {
                    case 0x1: /* RCT - signaling route set congestion test msg */
                        return; /* ignore per Q.704 */

/*  13.7.3  When the originating signalling point Z receives a transfer-controlled message
 *  relating to destination X, if the current congestion status of the signalling route set
 *  towards the destination X is less than the congestion status in the transfer-controlled
 *  message, it updates the congestion status of the signalling route set towards
 *  destination X with the value of the congestion status carried in the transfer-
 *  controlled message.
 *  
 *  13.7.6  In some circumstances it may happen that a signalling point receives a
 *  transfer-controlled message relating to a destination which is already inaccessible to
 *  to previous failures, in this case the transfer- controlled message is ignored.
 */
                    case 0x2: /* TFC - transfer controlled signal */
                        if ( rem->flags & SS7_RS_DONTUSE ) return;
                        if ( m->pvar.msg.tfc.stat > rem-> cong_status ) {
                            rem-> cong_status = m->pvar.msg.tfc.stat;
                            mtp_status(mtp,rem); /* inform users */
                            sig_rte_set_cong_test(skb, mtp, r);
                        }
                        return;

                    default:
                        return;
                }
            }
        case 0x5: /* RSM */
            {
                switch ( h1 )
                {
                    case 0x1: /* RSP - sig route set test prohibited signal */
                        if ( !( rem-> flags & SS7_RS_PROHIBITED ) )
                            send_rte_status( skb, mtp, r );
                        return;

                    case 0x2: /* RSR - sig route set test restricted  signal */
                        if ( !( rem-> flags & SS7_RS_RESTRICTED ) )
                            send_rte_status( skb, mtp, r );
                        return;

                    case 0x3: /* RCP - sig route set test cluster prohibited */ /* ANSI */
                        if ( !( rem-> flags & SS7_RS_PROHIBITED &&
                                    rem-> type & ( SS7_RST_NETWORK | SS7_RST_CLUSTER ) ) )
                            send_rte_status( skb, mtp, r );
                        return;

                    case 0x4: /* RCR - sig route set test cluster restricted */ /* ANSI */
                        if ( !( rem-> flags & SS7_RS_RESTRICTED &&
                                    rem-> type & ( SS7_RST_NETWORK | SS7_RST_CLUSTER ) ) )
                            send_rte_status( skb, mtp, r );
                        return;

                    default:
                        return;
                }
            }
        case 0x9: /* UFC */
            {
                switch ( h1 ) {
                    case 0x1: /* UPU */
                    case 0x2: /* UPA */ /* ANSI */
                    case 0x3: /* UPT */ /* ANSI */
                    default:
                        return;
                }
            }
        default:
            return;
    }
    return;
}

/*
 *  Signalling Special Test Control Messages.
 */
static void ss7mtp_ssltc_msg(struct sk_buff *skb, struct ss7mtp_cb *mtp, struct ss7_routeset *rt)
{
}

/*
 *  Message Transfer Function.
 *
 *  We have an message which is not destined to an internal route and likely
 *  corresponds to an address off of the platform.  We never transfer messages
 *  for MTP parts which do not have the transfer function specified by routing
 *  or socket, plus we never transfer for MTPs which are restarting, to MTPs
 *  which are restarting, or to MTPs which are congested beyond the priority
 *  of the message.  Consumes the buffer provided.
 *
 */
static __inline void mtp_transfer(struct sk_buff *skb, struct ss7mtp_cb *mtp, struct ss7_routeset *rt)
{
    ss7mtp_msg *m = (ss7mtp_msg *)skb->nh.raw;
    struct ss7_addr dest = { m->pvar.mh.rl.dpc };
    struct ss7_addr adjc;
    unsigned mp = m->pvar.mh.mp;
    unsigned sls = m->pvar.mh.rl.sls;
    struct ss7_route *route;
    struct ss7_link *link;

    /* Do not transfer messages if we don't have the transfer function
     * or are in MTP restart per Q.704 (07/96) 9.6.6. */
    if ( mtp->restart || !mtp->transfer) goto mtp_transfer_discard;
    if ( !(rt) && !(rt = ss7_rt(dest)) ) goto mtp_transfer_discard;
    if ( rt-> type & SS7_RST_LOCAL     ) goto mtp_transfer_discard;
    if ( rt-> type & SS7_RST_INTERNAL  ) { mtp_output(skb, mtp, rt); return; }

/* iii) When a message destined to signalling point X is received at signalling transfer
 *      point Y and Y is unable[7] to transfer the message, and if no corresponding timer
 *      T8 is running.  In this case the transfer-prohibited message is sent to the
 *      adjacent signaling point from which the message concerned was received (Response
 *      Method).  In addition, timer T8 is started concerning SP X.
 *
 *  Within T8 (see clause 16) after the last transfer-prohibited message was transmitted
 *  according to ii) or iii) above no transfer prohibited message will be sent via the
 *  Response Method referring to that destination.
 *  ----------
 *  [7] ``unable'' normally means that X is inaccessible or that Y has no routing data for
 *  X.  It might be of advantage in national networks (at the discretion of the network
 *  operator) not to send TFP if no routing data for X exists at Y.  This would avoid
 *  signalling route set tests for non- existent destinations, which otherwise might
 *  destabilize the network.
 */

    if ( rt->flags & SS7_RS_DONTUSE    )
    {
        __u32 mask = SS7_RS_MASK_MEMBER;
        unsigned h1 = 0x1;

        if ( rt-> flags & SS7_RS_TFPWAIT ) goto mtp_transfer_discard;
        if ( rt-> type & SS7_RST_NETWORK ) { mask = SS7_RS_MASK_NETWORK; h1 = 0x2; }
        if ( rt-> type & SS7_RST_CLUSTER ) { mask = SS7_RS_MASK_CLUSTER; h1 = 0x2; }
            
        if ( !(adjc.s_addr = ss7_get_adjacent(skb)) ) goto mtp_transfer_discard;

        m->pvar.mh.si = 0x0; /* stmm */
        m->pvar.mh.mp = 0x3;
        m->pvar.mh.ni = mtp->ni;
        m->pvar.mh.rl.dpc = adjc.s_addr;        /* to adjacent */
        m->pvar.mh.rl.opc = mtp->saddr.s_addr;  /* from us */
        m->pvar.mh.rl.sls = 0x0;
        m->pvar.mh.h0 = 0x4; /* tfm */
        m->pvar.mh.h1 = h1;  /* tfp or tcp */
        m->pvar.msg.tfp.dest = rt->addr & mask;

        *(skb->data) = SS7_L2_PDU;
        skb->priority = TC_PRIO_BESTEFFORT;
        __skb_trim(skb, hdrlen + sizeof(m->pvar.msg.tfp));

        rt-> flags |= SS7_RS_TFPWAIT;   /* we won't send another soon */

        del_timer(&rt->t);
        rt-> t.data = (unsigned long)rt;
        rt-> t.function = &mtp_t8_timeout;
        rt-> t.expires = jiffies + mtp->t8_value;
        add_timer(&rt->t);

        mtp_output(skb, mtp, NULL); /* this one's from us */
        return;
    }

    route = ss7_select_rt( rt );

    if ( route-> flags & SS7_RT_CONTROLLEDRR ) {
        skb_queue_tail( &route->ccrb, skb );
        return; }

    link = ss7_select_link(route, sls);

    if ( link-> flags & ( SS7_LINK_CHANGEOVER | SS7_LINK_CHANGEBACK ) ) {
        skb_queue_tail( &link->cocbb, skb );
        return; }
    /*
     *  Send repsonse method TFC to originator of mess discarded under
     *  congestion as per Q.704 (07/96) 13.7.2.
     */
    if ( link-> flags & SS7_LINK_CONGESTED && mp < link->cong_status )
    {
        m->pvar.mh.si = 0x0; /* stmm */
        m->pvar.mh.mp = 0x3;
        m->pvar.mh.ni = mtp->ni;
        m->pvar.mh.rl.dpc = m->pvar.mh.rl.opc;  /* to originator */
        m->pvar.mh.rl.opc = mtp->saddr.s_addr;  /* from us */
        m->pvar.mh.rl.sls = 0x0;
        m->pvar.mh.h0 = 0x3; /* fcm */
        m->pvar.mh.h1 = 0x2; /* tfc */
        m->pvar.msg.tfp.dest = rt->addr;
        m->pvar.msg.tfc.stat = link->cong_status;

        *(skb->data) = SS7_L2_PDU;
        skb->priority = TC_PRIO_BESTEFFORT;
        __skb_trim(skb, hdrlen + sizeof(m->pvar.msg.tfc));

        mtp_output(skb, mtp, NULL); /* this one's from us */
        return;
    }

    link_output(skb,link);
    return;

mtp_transfer_discard:
    kfree_skb(skb);
    return;
}

/*
 *  User Part Message.
 *
 *  Find a socket bound to the User Part or turn around a User Part
 *  Unavailable message back to sender if no bound socket.
 *
 */
static __inline void ss7mtp_up_msg(struct sk_buff *skb, struct ss7mtp_cb *mtp, struct ss7_routeset *rt) {
    /* let's see if there is a socket bound for the user part */
    ss7mtp_msg *m = (ss7mtp_msg *)skb->nh.raw;
    struct sock *sk;
    struct ss7mtp_cb *local;

    /* check address bound socket */
    local = rt->cb;
    if (local && (sk = local->sks[m->pvar.mh.si])) {
        sk->backlog_rcv(sk, skb);  /* deliver to socket */
        return;
    }

    /* check bound any socket */
    local = ss7mtp_all; 
    if (local && (sk = local->sks[m->pvar.mh.si])) {
        sk->backlog_rcv(sk, skb);  /* deliver to socket */
        return;
    }

    /* no bound socket build UPU */
    m->pvar.mh.mp = 0x3;
    m->pvar.mh.rl.dpc = m->pvar.mh.rl.opc;  /* to originator */
    m->pvar.mh.rl.opc = mtp->saddr.s_addr;
    m->pvar.mh.h0 = 0x1; /* ufc */
    m->pvar.mh.h1 = 0xa; /* upu */
    m->pvar.msg.upu.upi = m->pvar.mh.si;
    m->pvar.msg.upu.dest = rt->addr;
    m->pvar.mh.si = 0x0; /* snmm */

    *(skb->data) = SS7_L2_PDU; /* put a name on it */
    skb->priority = TC_PRIO_BESTEFFORT;
    __skb_trim(skb, hdrlen + sizeof(m->pvar.msg.upu));

    mtp_output(skb, mtp, NULL); /* route it and deliver it */
}

/*
 *  Input Method for protocol control blocks.
 *
 *  MTP Protocol Control blocks wishing to receive messages bind this method
 *  to their control block operations.
 *
 */
void mtp_input(struct ss7mtp_cb *mtp, struct sk_buff *skb)
{
    /* here's the message with the L2 header pulled */
    /* first find out who it's for */
    ss7mtp_msg *m = (ss7mtp_msg *)skb->data;
    struct ss7_routeset *rt = NULL;
    struct ss7_addr dest;

    /* find the control block for the linkset the message came in on */

    skb->nh.raw = skb->data;
    dest.s_addr = m->pvar.mh.rl.dpc;


    if ( !(rt = ss7_rt_type(dest, 0, SS7_RST_LOCAL)) || (rt-> cb != mtp) )
        return mtp_transfer(skb, mtp, rt);
    else
    {
        /* we have a local route to the destination, i.e., the message is for
         * the address on this side of the link that it came in on
         */
        int h0, h1, si;
        si = m->pvar.mh.si;
        h0 = m->pvar.mh.h0;
        h1 = m->pvar.mh.h1;

        /*
         *  These below can be distributed during MTP restart per rule Q.704
         *  (07/96) 9.6.7.
         */
        switch (si) {
            case SS7_PROTO_SNMM: /* 0x0 */
                switch (h0) {
                    case 0x7: /* TRM */
                        switch (h1) {
                            default: goto bad_message;
    /****/   /* STMM */     case 0x1: /* TRA - Traffic restart allowed signal */
    /****/   /* STMM */     case 0x2: /* TRW - Traffic restart waiting signal */ /* ANSI */
                        }
                        return ss7mtp_stm_msg(skb, mtp, rt);
                    case 0x4: /* TFM */
                        switch (h1) {
    /****/   /* SRMM */     default: goto bad_message;
    /****/   /* SRMM */     case 0x1: /* TFP - transfer prohibited signal */
    /****/   /* SRMM */     case 0x2: /* TCP - transfer cluster prohibited signal */ /* ANSI */
    /****/   /* SRMM */     case 0x3: /* TFR - transfer restricted signal */
    /****/   /* SRMM */     case 0x4: /* TCR - transfer cluster restricted signal */ /* ANSI */
    /****/   /* SRMM */     case 0x5: /* TFA - trasnfer allowed signal */
    /****/   /* SRMM */     case 0x6: /* TCA - transfer cluster allowed signal */ /* ANSI */
                        }
                        return ss7mtp_srm_msg(skb, mtp, rt);
                }
            /*
             *  Messages received with service indicator = 0001 are handled
             *  normally during the restart procedure - Q.704 (07/96) 9.6.6.
             */
            case SS7_PROTO_SNTM: /* 0x1 */
                switch (h0) {
                    case 0x1: /* TM */
                        switch (h1) {
    /****/   /* SLTCM */    case 0x1: /* SLTM */
    /****/   /* SLTCM */    case 0x2: /* SLTA */
                            default: goto bad_message;
                        }
                        return ss7mtp_sltc_msg(skb, mtp, rt);
                    default: goto bad_message;
                }
        }
        /*
         *  These below don't distribute during an MTP restart per rule Q.704
         *  (07/96) 9.6.7.
         */
        if (mtp->restart) goto dont_distribute;

        switch (si) {
            case SS7_PROTO_SNMM: /* 0x0 */
                switch (h0) {
                    default: goto bad_message;
                    case 0x8: /* DLM */
                        switch (h1) {
                            default: goto bad_message;
             /* SLMM */     case 0x1: /* DLC - Sig-data-link-connection-order signal */
             /* SLMM */     case 0x2: /* CSS - Connection-sucessful signal */
             /* SLMM */     case 0x3: /* CNS - Connection-not-successful signal */
             /* SLMM */     case 0x4: /* CNP - Connection-not-possible signal */
                        }
                        return ss7mtp_slm_msg(skb, mtp, rt);
                    case 0x1: /* CHM */
                        switch (h1) {
                            default: goto bad_message;
             /* STMM */     case 0x1: /* COO - changeover order signal */
             /* STMM */     case 0x2: /* COA - changeover acknowledgement signal */
             /* STMM */     case 0x3: /* CBD - changeback declaration signal */
             /* STMM */     case 0x4: /* CBA - changeback acknowledgement signal */
                        }
                    case 0x2: /* ECM */
                        switch (h1) {
                            default: goto bad_message;
             /* STMM */     case 0x1: /* ECO - emergency changeover order signal */
             /* STMM */     case 0x2: /* ECA - emergency changeover ack signal */
                        }
                    case 0x6: /* MIM */
                        switch (h1) {
                            default: goto bad_message;
             /* STMM */     case 0x1: /* LIN - link inhibit signal */
             /* STMM */     case 0x2: /* LUN - link uninhibit signal */
             /* STMM */     case 0x3: /* LIA - inhibit acknowledgement signal */
             /* STMM */     case 0x4: /* LUA - uninhibited acknowledgement signal */
             /* STMM */     case 0x5: /* LID - inhibit denied signal */
             /* STMM */     case 0x6: /* LFU - force inhibit signal */
                            case 0x7: /* LLI - local inhibit test signal */
                            case 0x8: /* LRI - remote inhibit test signal */
                        }
                        return ss7mtp_stm_msg(skb, mtp, rt);
                    case 0x3: /* FCM */
                        switch (h1) {
                            default: goto bad_message;
             /* SRMM */     case 0x1: /* RCT - signaling route set congestion test msg */
             /* SRMM */     case 0x2: /* TFC - transfer controlled signal */
                        }
                    case 0x5: /* RSM */
                        switch (h1) {
                            default: goto bad_message;
             /* SRMM */     case 0x1: /* RSP - sig route set test prohibited signal */
             /* SRMM */     case 0x2: /* RSR - sig route set test restricted  signal */
             /* SRMM */     case 0x3: /* RCP - sig route set test cluster prohibited */ /* ANSI */
             /* SRMM */     case 0x4: /* RCR - sig route set test cluster restricted */ /* ANSI */
                        }
                    case 0x9: /* UFC */
                        switch (h1) {
             /* SRMM */     default: goto bad_message;
             /* SRMM */     case 0x1: /* UPU */
             /* SRMM */     case 0x2: /* UPA */ /* ANSI */
             /* SRMM */     case 0x3: /* UPT */ /* ANSI */
                        }
                        return ss7mtp_srm_msg(skb, mtp, rt);
                }
            case SS7_PROTO_SNSM: /* 0x2 */
                switch (h0) {
                    default: goto bad_message;
                    case 0x1: /* TM */
                        switch (h1) {
                            default: goto bad_message;
             /* SLTCM */    case 0x1: /* SSLTM */ /* ANSI */
             /* SLTCM */    case 0x2: /* SSLTA */ /* ANSI */
                        }
                        return ss7mtp_ssltc_msg(skb, mtp, rt);
                }
        /*
         *  For the following, look for a bound socket.  If we have a bound
         *  socket which permits the destination address (wildcard 0.0.0
         *  address permitted) for the specified user part (wildcard 0 port
         *  permitted), then we will transfer the message to that socket.  We
         *  stop with the first socket on the list.  SCCP is a little
         *  different, because it might be implemented in the kernel...
         */
            case SS7_PROTO_SCCP: /* 0x3 */
#if 0           /* for later when we do SCCP */
                if (mtp->sccp) {
                    skb_push(skb, 1);
                    skb->data[0]=SS7_SCCP_PDU;
                    ss7sccp_msg(skb, mtp, rt); /* FIXME: call SCCP control block */
                    return;
                }   /* otherwise deliver like normal user part */
#endif
            case SS7_PROTO_TUP:  /* 0x4 */
            case SS7_PROTO_ISUP: /* 0x5 */
            case SS7_PROTO_DUP1: /* 0x6 */
            case SS7_PROTO_DUP2: /* 0x7 */
            case SS7_PROTO_MTUP: /* 0x8 */
            case SS7_PROTO_TUPE: /* 0xC */
            case SS7_PROTO_TUPP: /* 0xF */
                /* note: we DON'T pull the MTP header, we push the PDU cmd and
                 * the PDU type */
                skb_push(skb, 1);
                *(skb->data) = SS7_L4_PDU;
                ss7mtp_up_msg(skb, mtp, rt);
                return;
            default:
                if (net_ratelimit()) printk(KERN_WARNING
                    "SS7/MTP: received unknown MTP user-part message on link %s\n",
                    skb->dev->name);
        }
    }

bad_message:
    /* report bad message */
dont_distribute:
    kfree_skb(skb);
    return;
}

static __inline ss7_link *alt_link(struct ss7_link *link)
{
    struct ss7_linkset *ls = link->linkset;
    struct ss7_route *r = ls->adjacent->routes;
    struct ss7_linkset *s = r->linkset;
    struct ss7_link *l = NULL;
    int i;

    for ( ; r && !l ; r = r->next, s = r->linkset )
        if ( s != ls || s->usable > 1 )
            for ( i = 0 ; i < 16 ; i++ )
                if ( ( ( l = s->links[i] ) && ( l != link ) &&
                      !( l->flags & SS7_LINK_DONTUSE ) ) || ( l = NULL ) )
                    break;
    return (l);
}

/*
 *  5.3     Changeover initiation and actions
 *
 *  5.3.1   Changeover is initiated at a signalling point when a signalling link is
 *  recognized as unavailable according to the criteria listed in 3.2.2.
 *
 *  The following actions are then performed:
 *
 *  a)  transmission and acceptance of message signal units on the concerned signalling link
 *      is terminated;
 *
 *  b)  transmission of link status signal units or fill in signal units, as described in
 *      5.3/Q.703 takes place;
 * 
 *  c)  the alternative signalling link(s) are determined according to the rules specified in
 *      clause 4;
 *
 *  d)  a procedure to update the content of the retransmission buffer of the unavailable
 *      signalling link is performed as specified in 5.4 below;
 *
 *  e)  signalling traffic is diverted to the alternative signalling link(s) as specified in
 *      5.5 below.
 *
 *  In addition, if traffic towards a given destination is diverted to an alternative
 *  signalling link terminating in a signalling tranfser point not currently used to carry
 *  traffic towards that destination, a transfer-prohibited procedure is performed as
 *  specified in 13.2.
 *
 *  5.3.2   In the case when there is no traffic to transfer from the unavailable signalling
 *  link action, only item b) of 5.3.1 is required.
 *
 *  5.3.3   If no alternative signalling link exists for signalling traffic towards one or
 *  more destinations, the concerned destination(s) are declared inaccessible and the
 *  following actions apply:
 *
 *  i)      the routing of concerned signalling traffic is blocked and the concerned messages
 *          already stored in the transmission and retransmission buffers of the unavailable
 *          signalling link, as well as those received subsequently, are discarded[3];
 *          ---------
 *          [3] The adequacy of this procedure to meet the acceptable dependability objective
 *          in terms of loss of messages requires further study.
 *
 *  ii)     a command is sent to the User Part(s) (if any) in order to stop generating the
 *          concerned signalling traffic;
 *
 *  iii)    the transfer-prohibited procedure is performed, as specified in 13.2;
 *
 *  iv)     the appropriate signalling link mangement procedures are performed, as specified
 *          in clause 12.
 *
 *  5.3.4   In some cases of failiure or in some network configurations, the normal buffer
 *  updating and retrieval procedures described in 5.4 and 5.5 cannot be accomplished.  In
 *  such cases, the emergency changeover procedures described in 5.6 apply.
 *
 *  Other procedures to cover possible abnormal cases appear in 5.7.
 *
 *  5.4     Buffer updating procedure
 *
 *  5.4.1   When a decision to changeover is made, a changeover order is sent to the remote
 *  signalling point.  In the case that the changeover was initiated by the reception of a
 *  changeover order (see 5.2), a changeover acknowledgement is sent instead.
 *
 *  A changeover order is always acknowledged by a changeover acknowledgement, even when
 *  changeover has alread been initiated in accordance with another criterion.
 *
 *  No priority is given to the changeover order or changeover acknowledgement in relation to
 *  the normal traffic of the signalling link on which messages is sent.
 *
 *  5.4.2   The changeover order and changeover acknowledgement are signalling network
 *  management messages and contain the following information:
 *
 *  -   the label, indicating the destination and originating signalling points and the
 *      identity of the unavailable signalling link;
 *
 *  -   the changeover-order (or changeover-acknowledgement) signal; and
 *
 *  -   the forward sequence number fo the last message signal unit accepted from the
 *      unavailable signalling link.
 *
 *  Formats and codes of the changeover order and the changeover acknowledgement appear in
 *  clause 15.
 *
 *  5.4.3   Upon reception of a changeover order or changeover acknowledgement, the
 *  retransmission buffer of the unavailable signalling link is updated (except as noted in
 *  5.6), according to the information contained in the message.  The message signaling units
 *  successive to that indicated by the message are those which have to be retransmitted on
 *  the alternative signalling link(s), according to the retrieval and diversion procedure.
 *
 *  5.5     Retrieval and diversion of trafic
 *
 *  When the procedure to update the retransmission buffer content is completed, the
 *  following actions are performed:
 *
 *  -   the routing of the signalling traffic to be diverted is changed;
 *
 *  -   the signal traffic already stored in the transmission buffers and retransmission
 *      buffer of the unavailable signalling link is sent directly towards the new signalling
 *      link(s), according to the modified routing.
 *
 *  The diverted signalling traffic will be sent towards the new signalling link(s) in such a
 *  way that the correct message sequence is maintained.  The diverted traffic has no
 *  priority in relation to normal traffic already conveyed on the signalling link(s).
 *
 *  5.6     Emergency changeover procedures
 *
 *  5.6.1   Due to the failure in a signalling terminal, it may be impossible for the
 *  corresponding end of the faulty signalling link to determine the forward sequence number
 *  of the last message signal unit accepted over the unavailable link.  In this case, the
 *  concerned end accomplishes, if possible, the buffer updating procedures described in 5.4
 *  but it makes use of an emergency changeover order or and emergency changeover
 *  acknowledgement instead of the corresponding normal message; these emergency messaages,
 *  the format of which appears in clause 15, do not contain the forward sequence number of
 *  the last accepted message signal unit.  Furthermore, the siginalling link is taken out of
 *  service, i.e. the concerned end initiates, if possible, the sending of out-of-service
 *  link status signal units on the unavailable link (see 5.3/Q.703).
 *
 *  When the other end of the unavailable signalling link receives the emergency changeover
 *  order or acknowledgement, it accommplishes the changeover procedures described in 5.4 and
 *  5.5, the only difference being that it does not perform either buffer updating or
 *  retrieval.  Instead, it directly starts sending the signalling traffic not yet
 *  transmitted on the unavailable link on the alternative signalling link(s).
 *
 *  The use of normal or emergency messages depends on the local conditions of the sending
 *  signalling point only, in particular:
 *
 *  -   an emergency changeover order is acknowledged by a changeover acknowledgement if the
 *      local conditions are normal; and
 *
 *  -   a changeover order is acknowledged by an emergency changeover acknowledgement if
 *      there are local fault conditions.
 *
 *  5.6.2   Time-controlled changeover is initiated when the exchange of changeover messages
 *  is not possible or not desirable, i.e. if any (or several) of the following cases apply:
 *
 *  i)      No signalling pat exists between the two ends of the unavailable link, so that
 *          the  exchange of changeover messages is impossible.
 *
 *  ii)     Processor outage indication is received on a link.  In this case, if the remote
 *          processor outage condition is only transitory, sending of a changeover order
 *          could result in failure of the link.
 *
 *  iii)    A signalling link currently carrying traffic has been marked (locally or
 *          remotely) inhibited.  In this case, time controlled changeover is used to divert
 *          traffic for the inhibited link without causing the link to fail.
 *
 *  When the concerned signalling point decides to initiate changeover in such circumstances,
 *  after the expirty of time T1 (see 16.8), it starts signalling traffic not yet transmitted
 *  on the unavailable signalling link on the alternative link(s), the purpose of
 *  withholding traffic for time T1 (see 16.8) is to reduce the probability of message
 *  mis-sequencing.
 *
 *  An example of such a case appears in Annex A/Q.705.
 *
 *  In the abnormal case when the concerned signalling point is not aware of the situation,
 *  it will start the normal changeover procedure and send a changeover order; in this case
 *  it will receive no changeover message in response and th procedure will be completed as
 *  indicated in 5.7.2.  Possible reception of a transfer-prohibited message (sent by an
 *  involved signalling transfer point on reception of the changeover order, see 13.2) will
 *  not affect changeover procedures.
 *
 *  If time-controlled changeover has been initiated according to case ii) above and if a
 *  changeover order is receoved from the remote and during time T1, it is advantageous to
 *  switch to the normal changeover procedure including retrieval because unnecessary message
 *  loss or sending of old messages is avoided in a simple way.  The ability to perform this
 *  switch is considered to be implementation dependent.  A changeover acknowledgement
 *  however, must be returned in any case in order to assure the normal completion of the
 *  changeover procedure at the remote end.  If a changeover order is received after timer T1
 *  has expried time-controlled changeover is completed (if not yet done) and an emergency
 *  changeover acknowledgement is sent to the remote end.
 *
 *  In the case that processor outage is of long-term, the remote side completes the
 *  time-controlled changeover procedure.  In order to avoid sending out old messages (see
 *  clause 8/Q.703) the level 2 buffers on both sides of the link should be flushed
 *  immediately, when the local/remote processor outage state terminates.  How the flushing
 *  is performed is implementation dependent.  The decision whether processor outage is of
 *  long-term is a local thing.  At the remote side long-term processor outage occurs when
 *  the time-controlled changeover timer T1 expires.  At the local side an equivalent timer
 *  is used in quite the same way.
 *
 *  5.6.3   Due to failures, it may be impossible for signalling point to perform retrieval
 *  even if it has received the retrieval information from the far end of the unavailable
 *  singalling link.  In this case, it starts sending new traffic on reception of the
 *  changeover message (or on time-out expiry, see 5.6.2 and 5.7.2); no further action in
 *  addition to the other normal changeover procedures are performed.
 *
 *  5.7     Procedures in abnormal conditions
 *
 *  5.7.1   The procedures described in this subclause allow the completion of the changeover
 *  procedures in abnormal cases other than those described in 5.6.
 *
 *  5.7.2   If no changeover message in response to a changeover order is received within a
 *  timer T2 (see 16.8), new traffic is started on the alternative signalling link(s).
 *
 *  5.7.3   If a changeover order or acknowledgement containing an unreasonable value of the
 *  forward sequence number is received, no buffer updating or retrieval is performed, and
 *  new traffic is started on the alternative signalling link(s).
 *
 *  5.7.4   If a changeover acknowledgement is received without having previously sent a
 *  changeover order, no action is taken.
 *
 *  5.7.5   If a changeover order is received relating to a particular singalling link after
 *  the completion of changeover from that signalling link, an emergency changeover
 *  acknowledgement is sent in response, without any further action.
 */

static __inline void changeback_abort(struct ss7_link *link);
static void link_down(struct ss7_link *link, __u8 reason)
{
    if ( link->flags & SS7_LINK_DONTUSE ) {
        link->flags |= reason;
        return;
    }

    if ( link->flags & SS7_LINK_CHANGEBACK ) {
        changeback_abort(link);
    }

    link->flags |= SS7_LINK_CHANGEOVER;
    link->flags |= reason;
    
    /* tell link to send FISU/SIOS per Q.704 (07/96) 5.3.1(b) */

    /*
     *  If there was no traffic on the signalling link gone unavailable,
     *  perform the changeover diversion procedure immediately (as per Q.704
     *  (07/96) 5.3.2.
     */
    {
        struct ss7_route *r;

        for ( r = link->linkset-> routes ; r ; r = r-> lset )
            if ( !( r-> flags & SS7_RT_DONTUSE ) )
                break;
        if (!r) return changeover_divert(link);
    }

    {
        struct ss7_link *alt;

        if ( (alt = alt_link(link)) ) {

            struct ss7mtp_cb *mtp = link->linkset->local->cb;
            struct sk_buff *skb0, *skb = mtp_alloc_skb(GFP_KERNEL);
            ss7mtp_msg *m = (ss7mtp_msg *)skb->nh.raw;
            struct ss7_addr adjc = { link->linkset->adjacent->addr };

            link->skb = skb;

            /*
             *  If we can't communicate with the adjacent node (no buffers, no
             *  route) perform the time-controlled procedure.  Per Q.704
             *  (07/96) 5.6.2 and 5.6.2 i).
             */
            if ( !skb || !ss7_rt(adjc) )
            {
                link->cob_state = SS7_LK_COB_TIME_CONTROLLED;
                del_timer(&link->t);
                link->t.data = (unsigned long)link;
                link->t.function = &mtp_t1_timeout;
                link->t.expires = jiffies + mtp->t1_value;
                add_timer(&link->t);
                return;
            }
            /*
             *  If we have a route to the adjacent signalling point, it might
             *  go away before we get around to sending the COO/ECO.  That's
             *  acceptable per Q.704 (07/96) 5.6.2 ``abnormal case''.
             */
            else { }
            /*
             *  Prepare a message for COO or COA to be used after buffer
             *  updating in the procedures per Q.704 (07/96) 5.4.1.
             */
            m->pvar.mh.si = 0x0; /* snmm */
            m->pvar.mh.ni = mtp->ni;
            m->pvar.mh.mp = 0x3;
            m->pvar.mh.rl.dpc = link->linkset->adjacent->addr;
            m->pvar.mh.rl.opc = link->linkset->local->addr;
            m->pvar.mh.rl.sls = 0x0; /* can be same sls as failed link */
            m->pvar.mh.h0 = 0x1; /* chm */ /* 0x2 for ecm */
            m->pvar.mh.h1 = 0x1; /* coo */ /* 0x1 for eco */

            *(skb->data) = SS7_L2_PDU;
            skb->priority = TC_PRIO_BESTEFFORT;
            __skb_trim(skb, hdrlen + sizeof(m->pvar.msg.coo));
            /*
             *  If we can't get a buffer to ask for BSNT, do an ECO per Q.704
             *  (07/96) 5.3.4.
             */
            if ( !(skb0 = skb_clone(skb, GFP_KERNEL)) ) {
                /* can't ask for BSNT, do ECO */
                m->pvar.mh.h0 = 0x2; /* ecm */
                link_output(skb, alt);

                link->cob_state = SS7_LK_COB_WAITING_ACK;

                del_timer(&link->t);
                link->t.data = (unsigned long)link;
                link->t.function = &mtp_t2_timeout;
                link->t.expires = jiffies + mtp->t2_value;
                add_timer(&link->t);
                return;
            }
            /*
             *  Perform the buffer updating procedure per Q.704 (07/96) 5.4.
             */
            link->skb = skb;
            link->alt = alt;

            skb0->data[0] = SS7_L2_RETRIEVE_BSNT;
            skb0->priority = TC_PRIO_CONTROL;
            __skb_trim(skb, 1);
            link_output(skb, link);

            return; /* wait for SS7_L3_BSNT (buffer update) */
        }
    }
    /*
     *  No alternative signalling link exists for traffic on the failed
     *  signalling link.  Perform procedure per Q.704 (07/96) 5.3.3.
     */
    skb_queue_purge(&link->cocbb);
    skb_queue_purge(&link->rtrvb);

    changeover_divert(link);

/*  5.3.3 ...
 *
 *  iv) the appropriate signalling link management procedures are performed, as specified
 *      in clause 12.
 */


/*
 *  12.2.2  Signalling link restoration
 *
 *  After a signialling link failure is detected, signialling link initial
 *  alignment will take place.  In the case wehn the initial alignment
 *  procedure is successful, a signalling link test is started.  If the
 *  signalling link test is successful the link becomes restored and thus
 *  available for signalling.
 *
 *  If initial alignment is not possible, as determined at Message Transfer
 *  Part level 2 (see clause 7/Q.703, new initial alignment procedures may be
 *  started on the same signalling link after a timer T17 until the signalling
 *  link is restored or a manual intervention is made, e.g. to replace the
 *  signalling data link or the signalling terminal.
 *
 *  If the signalling link set test fails, the restoration procedure is
 *  repeated until the link is restored or a manual intervention made.
 */

    /* FIXME: do some procedures from clause 12. */

    return;
}


/*
 *  Perform diversion of signal traffic from a failed link according to
 *  changeover procedures in Q.704 (07/96) 5.3.1, 5.3.3, and 5.5, and (if
 *  necessary) clause 6.
 */
static __inline void changeover_divert(struct ss7_link *link)
{
    struct ss7mtp_cb *mtp;
    struct sk_buff *skb0 = NULL;
    struct ss7_routeset *adj;
    struct ss7_route *r;
    int i, len;

    link->cob_state = SS7_LK_COB_IDLE;
    /*
     *  If the link recovered during changeover, perform the procedures
     *  indicated per Q.704 (07/96) Figure 30/Q.704 (sheet 5 of 6).
     */
    if ( link->flags & SS7_LINK_CHANGEBACK )
        return link_up(link, link->reason);

    del_timer(&link->t);
    link->flags    &= ~(SS7_LINK_CHANGEOVER);
    link->alt       = NULL;
    link->loc_bsnt  = 0x00;
    link->rem_bsnt  = 0x00;
    if (link->skb) { kfree_skb(link->skb); link->skb = NULL; }

    mtp = link->linkset->local->cb;
    adj = link->linkset->adjacent;

    /*
     *  If required, perform the procedures associated with route
     *  unavailability per Q.704 (07/96) 5.3.3.
     */
    if ( !(link->linkset->usable--) )
        for ( r = link->linkset-> routes ; r ; r = r-> lset )
            route_prohibit(mtp, r);

    if ( (skb0 = mtp_alloc_skb(GFP_KERNEL)) ) {
        *(skb0->data) = SS7_L2_CLEAR_BUFFERS;
        skb0->priority = TC_PRIO_CONTROL;
        __skb_trim(skb0, 1);
        link_output(skb0, link);
    }
    /*
     *  Perform diversion procedures per Q.704 (07/96) 5.5.
     */
    while ( (skb0 = skb_dequeue(&link->rtrvb)) ) {
        mtp_output(skb0, mtp, adj);
    }
    for ( i = 0, len = skb_queue_len(&link->cocbb) ; i < len ; i++ )
        if ( (skb0 = skb_dequeue(&link->cocbb)) ) {
            mtp_output(skb0, mtp, adj); /* may requeue */
        }

}
/*
 *  Abort changeover on a link following procedures indicated in Q.704 (07/96)
 *  Figure 30/Q.704 (sheet 3 of 6).
 */
static __inline void changeover_abort(struct ss7_link *link)
{
    struct sk_buff *skb0 = NULL;

    del_timer(&link->t);
    link->flags    &= ~(SS7_LINK_CHANGEOVER);
    link->alt       = NULL;
    link->loc_bsnt  = 0x00;
    link->rem_bsnt  = 0x00;
    link->cob_state = SS7_LK_COB_IDLE;
    if (link->skb) { kfree_skb(link->skb); link->skb = NULL; }

    while ( (skb0 = skb_dequeue(&link->rtrvb)) )
        link_output(skb0, link);
    while ( (skb0 = skb_dequeue(&link->cocbb)) )
        link_output(skb0, link);
}

static __inline void changeback_divert(struct ss7_link *link)
{
    struct ss7mtp_cb *mtp;
    struct sk_buff *skb0 = NULL;
    struct ss7_routeset *adj;
    struct ss7_route *r;
    int i, len;

    del_timer(&link->t);
    link->flags    &= ~(SS7_LINK_CHANGEBACK);
    link->cob_state = SS7_LK_COB_IDLE;
    if (link->skb) { kfree_skb(link->skb); link->skb = NULL; }

    mtp = link->linkset->local->cb;
    adj = link->linkset->adjacent;

    if ( (link->linkset->usable++) == 1 )
        for ( r = link->linkset-> routes ; r ; r = r-> lset )
            route_allow(mtp, r);


    for ( i = 0, len = skb_queue_len(&link->cocbb) ; i < len ; i++ )
        if ( (skb0 = skb_dequeue(&link->cocbb)) ) {
            mtp_output(skb0, mtp, adj);  /* may requeue */
        }
    if ( link->flags & SS7_LINK_CHANGEOVER )
        return link_down(link, link->reason);
}

/*
 *  Cancel a changeback as a result of link failure during changeback.
 *  Anything buffered in the changeback buffer (cocbb) now becomes the
 *  changeover buffer (cocbb).
 */
static __inline void changeback_abort(struct ss7_link *link)
{
    del_timer(&link->t);
    link->flags    &= ~(SS7_LINK_CHANGEBACK);
    link->cob_state = SS7_LK_COB_IDLE;
    if (link->skb) { kfree_skb(link->skb); link->skb = NULL; }
}

/*  6.2     Changeback initiation and actions
 *
 *  6.2.1   Changeback is initiated at a signalling point when a signalling link is restored,
 *  unblocked or unihibited, and therefore it becomes once again available, according to the
 *  criteria listed in 3.2.3 and 3.2.7.  The following actions are then performed:
 *
 *  a)  the alternative signalling link(s) to which traffic normally carried by the
 *      signalling link made available was previously diverted (e.g. on occurrence of a
 *      changeover), are determined.  To this set are added, if applicable, other links
 *      determined as defined in 4.4.2;
 *
 *  b)  signalling traffic is diverted (if appropriate, according to the criteria specified
 *      in clause 4) to the concerned signalling link by means of the sequence control
 *      procedure specified in 6.3; traffic diversion can be performed at the discretion of
 *      the signalling point initiating changeback, as follows:
 *
 *      i)      individually for each traffic flow (i.e. on destination basis);
 *
 *      ii)     individually for each alternative signalling link (i.e. for all the
 *              destinations previously diverted on that alternative signalling link);
 *
 *      iii)    at the same time for a number of, or for all the alternative signalling
 *              links.
 *
 *  On occurence of changeback, it may happen that traffic towards a given destination is no
 *  longer routed via a given adjacent signalling transfer point, towards which a
 *  transfer-prohibited procedure was previously performed on occurence of changeover (see
 *  5.3.1); in this case a transfer-allowed procedure is performed, as specified in 13.3.
 *
 *  In addition, if traffic towards a given destination is diverted to an alternative
 *  signalling link terminating in a signalling transfer point not currently used to carry
 *  traffic toward that destination, a transfer-prohibited procedure is performed as
 *  specified in 13.2.
 *
 *  6.2.2   In the case when there is no traffic to transfer to the signalling link made
 *  available, none of the previous actions are performed.
 *
 *  6.2.3   In the case that the signalling link made available can be used to carry
 *  signalling traffic towards a non-adjacent destination which was previously declared
 *  inacessible, the following actions apply:
 *
 *  i)      the routing of the concerned signalling traffic is unblocked and transmission of
 *          the concerend messages (if any) is started on the link made available;
 *
 *  ii)     an indication is sent to the User Part(s) (if any) to restart the concerned
 *          signalling traffic;
 *  
 *  iii)    the transfer-allowed procedure is performed, as specified in 13.3.  However, in
 *          national networks, when the recovered link is not on the normal route for that
 *          destination, the transfer- restricted procedure may be performed as specified in
 *          13.4;
 *
 *  iv)     the transfer-prohibited procedure is performed as specified in 13.2.2 i).
 *
 *  6.2.4   In the case that the signalling link made available will be the first link to be
 *  used on the normal route towards a destination previously declared restricted, the status
 *  of the route is changed to available and the transfer-allowed procedure is performed as
 *  specified in 13.3.
 *
 *  6.2.5   If the signalling point at the far end of the link made available currently is
 *  inaccessible, from the signalling point initiating changeback (see clause 9 on MTP
 *  Restart), the sequence control procedure specified in 6.3 (which requires communication
 *  between the two concerned signalling points) does not apply, instead, the time-controlled
 *  diversion specified in 6.4 is performed.  This is made also when the concerned signalling
 *  points are accessible, but there is no signalling route to it using the same outgoing
 *  signalling link(s) (or one of the same slgnalling links) from which traffic will be
 *  diverted.
 *
 *  The time-controlled diversion procedure may also be used for the changeback between
 *  different link sets instead of the sequence control procedure in order to avoid possible
 *  message mis-sequencing (see Note) or problems with multiple parallel changebacks.
 *
 *  NOTE -- The sequence control procedure can only guarantee correct sequencing of MSUs in
 *  all cases if the alternative link terminates in the same signalling point (i.e. the
 *  destination of the changeback declaration) as the newly available one.
 *
 *  6.3     Sequence control procedure
 *
 *  6.3.1   When a decision is made at a given signalling point to divert a given traffic
 *  flow (towards one or more destinations) form an alternative singalling link to a
 *  signalling link made available, the following actions are performed if possible (see
 *  6.4):
 *
 *  i)      transmission of the concerned traffic on the alternative signalling link is
 *          stopped; such traffic is stored in a changeback buffer.
 *
 *  ii)     a changeback declaration is sent to the remote signalling point of the signalling
 *          link made available via the concerned alternative siginalling link; this message
 *          indicates that no more message signal units relating to the traffic being
 *          diverted to the link made available will be sent on the alternative signalling
 *          link.
 *
 *  6.3.2   The concerned signalling point will restart diverted traffic over the signalling
 *  link made available when it receives a changeback acknowledgement from the far signalling
 *  point of the link made availble; this message indicates that all signal messages relating
 *  to the concerned traffic flow and routed to the remote signalling point via the
 *  alternative signalling link have been received.  The remote signalling point will send
 *  the changeback acknowledgement to the signalling point initiating changeback in response
 *  to the changeback declaration; any available signalling route between the two signalling
 *  points can be used to carry the changeback acknowledgement.
 *
 *  6.3.3   The changeback declaration and changeback acknowledgment are signalling network
 *  management messages and contain:
 *
 *  -   the label, indicating the destination and originating signalling points, and the
 *      identity of the signalling link to which traffic will be diverted.
 *
 *  -   the changeback-declaration (or changeback-acknowledgement) signal; and
 *
 *  -   the changeback code.
 *
 *  Formats and codes of the changeback-declaration and changeback-acknowledgement appear in
 *  clause 15.
 *
 *  6.3.4   A particular configuration of the changeback code is autonomously assiged to the
 *  changeback declaration by the signalling point initiating changeback; the same
 *  configuration is included in the changeback acknowledgement by the acknowledging
 *  signalling point.  This allows discrimination between different changeback declarations
 *  and acknowledgements when more than one sequence controll procedures are initiated in
 *  parallel, as follows.
 *
 *  6.3.5   In the case that a signalling point intends to initiate changeback in parallel
 *  for more than one alternative signalling link, a sequence control procedure is
 *  accomplished for each involved signalling link, and a changeback-declaration is sent on
 *  each of them; each changeback-declaration is assigned a different configuration of the
 *  changeback code.  Stopped traffic is stored in one or more changeback buffers (in the
 *  latter case, a changeback buffer is provided for each alternative signalling link).  When
 *  the changeback-acknowledgement relating to that alternative signalling link is received,
 *  traffic being diverted from a given alternative signalling link can be restarted on the
 *  signalling link made available, starting with the content of the changeback buffer,
 *  discrimination between the different changeback-acknowledgements is made by the
 *  changeback code configuration, which is the same as that sent in the
 *  changeback-declaration.
 *
 *  The procedure allows either reopening the recovered singalling link to traffic in a
 *  selective manner (provided that different changeback buffers are used) as soon as each
 *  changeback-acknowledgement is received, or only when all the changeback-acknowledgements
 *  have been received.
 *
 *  6.4     Time-controlled diversion procedure
 *
 *  6.4.1   The time-controlled diversion procedure is used at the end of the MTP restart
 *  procedure (see clause 9) when an adjacent signalling point becomes available, as well as
 *  for the reasons given in 6.2.5.  An example of such a use appears in Figure 12.
 *
 *  In this example, on failure of signalling link AB, traffic towards the destination D was
 *  directed signalling link AC.  When AB becomes available, the point A considers itself as
 *  the neighbour of a point which restarts and applies the MTP restart procedure (see clause
 *  9).
 *
 *  6.4.2   When changeback is initiated after the MTP restart procedure, the adjacent
 *  signalling point of the point whose MTP is restarting stops traffic to be directed from
 *  the alternative signalling link(s) for a time T3, after which it starts traffic on the
 *  signalling link(s) made available.  The time delay minimizes the probability of out-of-
 *  sequence deliver to the destination point(s).
 *
 *  6.5     Procedures in abnormal conditions
 *
 *  6.5.1   If a changeback-acknowledgement is received by a signalling point which has not
 *  previously sent a changeback-declaration, no action is taken.
 *
 *  6.5.2   If a changeback-declaration is received after the completion of the changeback
 *  procedure, a changeback-acknowledgement is sent in reponse, without taking any further
 *  action.  This corresponds to the normal action described in 6.3.2 above.
 *
 *  6.5.3   If no changeback-acknowledgement is received in response to a changeback
 *  declaration with a time T4 (see 16.8), the changeback-declaration is repeated and a new
 *  timer T5 (see 16.8) is started.  If no changeback-acknowledgement is received before the
 *  expiry of T5, the maintenance function are alterted and traffic on the link made
 *  available is started.  The changeback code contained in the changeback-acknowledgement
 *  message makes it possible to determine, in the case of parallel changebacks from more
 *  than one reserve path, which changeback-declaration is unacknowledged and has therefore
 *  to be repeated.
 */


/*
 *  Initiate changeback procedure on a siginalling link per Q.704 (07/96)
 *  6.2.1.
 */
static void link_up(struct ss7_link *link, __u8 reason)
{
    if ( !(link->flags & SS7_LINK_DONTUSE) ) {
        link->flags &= ~reason;
        return;
    }
    /*
     *  If we are performing a changeover, cancel per Q.704 (07/96) Figure
     *  30/Q.704 (sheet 3 of 6).
     */
    if ( link->flags & SS7_LINK_CHANGEOVER )
    {
        if ( link->cob_state == SS7_LK_COB_RETRIEVING ) {
            link->flags |= SS7_LINK_CHANGEBACK;
            link->reason = reason;
            return; /* wait for retrieval to finish */
        } else {
            link->flags &= ~reason;
            changeover_abort(link);
            return;
        }
    }

    link->flags |= SS7_LINK_CHANGEBACK;
    link->flags &= ~reason;

    /* perform changeback procedure */

    {
        /*
         *  If all of the routesets which have this linkset on a route to a
         *  destination signalling point have another viable signalling route
         *  which is of higher priority than the signalling route coming
         *  available, then the signalling link coming available will not be
         *  carrying traffic: divert signalling traffic to the newly available
         *  signalling link immediately.
         */

        ss7_route *ra;
        ss7_route *ru;

        for ( ra = link->linkset->routes ; ra ; ra = ra-> lset )
            if ( !(ru = alt_route(ra)) || ra->priority <= ru->priority )
                break;
        if ( !ra ) return changeback_divert(link);
    }
    {
        struct ss7mtp_cb *mtp = link->linkset->local->cb;

        if ( link->linkset->usable )
        {
            /*
             *  If there is another viable link in the linkset to which the
             *  signalling link coming available belongs, use that link to
             *  send the CBD message and do a sequence controlled changeback.
             */

            struct sk_buff *skb;
            struct ss7_link *alt;

            if ( (alt = alt_link(link)) &&
                    (skb = mtp_alloc_skb(GFP_KERNEL)) )
            {
                ss7mtp_msg *m = (ss7mtp_msg *)skb->nh.raw;
                m->pvar.mh.si = 0x0; /* snmm */
                m->pvar.mh.ni = mtp->ni;
                m->pvar.mh.mp = 0x3;
                m->pvar.mh.rl.dpc = link->linkset->adjacent->addr;
                m->pvar.mh.rl.opc = link->linkset->local->addr;
                m->pvar.mh.rl.sls = link->index;
                m->pvar.mh.h0 = 0x1; /* chm */
                m->pvar.mh.h1 = 0x3; /* cbd */
                m->pvar.msg.cbd.cbc = 0x0;
                *(skb->data) = SS7_L2_PDU;
                skb->priority = TC_PRIO_BESTEFFORT;
                __skb_trim(skb, hdrlen + sizeof(m->pvar.msg.cbd));
                link_output(skb, alt);

                link->alt = alt;
                link->skb = skb;

                del_timer(&link->t);
                link->t.data = (unsigned long)link;
                link->t.function = &mtp_t4_timeout;
                link->t.expires = jiffies + mtp->t4_value;
                add_timer(&link->t);
                return;
            }
        }
#if 0
        /* FIXME: I shouldn't (???) be peforming changeback
         * until after the MTP restart procedure has
         * completed.
         */
        else {
            struct ss7_routeset *adj = link->linkset->adjacent;

            if ( adj-> flags & SS7_RS_PROHIBITED ) {
                /*
                 *  If the link coming available is the only
                 *  link in the linkset to an otherwise
                 *  unavailable signalling point, then we
                 *  must perform MTP restart procedure.
                 */
                adj-> flags |= SS7_RS_MTP_RESTART;
                adj-> flags &= ~SS7_RS_TRA_SENT;
                del_timer(&adj->tmr);
                adj->tmr.data = (unsigned long)link;
                adj->tmr.function = &mtp_t21_timeout;
                adj->tmr.expires = jiffies + mtp->t21_value;
                add_timer(&adj->tmr);
            }
        }
#endif
        /*
         *  If the link comining available is the only link in the linkset
         *  (i.e. we would have to send the CBD via a transfer point), or we
         *  cannot otherwise communicate with the adjacent signalling point
         *  (e.g. no buffers), do a time-controlled changeback, per Q.704
         *  (07/96) 6.2.5.  When the link comes available at L3, the MTP
         *  restart procedure should be performed per Q.704 (07/96) clause 9.
         */

        del_timer(&link->t);
        link->t.data = (unsigned long)link;
        link->t.function = &mtp_t3_timeout;
        link->t.expires = jiffies + mtp->t3_value;
        add_timer(&link->t);
        return;
    }
}

static __inline void link_congested(struct ss7_link *link, unsigned char cong_status)
{
    link->cong_status = cong_status;
    link-> flags |= SS7_LINK_CONGESTED;
}

static __inline void link_congestion_ceased(struct ss7_link *link)
{
    link->cong_status = 0;
    link-> flags &= ~SS7_LINK_CONGESTED;
}

static __inline void link_retrieval_complete(struct ss7_link *link)
{
    if ( !(link-> flags & SS7_LINK_CHANGEOVER) ||
            link->cob_state != SS7_LK_COB_RETRIEVING )
        return;

    changeover_divert(link);
}

static __inline void link_retrieval_not_possible(struct ss7_link *link)
{
    if ( !(link-> flags & SS7_LINK_CHANGEOVER) ||
            link->cob_state != SS7_LK_COB_RETRIEVING )
        return;

    skb_queue_purge(&link->rtrvb);
    changeover_divert(link);
}

static __inline void link_retrieved_messages(struct ss7_link *link, struct sk_buff *skb)
{
    struct sk_buff *skb0;
    if ( !(link->flags & SS7_LINK_CHANGEOVER) ||
            link->cob_state != SS7_LK_COB_RETRIEVING )
        return;
    if ( !(skb0 = skb_clone(skb, GFP_KERNEL)) )
        return link_retrieval_not_possible(link);
    skb_queue_tail(&link->rtrvb, skb0);
    return;
}

static __inline void link_rb_cleared(struct ss7_link *link)
{
    (void)link;
    return;
}

static __inline void link_bsnt(struct ss7_link *link, unsigned char bsnt)
{
    if ( !(link-> flags & SS7_LINK_CHANGEOVER) ||
            ( link->cob_state != SS7_LK_COB_BSNT_REQ_COO &&
              link->cob_state != SS7_LK_COB_BSNT_REQ_COA ) )
        return;
    link->loc_bsnt = bsnt | 0x80;
    if ( link->skb )
    {
        struct ss7mtp_cb *mtp = link->linkset->local->cb;
        ss7mtp_msg *m = (ss7mtp_msg *)link->skb->nh.raw;
        struct sk_buff *skb = link->skb;

        switch (link->cob_state) {
            case SS7_LK_COB_BSNT_REQ_COO:
                m->pvar.mh.h0 = 0x1;
                m->pvar.mh.h1 = 0x1; /* coo */
                m->pvar.msg.coo.fsnl = bsnt;

                *(skb->data) = SS7_L2_PDU;
                skb->priority = TC_PRIO_BESTEFFORT;
                __skb_trim(skb, hdrlen + sizeof(m->pvar.msg.coo));

                link->cob_state = SS7_LK_COB_WAITING_ACK;
                
                link_output(link->skb, link->alt);

                del_timer(&link->t);
                link->t.data = (unsigned long)link;
                link->t.function = &mtp_t2_timeout;
                link->t.expires = jiffies + mtp->t2_value;
                add_timer(&link->t);

                break;

            case SS7_LK_COB_BSNT_REQ_COA:
                m->pvar.mh.h0 = 0x1;
                m->pvar.mh.h1 = 0x2; /* coa */
                m->pvar.msg.coa.fsnl = bsnt;

                *(skb->data) = SS7_L2_PDU;
                skb->priority = TC_PRIO_BESTEFFORT;
                __skb_trim(skb, hdrlen + sizeof(m->pvar.msg.coa));

                link_output(link->skb, link->alt);

                changeover_retrieval(skb,link);
                break;
        }
        kfree_skb(link->skb);
        link->skb = NULL;
    }
    return;
}

static __inline void link_in_service(struct ss7_link *link)
{
    if ( !(link->flags & SS7_LINK_OUT_OF_SERVICE) )
        return;
    del_timer(&link->t); /* stop T17 if its running */
    /*
     *  Signalling link must pass an exchange of SLTM/SLTA
     *  before it is put into service at L3.
     */
    link->flags |= SS7_LINK_TEST;
    send_sltm(link);
}

static __inline void link_out_of_service(struct ss7_link *link)
{
    if ( link->flags & SS7_LINK_OUT_OF_SERVICE ) {
        /*
         *  Restoration failed, follow procedures in Q.704 (07/96) 12.2.2 to
         *  further attempt to restore link after time T17.
         */
        ss7mtp_cb *mtp = link->linkset->local->cb;

        del_timer(&link->t);
        link->t.data = (unsigned long)link;
        link->t.function = &mtp_t17_timeout;
        link->t.expires = jiffies + mtp->t17_value;
        add_timer(&link->t);
        return;
    }
    link_down(link, SS7_LINK_OUT_OF_SERVICE);
}

static __inline void link_remote_processor_outage(struct ss7_link *link)
{
    link_down(link, SS7_LINK_BLOCKED);
    return;
}

static __inline void link_remote_processor_recovered(struct ss7_link *link)
{
    link_up(link, SS7_LINK_BLOCKED);
    return;
}

static __inline void link_rtb_cleared(struct ss7_link *link)
{
    (void)link;
    /* FIXME: make this doooo something */
    return;
}

void ss7mtp_input(struct sk_buff *skb)
{
    /* ok, I have an mtp message from L2: what do I do with it ? */
    unsigned char cmd = skb->data[0];
    struct ss7_linkset *ls;
    struct ss7_link *link;
    struct ss7mtp_cb *mtp;

    if (!skb->dev) goto bad_control;
    if (!skb->dev->ss7_ptr) goto bad_control;
    if (!(link = &((struct ss7_iface *)skb->dev->ss7_ptr)->link)) goto bad_control;
    if (!(ls = link->linkset)) goto bad_control;
    if (!ls->local) goto bad_control;
    if (!(mtp = ls->local->cb)) goto bad_control;

    /* ok, we have an mtp control block identified, check what kind of message
     * */
    switch (cmd) {
        case SS7_L3_PDU:
            skb_pull(skb, 4); /* command fsn/fib bsn/bib and li */
            mtp->input(mtp, skb);
            break;
        case SS7_L3_LINK_CONGESTED:
            link_congested(link, skb->data[1]);
            break;
        case SS7_L3_LINK_CONGESTION_CEASED:
            link_congestion_ceased(link);
            break;
        case SS7_L3_RETRIEVED_MESSAGES:
            link_retrieved_messages(link, skb);
            break;
        case SS7_L3_RETRIEVAL_COMPLETE:
            link_retrieval_complete(link);
            break;
        case SS7_L3_RB_CLEARED:
            link_rb_cleared(link);
            break;
        case SS7_L3_BSNT:
            link_bsnt(link, skb->data[1]);
            break;
        case SS7_L3_IN_SERVICE:
            link_in_service(link);
            break;
        case SS7_L3_OUT_OF_SERVICE:
            link_out_of_service(link);
            break;
        case SS7_L3_REMOTE_PROCESSOR_OUTAGE:
            link_remote_processor_outage(link);
            break;
        case SS7_L3_REMOTE_PROCESSOR_RECOVERED:
            link_remote_processor_recovered(link);
            break;
        case SS7_L3_RTB_CLEARED:
            link_rtb_cleared(link);
            break;
        default:
            goto bad_control;
    }
    /*
     *  Note: although the above calls to not consume the skb, they do in some
     *  circumstances modify it.
     */
    kfree_skb(skb);
    return;

bad_control:
    /* log and peg */
    kfree_skb(skb);
    return;
}



