📄 mtp.c
字号:
/* mtp.c - MTP2 and MTP3 functionality.
*
* Copyright (C) 2005-2006, Sifira A/S.
*
* Author: Kristian Nielsen <kn@sifira.dk>
* Anders Baekgaard <ab@sifira.dk>
* Anders Baekgaard <ab@dicea.dk>
*
* This file is part of chan_ss7.
*
* chan_ss7 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.
*
* chan_ss7 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 chan_ss7; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdarg.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <pthread.h>
#include <signal.h>
#include <sys/poll.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include "zaptel.h"
#define FAST_HDLC_NEED_TABLES
#include "fasthdlc.h"
#include "config.h"
#include "mtp.h"
#include "transport.h"
#include "lffifo.h"
#include "cluster.h"
#include "utils.h"
#ifdef MTP_STANDALONE
#include "aststubs.h"
#define cluster_mtp_received(link, event) {}
#define cluster_mtp_forward(req) {}
#define cluster_receivers_alive(linkset) (0)
#else
#include "asterisk/options.h"
#include "asterisk/logger.h"
#include "asterisk/sched.h"
#define mtp_sched_add ast_sched_add
#define mtp_sched_del ast_sched_del
#define mtp_sched_runq ast_sched_runq
#define mtp_sched_context_create sched_context_create
#define mtp_sched_context_destroy sched_context_destroy
#endif
/* NOTE: most of this code is run in the MTP thread, and has realtime
constraints because of the need to constantly feed/read the
signalling link with low latence and no frame drop.
The thread runs with high realtime priority, and any kind of
locking should generally be avoided. This includes ast_log() (use
fifo_log() instead), and malloc()/free()!
*/
/* For testing failover mechanism */
int testfailover = 0;
/* #define DROP_PACKETS_PCT 66 */
/* #define DO_RAW_DUMPS */
/* Scheduling context for MTP2. */
/* This should ONLY be used by the MTP2 thread, otherwise the locking done
by the sched operations may fatally delay the MTP2 thread because of
priority inversion. */
static struct sched_context *mtp2_sched = NULL;
/* Set true to ask mtp thread to stop */
static int stop_mtp_thread;
static int receivepipe[2] = {-1, -1};
/* Lock-free FIFOs for communication with the MTP thread.
The sendbuf is polled by the MTP thread whenever the link is ready to
transmit data (every 2msec). It contains struct mtp_req entries for
the higher level protocol layers (currently only ISUP).
The receivebuf has an associated event pipe, and the MTP thread do
non-blocking dummy writes to it whenever blocks are put in the buffer.
The SS7 monitor thread can then wait for new data in poll(). The
receivebuf contains struct mtp_event entries.
The controlbuf is polled by the MTP thread, it contains struct mtp_req
entries for control purposes only.
*/
struct lffifo *sendbuf[MAX_LINKSETS];
struct lffifo *receivebuf;
struct lffifo *controlbuf;
typedef struct mtp2_state {
/* MTP2 stuff. */
enum {
/* Link is stopped by management command, will not go up until
started explicitly. */
MTP2_DOWN,
/* Initial alignment has started, link is transmitting 'O', but no 'O',
'N', or 'E' has been received. */
MTP2_NOT_ALIGNED,
/* 'O' has been received, 'N' or 'E' is transmitted. */
MTP2_ALIGNED,
/* 'N' or 'E' is transmitted and received. Runs for the duration of T4 to
check that the link is of sufficient quality in terms of error rate. */
MTP2_PROVING,
/* Local T4 expired, and we are sending FISU, but remote is still
proving. */
MTP2_READY,
/* The link is active sending and receiving FISU and MSU. */
MTP2_INSERVICE,
} state;
/* Counts of raw bytes read and written, used to timestamp raw dumps.
Make them double to avoid overflow for quite a while. */
double readcount, writecount;
/* Sequence numbers and indicator bits to be sent in signalling units. */
int send_fib;
int send_bsn, send_bib;
/* Send initial SLTM? */
int send_sltm;
/* Timeslot for signalling channel */
int schannel;
int slinkno;
struct link* link;
int sls;
int subservice;
/* logical link name */
char* name;
/* Open fd for signalling link zaptel device. */
int fd;
/* Receive buffer. */
unsigned char rx_buf[272 + 7];
int rx_len;
unsigned short rx_crc;
/* Transmit buffer. */
unsigned char tx_buffer[272 + 7 + 5];
int tx_len;
int tx_sofar;
int tx_do_crc; /* Flag used to handle writing CRC bytes */
unsigned short tx_crc;
/* Zaptel transmit buffer. */
unsigned char zap_buf[ZAP_BUF_SIZE];
int zap_buf_full;
/* HDLC encoding and decoding state. */
struct fasthdlc_state h_rx;
struct fasthdlc_state h_tx;
/* Last few raw bytes received, for debugging link errors. */
unsigned char backbuf[36];
int backbuf_idx;
/* Retransmit buffer. */
struct { int len; unsigned char buf[MTP_MAX_PCK_SIZE]; } retrans_buf[128];
/* Retransmit counter; if this is != -1, it means that retransmission is
taking place, with this being the next sequence number to retransmit. */
int retrans_seq;
/* Last sequence number ACK'ed by peer. */
int retrans_last_acked;
/* Last sequence number sent to peer. */
int retrans_last_sent;
/* Counter for signal unit/alignment error rate monitors (Q.703 (10)). */
int error_rate_mon;
/* Counters matching the D and N values of the error rate monitors. */
int emon_ncount, emon_dcount;
/* Counter for bad BSN */
int bsn_errors;
/* Q.703 timer T1 "alignment ready" (waiting for peer to end initial
alignment after we are done). */
int mtp2_t1;
/* Q.703 timer T2 "not aligned" (waiting to receive O, E, or N after sending
O). */
int mtp2_t2;
/* Q.703 timer T3 "aligned" (waiting to receive E or N after sending E or
N). */
int mtp2_t3;
/* Q.703 timer T4 "proving period" - proving time before ending own initial
alignment. */
int mtp2_t4;
/* Q.703 timer T7 "excessive delay of acknowledgement" . */
int mtp2_t7;
/* Set true when SLTA is received and User Parts (ie. ISUP) is notified that
the link is now in service. */
int level4_up;
/* Hm, the rest is actually MTP3 state. Move to other structure, or
rename this structure. */
int sltm_t1; /* Timer T1 for SLTM (Q.707) */
int sltm_t2; /* Timer T2 for SLTM (Q.707) */
int sltm_tries; /* For SLTM retry (Q.707 (2.2)) */
/* Q.704 timer T17, "initial alignment restart delay". */
int mtp3_t17;
} mtp2_t;
/* ToDo: Support more than one signalling link ... */
/* ToDo: Need real initialization, that doesn't depend on linker. */
mtp2_t mtp2_state[MAX_SCHANNELS];
/* Get the next sequence number, modulo 128. */
#define MTP_NEXT_SEQ(x) (((x) + 1) % 128)
/* Forward declaration, needed because of cyclic reference graph. */
static mtp2_t* find_alternative_slink(mtp2_t* m);
static void start_initial_alignment(mtp2_t *m, char* reason);
static void abort_initial_alignment(mtp2_t *m);
static void mtp2_cleanup(mtp2_t *m);
static void mtp2_queue_msu(mtp2_t *m, int sio, unsigned char *sif, int len);
static void deliver_l4(mtp2_t *m, unsigned char *sif, int len, int sio);
static void l4up(mtp2_t* m);
static void l4down(mtp2_t* m);
static void t7_stop(mtp2_t *m);
static void fifo_log(mtp2_t *m, int level, const char *file, int line,
const char *function, const char *format, ...)
__attribute__ ((format (printf, 6, 7)));
static void process_msu(struct mtp2_state* m, unsigned char* buf, int len);
static inline ss7_variant variant(mtp2_t* m) /* by lin.miao@openvox.cn */
{
return m->link->linkset->variant;
}
int mtp2_slink_inservice(int ix) {
struct mtp2_state* m = &mtp2_state[ix];
return m->state == MTP2_INSERVICE;
}
int mtp_cmd_linkstatus(char* buff, int slinkno)
{
char* format = "linkset %s, link %s, schannel %d, sls %d, %s, rx: %d, tx: %d/%d, sentseq/lastack: %d/%d, total %9llu, %9llu\n";
char* s = "?";
if (slinkno >= this_host->n_schannels)
return -1;
/* Todo: when more than one signalling link supported, check against that */
struct mtp2_state* m = &mtp2_state[slinkno];
switch (m->state) {
case MTP2_DOWN: s = "DOWN"; break;
case MTP2_NOT_ALIGNED: s = "NOT_ALIGNED"; break;
case MTP2_ALIGNED: s = "ALIGNED"; break;
case MTP2_PROVING: s = "PROVING"; break;
case MTP2_READY: s = "READY"; break;
case MTP2_INSERVICE: s = "INSERVICE"; break;
default: s = "UNKNOWN";
}
sprintf(buff, format, m->link->linkset->name, m->link->name, m->schannel, m->sls, s, m->rx_len, m->tx_sofar, m->tx_len, m->retrans_last_sent, m->retrans_last_acked, (long long) m->readcount, (long long) m->writecount);
return 0;
}
int mtp_cmd_data(int fd, int argc, char *argv[])
{
unsigned char buf[MTP_EVENT_MAX_SIZE];
int len = 0;
int i;
mtp2_t* m = &mtp2_state[0];
for (i = 3; i < argc; i++) {
char* p = argv[i];
while (*p) {
char b[3];
unsigned int v;
if (*p == ' ') {
p++;
continue;
}
b[0] = *p++;
b[1] = *p++;
b[2] = 0;
sscanf(b, "%x", &v);
buf[len++] = v;
}
}
mtp2_queue_msu(m, 3, buf, len);
deliver_l4(m, &buf[0], len, MTP_EVENT_SCCP);
return 0;
}
static inline int peerpc(mtp2_t* m)
{
return m->link->linkset->dpc;
}
static inline int linkpeerpc(mtp2_t* m)
{
if (m->link->dpc)
return m->link->dpc;
return m->link->linkset->dpc;
}
static mtp2_t* findtargetslink(mtp2_t *originalm, int sls)
{
int i;
struct link* link = originalm->link;
struct mtp2_state* bestm = NULL;
for (i = 0; i < this_host->n_schannels; i++) {
struct mtp2_state* m = &mtp2_state[i];
struct link* slink = m->link;
if (m->sls == sls) {
if (link->linkset == slink->linkset) {
fifo_log(m, LOG_DEBUG, "Target slink %s %d -> %s\n", originalm->name, sls, m->name);
return m;
}
if (is_combined_linkset(link->linkset, slink->linkset))
bestm = m;
}
}
fifo_log(originalm, LOG_DEBUG, "Target slink %s %d -> %s\n", originalm->name, sls, bestm ? bestm->name : "(none)");
return bestm;
}
static void mtp_put(mtp2_t *m, struct mtp_event *event) {
static int log_safe_count = 0;
int res;
res = lffifo_put(receivebuf, (unsigned char *)event,
sizeof(*event) + event->len);
if(res) {
/* Can't fifo_log() here, or we would get an infinite loop. */
/* Still, avoid excessive logging if the other thread gets long behind. */
if(log_safe_count == 0) {
ast_log(LOG_NOTICE, "Full MTP receivebuf, event lost, type=%d.\n", event->typ);
log_safe_count = 2000;
}
} else {
/* Wake up the other end. */
write(receivepipe[1], "\0", 1);
}
if ((event->typ == MTP_EVENT_ISUP) || (event->typ == MTP_EVENT_STATUS)) {
cluster_mtp_received(m ? m->link : NULL, event);
}
if(log_safe_count > 0) {
log_safe_count--;
}
}
/* Use this instead of ast_log() in the MTP thread, to avoid locking
issues interupting the link timing.
Note that LOG_WHATEVER includes all of (level, file, line, function), thanks
to #define trickery in asterisk/logger.h! */
/* Grmble... stupid GCC allows the __attribute__ only in a
declaration, not definition. */
static void fifo_log(mtp2_t *m, int level, const char *file, int line,
const char *function, const char *format, ...)
{
va_list arg;
unsigned char buf[MTP_EVENT_MAX_SIZE];
struct mtp_event *event = (struct mtp_event *)buf;
event->typ = MTP_EVENT_LOG;
event->log.level = level;
event->log.file = file;
event->log.line = line;
event->log.function = function;
va_start(arg, format);
vsnprintf((char*)event->buf, sizeof(buf) - sizeof(struct mtp_event), format, arg);
va_end(arg);
event->len = strlen((char*)event->buf) + 1;
mtp_put(m, event);
}
static void log_frame(mtp2_t *m, int out, unsigned char *buf, int len) {
unsigned char ebuf[MTP_EVENT_MAX_SIZE];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -