?? 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];/* 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);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 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]; struct mtp_event *event = (struct mtp_event *)ebuf; event->typ = MTP_EVENT_DUMP; event->dump.out = out; gettimeofday(&event->dump.stamp, NULL); event->dump.slinkno = m->slinkno; if(sizeof(struct mtp_event) + len > MTP_MAX_PCK_SIZE) { len = MTP_MAX_PCK_SIZE - sizeof(struct mtp_event); } event->len = len; memcpy(event->buf, buf, len); mtp_put(m, event);}#ifdef DO_RAW_DUMPSstatic void mtp2_dump_raw(mtp2_t *m, unsigned char *buf, int len, int out) { unsigned char ebuf[MTP_EVENT_MAX_SIZE]; struct mtp_event *event = (struct mtp_event *)ebuf; event->typ = MTP_EVENT_RAWDUMP; event->rawdump.out = out; if(sizeof(struct mtp_event) + len > MTP_MAX_PCK_SIZE) { len = MTP_MAX_PCK_SIZE - sizeof(struct mtp_event); } event->len = len; memcpy(event->buf, buf, len); mtp_put(m, event);}#endifstatic int t17_timeout(void *data) { mtp2_t *m = data; fifo_log(m, LOG_DEBUG, "link %s\n", m->name); m->mtp3_t17 = -1; start_initial_alignment(m, "t17_timeout"); return 0; /* Do not re-schedule */}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -