mISDNuser/tools/logger.c

2077 lines
50 KiB
C

/*
*
* Copyright 2015 Karsten Keil <keil@b1-systems.de>
*
* 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., 59
* Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* The full GNU General Public License is included in this distribution in the
* file called LICENSE.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#include <netdb.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <malloc.h>
#include <unistd.h>
#include <poll.h>
#include <signal.h>
#include <time.h>
#include <sys/ioctl.h>
#include <mISDN/q931.h>
#include <mISDN/suppserv.h>
#include "logger.h"
#include "../lib/include/debug.h"
#ifndef DEF_CONFIG_FILE
#define DEF_CONFIG_FILE "/etc/misdnlogger.conf"
#endif
#define MISDNLOGGER_VERSION "0.9"
static int running = 0;
static int do_daemon = 1;
static unsigned int ml_debugmask = 0;
static char def_config[] = DEF_CONFIG_FILE;
static char *config_file = NULL;
int mI_ControllerCount;
static struct mController *mI_Controller;
static struct sigaction mysig_term, mysig_dump;
struct mController *defController;
const char *mTypesStr[] = {
"UNKNOWN",
"ALERTING",
"CALL_PROCEEDING",
"CONNECT",
"CONNECT_ACKNOWLEDGE",
"PROGRESS",
"SETUP",
"SETUP_ACKNOWLEDGE",
"RESUME",
"RESUME_ACKNOWLEDGE",
"RESUME_REJECT",
"SUSPEND",
"SUSPEND_ACKNOWLEDGE",
"SUSPEND_REJECT",
"USER_INFORMATION",
"DISCONNECT",
"RELEASE",
"RELEASE_COMPLETE",
"RESTART",
"RESTART_ACKNOWLEDGE",
"SEGMENT",
"CONGESTION_CONTROL",
"INFORMATION",
"FACILITY",
"NOTIFY",
"STATUS",
"STATUS_ENQUIRY",
"HOLD",
"HOLD_ACKNOWLEDGE",
"HOLD_REJECT",
"RETRIEVE",
"RETRIEVE_ACKNOWLEDGE",
"RETRIEVE_REJECT",
"REGISTER",
"ALL"
};
/**********************************************************************
Signal handler for clean shutdown
***********************************************************************/
static void
termHandler(int sig)
{
iprint("Terminating on signal %d -- request shutdown mISDNlogger\n", sig);
running = 0;
return;
}
/**********************************************************************
Signal handler for dumping state info
***********************************************************************/
static void
dumpHandler(int sig)
{
iprint("Received signal %d -- start dumping\n", sig);
}
static void usage(void)
{
fprintf(stderr, "Usage: mISDNlogger [OPTIONS]\n");
fprintf(stderr, " Options are\n");
fprintf(stderr, " -?, --help this help\n");
fprintf(stderr, " -c, --config <file> use this config file - default %s\n", def_config);
fprintf(stderr, " -d, --debug <level> extra debug\n");
fprintf(stderr, " -f, --foreground run in forground, not as daemon\n");
fprintf(stderr, "\n");
fprintf(stderr, "mISDNlogger Version %s\n", MISDNLOGGER_VERSION);
fprintf(stderr, "\n");
}
static int opt_parse(int ac, char *av[])
{
int c;
for (;;) {
int option_index = 0;
static struct option long_options[] = {
{"help", 0, 0, '?'},
{"config", 1, 0, 'c'},
{"debug", 1, 0, 'd'},
{"foreground", 0, 0, 'f'},
{0, 0, 0, 0}
};
c = getopt_long(ac, av, "?c:f", long_options, &option_index);
if (c == -1)
break;
switch (c) {
case 0:
fprintf(stderr, "option %s", long_options[option_index].name);
if (optarg)
fprintf(stderr, " with arg %s", optarg);
fprintf(stderr, "\n");
break;
case 'c':
if (optarg)
config_file = optarg;
else {
fprintf(stderr, "option -c but no filename\n");
return -2;
}
break;
case 'd':
if (optarg) {
errno = 0;
ml_debugmask = (unsigned int)strtoul(optarg, NULL, 0);
if (errno) {
fprintf(stderr, "cannot read debuglevel from %s - %s\n", optarg, strerror(errno));
return -3;
}
} else {
fprintf(stderr, "option -d but no value for debugmask\n");
return -3;
}
break;
case 'f':
do_daemon = 0;
break;
case '?':
usage();
return -1;
}
}
c = ac - optind;
if (c != 0) {
fprintf(stderr, "unknown options: %s\n", av[optind]);
return -2;
}
return 0;
}
extern int parse_config(FILE *);
static int read_config_file(char *name)
{
FILE *f;
int ret;
f = fopen(name, "r");
if (!f) {
fprintf(stderr, "cannot open %s - %s\n", name, strerror(errno));
return -1;
} else {
ret = parse_config(f);
}
fclose(f);
return ret;
}
static int my_lib_debug(const char *file, int line, const char *func, int level, const char *fmt, va_list va)
{
int ret, ll;
char log[512];
ret = vsnprintf(log, 512, fmt, va);
if (do_daemon) {
switch(level) {
case 1:
ll = LOG_ERR;
break;
case 2:
ll = LOG_WARNING;
break;
case 3:
ll = LOG_INFO;
break;
default:
ll = LOG_DEBUG;
break;
}
syslog(ll, "%20s:%3d %s\n", file, line, log);
} else {
fprintf(stderr, "%20s:%3d %s\n", file, line, log);
}
return ret;
}
static struct mi_ext_fn_s myfn = {
.prt_debug = my_lib_debug,
};
static int log_output(struct mController *mc)
{
struct tm *mt;
char log[32];
if (mc->lp == mc->logtxt) /* nothing to log */
return 0;
if (mc->tv) {
mt = localtime((time_t *) &mc->tv->tv_sec);
snprintf(log, 32, "Contr%d: %02d:%02d:%02d.%06ld",
mc->mNr, mt->tm_hour, mt->tm_min, mt->tm_sec, mc->tv->tv_usec);
} else
snprintf(log, 32, "Contr%d:", mc->mNr);
if (do_daemon) {
if (mc->syslog > 0)
syslog(mc->syslog, "%s %s\n", log, mc->logtxt);
} else if (mc->syslog > 0) {
fprintf(stderr, "%s %s\n", log, mc->logtxt);
fflush(stderr);
}
if (mc->log) {
fprintf(mc->log, "%s %s\n", log, mc->logtxt);
fflush(mc->log);
}
return 0;
}
static int open_logfile()
{
int i, err;
struct mController *mc;
for (i = 0; i < mI_ControllerCount; i++) {
mc = &mI_Controller[i];
if (mc->logfile[0]) { /* logfilename set */
if (strcmp(mc->logfile, defController->logfile)) {
/* own name */
mc->log = fopen(mc->logfile, "w");
if (!mc->log) {
err = errno;
eprint("Cannot open log file %s - %s\n", mc->logfile, strerror(err));
return -1;
}
} else { /* same name as default - only use one logfile */
if (!defController->log) {
mc->log = fopen(mc->logfile, "w");
if (!mc->log) {
err = errno;
eprint("Cannot open log file %s - %s\n", mc->logfile, strerror(err));
return -1;
} else
defController->log = mc->log;
} else
mc->log = defController->log;
}
}
}
return 0;
}
static void close_logfile()
{
int i;
FILE *log;
for (i = 0; i < mI_ControllerCount; i++) {
log = mI_Controller[i].log;
if (log) {
mI_Controller[i].log = NULL;
if (defController->log == log)
continue;
fclose(log);
}
}
if (defController->log) {
fclose(defController->log);
defController->log = NULL;
}
}
static int logprint(struct mController *mc, const char *fmt, ...)
{
int rest, ret = 0;
va_list args;
va_start(args, fmt);
rest = LOGTXT_SIZE - (mc->lp - mc->logtxt);
ret = vsnprintf(mc->lp, rest, fmt, args);
if (ret > rest) {
ret = -1;
mc->lp += rest;
mc->logtxt[LOGTXT_SIZE - 1] = 0;
} else
mc->lp += ret;
va_end(args);
return ret;
}
/*
* fmt 0 log each octet without space
* fmt 1 log each octet with one space before
* fmt n log each octet with one space before and a extra space every n octets
*/
static void loghex(struct mController *mc, unsigned char *p, int len, int fmt)
{
int i, rest, n = 1;
rest = LOGTXT_SIZE - (mc->lp - mc->logtxt);
while (rest > 3 && len) {
if (fmt)
i = snprintf(mc->lp, rest, " %02x", *p++);
else
i = snprintf(mc->lp, rest, "%02x", *p++);
rest -= i;
mc->lp += i;
len--;
if (fmt > 1) {
if (rest && len && !(n % fmt)) {
*mc->lp++ = ' ';
*mc->lp = 0;
rest--;
}
n++;
}
}
}
struct ctstamp {
size_t cmsg_len;
int cmsg_level;
int cmsg_type;
struct timeval tv;
};
#define RR 0x01
#define RNR 0x05
#define REJ 0x09
#define SABME 0x6f
#define SABM 0x2f
#define DM 0x0f
#define UI 0x03
#define DISC 0x43
#define UA 0x63
#define FRMR 0x87
#define XID 0xaf
#define CMD 0
#define RSP 1
static int IsUI(unsigned char *data)
{
return (data[0] & 0xef) == UI;
}
static int IsUA(unsigned char *data)
{
return (data[0] & 0xef) == UA;
}
static int IsDM(unsigned char *data)
{
return (data[0] & 0xef) == DM;
}
static int IsDISC(unsigned char *data)
{
return (data[0] & 0xef) == DISC;
}
static int IsRR(unsigned char *data)
{
return data[0] == RR;
}
static int IsSABME(unsigned char *data)
{
return (data[0] & 0xef) == SABME;
}
static int IsREJ(unsigned char *data)
{
return data[0] == REJ;
}
static int IsFRMR(unsigned char *data)
{
return data[0] == FRMR;
}
static int IsRNR(unsigned char *data)
{
return data[0] == RNR;
}
static const char MTidx[] = {
MT_ALERTING,
MT_CALL_PROCEEDING,
MT_CONNECT,
MT_CONNECT_ACKNOWLEDGE,
MT_PROGRESS,
MT_SETUP,
MT_SETUP_ACKNOWLEDGE,
MT_RESUME,
MT_RESUME_ACKNOWLEDGE,
MT_RESUME_REJECT,
MT_SUSPEND,
MT_SUSPEND_ACKNOWLEDGE,
MT_SUSPEND_REJECT,
MT_USER_INFORMATION,
MT_DISCONNECT,
MT_RELEASE,
MT_RELEASE_COMPLETE,
MT_RESTART,
MT_RESTART_ACKNOWLEDGE,
MT_SEGMENT,
MT_CONGESTION_CONTROL,
MT_INFORMATION,
MT_FACILITY,
MT_NOTIFY,
MT_STATUS,
MT_STATUS_ENQUIRY,
MT_HOLD,
MT_HOLD_ACKNOWLEDGE,
MT_HOLD_REJECT,
MT_RETRIEVE,
MT_RETRIEVE_ACKNOWLEDGE,
MT_RETRIEVE_REJECT,
MT_REGISTER,
0
};
static int log_coding(struct mController *mc, char *ie, int cstd, int loc)
{
int ret = 0;
if (ie) {
ret = logprint(mc, " %s:", ie);
if (ret < 0)
return -1;
}
if (cstd >= 0) {
switch(cstd) {
case 0:
ret = logprint(mc, " Std:CCITT");
break;
case 1:
ret = logprint(mc, " Std:other Intnational");
break;
case 2:
ret = logprint(mc, " Std:national");
break;
default:
ret = logprint(mc, " Std:other");
break;
}
}
switch(loc) {
case -1: /* skip */
break;
case 0:
ret = logprint(mc, " Loc:user,");
break;
case 1:
ret = logprint(mc, " Loc:private network serving the local User,");
break;
case 2:
ret = logprint(mc, " Loc:public network serving the local user,");
break;
case 3:
ret = logprint(mc, " Loc:transit network,");
break;
case 4:
ret = logprint(mc, " Loc:public network serving the remote user,");
break;
case 5:
ret = logprint(mc, " Loc:private network serving the remote user,");
break;
case 7:
ret = logprint(mc, " Loc:international network,");
break;
case 10:
ret = logprint(mc, " Loc:network beyond interworking point,");
break;
default:
ret = logprint(mc, " Loc:reserved,");
break;
}
return ret;
}
static int log_bearer_capability(struct mController *mc, struct l3_msg *l3m)
{
int ret, r, m, l, i, o;
unsigned char *p = l3m->bearer_capability ;
l = *p++;
if (0 > log_coding(mc, "bearerCap", (*p & 0x60) >> 5, -1))
return -1;
if (0 > logprint(mc, " %s", mi_bearer2str(*p & 0x1f)))
return -1;
if (l < 2) {
if (0 > logprint(mc, " bearerCap too short"))
return -1;
return 0;
}
l -= 2;
/* octet 4 */
p++;
m = *p & 0x60;
r = *p & 0x1f;
if (m == 0x40) {
if (0 > logprint(mc, " packet mode"))
return -1;
} else if (m == 0) {
if (0 > logprint(mc, " circuit mode rate:"))
return -1;
switch(r) {
case 0x10:
ret = logprint(mc, "64Kbit/s");
break;
case 0x11:
ret = logprint(mc, "2x64Kbit/s");
break;
case 0x13:
ret = logprint(mc, "384Kbit/s");
break;
case 0x15:
ret = logprint(mc, "1536Kbit/s");
break;
case 0x17:
ret = logprint(mc, "1920Kbit/s");
break;
default:
ret = logprint(mc, "reserved");
break;
}
if (ret < 0)
return -1;
} else {
if (0 > logprint(mc, " undefined mode"))
return -1;
}
if (l && !(*p & 0x80)) {
/* octet 4a */
l--;
p++;
if (0 > logprint(mc, " octet4a=0x%02x"))
return -1;
if (l && !(*p & 0x80)) {
/* octet 4b */
l--;
p++;
if (0 > logprint(mc, " octet4a=0x%02x"))
return -1;
}
}
/* octet 5 */
while (l > 0) {
l--;
p++;
i = (*p & 0x60) >> 5;
m = *p & 0x1f;
if (0 > logprint(mc, " L%d ", i))
return -1;
if (i == 1) {
switch(m) {
case 1:
ret = logprint(mc, "protocol V.110/X.30");
break;
case 3:
ret = logprint(mc, "protocol G.711 Alaw");
break;
case 4:
ret = logprint(mc, "protocol G.721 32kbit/s ADPCM");
break;
case 5:
ret = logprint(mc, "protocol G.721 32kbit/s ADPCM");
break;
case 6:
ret = logprint(mc, "protocol G.721 32kbit/s ADPCM");
break;
case 7:
ret = logprint(mc, "protocol G.721 32kbit/s ADPCM");
break;
case 9:
ret = logprint(mc, "protocol G.721 32kbit/s ADPCM");
break;
default:
ret = logprint(mc, "protocol reserved (%d)", m);
break;
}
if (ret < 0)
return -1;
o = 'a';
while((l > 0) && !(*p & 0x80)) {
l--;
p++;
if (0 > logprint(mc, " octet5%c=0x%02x", o, *p))
return -1;
o++;
}
} else if (i == 2) {
switch(m) {
case 2:
ret = logprint(mc, "protocol Q.921");
break;
case 6:
ret = logprint(mc, "protocol X.25");
break;
default:
ret = logprint(mc, "protocol reserved (%d)", m);
break;
}
if (ret < 0)
return -1;
} else if (i == 3) {
switch(m) {
case 2:
ret = logprint(mc, "protocol Q.931");
break;
case 6:
ret = logprint(mc, "protocol X.25");
break;
default:
ret = logprint(mc, "protocol reserved (%d)", m);
break;
}
if (ret < 0)
return -1;
} else {
if (0 > logprint(mc, "invalid"))
return -1;
break;
}
}
return 0;
}
static int log_cause(struct mController *mc, struct l3_msg *l3m)
{
unsigned char *p = l3m->cause;
int ret, c, loc, l = *p++;
c = (*p & 0x60) >> 5;
loc = *p & 0xf;
l--;
ret = log_coding(mc, "cause", c, loc);
if (ret < 0)
return -1;
if (ret < 0)
return -1;
if (l && !(*p & 0x80)) {
/* octet 3a */
l--;
p++;
c = *p & 0x7f;
switch(c) {
case 0:
ret = logprint(mc, " Q.931");
break;
case 3:
ret = logprint(mc, " X.21");
break;
case 4:
ret = logprint(mc, " X.25");
break;
default:
ret = logprint(mc, " reserved recommendation(%d)", c);
break;
}
if (ret < 0)
return -1;
}
if (l) {
p++;
l--;
c = *p++ & 0x7f;
switch(c) {
case 0x01: ret = logprint(mc, " 'Unallocated (unassigned) number'"); break;
case 0x02: ret = logprint(mc, " 'No route to specified transit network'"); break;
case 0x03: ret = logprint(mc, " 'No route to destination'"); break;
case 0x06: ret = logprint(mc, " 'Channel unacceptable'"); break;
case 0x07: ret = logprint(mc, " 'Call awarded and being delivered in an established channel'"); break;
case 0x10: ret = logprint(mc, " 'Normal call clearing'"); break;
case 0x11: ret = logprint(mc, " 'User busy'"); break;
case 0x12: ret = logprint(mc, " 'No user responding'"); break;
case 0x13: ret = logprint(mc, " 'No answer from user (user alerted)'"); break;
case 0x15: ret = logprint(mc, " 'Call rejected'"); break;
case 0x16: ret = logprint(mc, " 'Number changed'"); break;
case 0x1A: ret = logprint(mc, " 'Non-selected user clearing'"); break;
case 0x1B: ret = logprint(mc, " 'Destination out of order'"); break;
case 0x1C: ret = logprint(mc, " 'Invalid number format'"); break;
case 0x1D: ret = logprint(mc, " 'Facility rejected'"); break;
case 0x1E: ret = logprint(mc, " 'Response to STATUS ENQUIRY'"); break;
case 0x1F: ret = logprint(mc, " 'Normal, unspecified'"); break;
case 0x22: ret = logprint(mc, " 'No circuit / channel available'"); break;
case 0x26: ret = logprint(mc, " 'Network out of order'"); break;
case 0x29: ret = logprint(mc, " 'Temporary failure'"); break;
case 0x2A: ret = logprint(mc, " 'Switching equipment congestion'"); break;
case 0x2B: ret = logprint(mc, " 'Access information discarded'"); break;
case 0x2C: ret = logprint(mc, " 'Requested circuit / channel not available'"); break;
case 0x2F: ret = logprint(mc, " 'Resources unavailable, unspecified'"); break;
case 0x31: ret = logprint(mc, " 'Quality of service unavailable'"); break;
case 0x32: ret = logprint(mc, " 'Requested facility not subscribed'"); break;
case 0x39: ret = logprint(mc, " 'Bearer capability not authorized'"); break;
case 0x3A: ret = logprint(mc, " 'Bearer capability not presently available'"); break;
case 0x3F: ret = logprint(mc, " 'Service or option not available, unspecified'"); break;
case 0x41: ret = logprint(mc, " 'Bearer capability not implemented'"); break;
case 0x42: ret = logprint(mc, " 'Channel type not implemented'"); break;
case 0x45: ret = logprint(mc, " 'Requested facility not implemented'"); break;
case 0x46: ret = logprint(mc, " 'Only restricted digital information bearer capability is available'"); break;
case 0x4F: ret = logprint(mc, " 'Service or option not implemented, unspecified'"); break;
case 0x51: ret = logprint(mc, " 'Invalid call reference value'"); break;
case 0x52: ret = logprint(mc, " 'Identified channel does not exist'"); break;
case 0x53: ret = logprint(mc, " 'A suspended call exists, but this call identity does not'"); break;
case 0x54: ret = logprint(mc, " 'Call identity in use'"); break;
case 0x55: ret = logprint(mc, " 'No call suspended'"); break;
case 0x56: ret = logprint(mc, " 'Call having the requested call identity has been cleared'"); break;
case 0x58: ret = logprint(mc, " 'Incompatible destination'"); break;
case 0x5B: ret = logprint(mc, " 'Invalid transit network selection'"); break;
case 0x5F: ret = logprint(mc, " 'Invalid message, unspecified'"); break;
case 0x60: ret = logprint(mc, " 'Mandatory information element is missing'"); break;
case 0x61: ret = logprint(mc, " 'Message type non-existent or not implemented'"); break;
case 0x62: ret = logprint(mc, " 'Message not compatible with call state or message type non-existent or not implemented'"); break;
case 0x63: ret = logprint(mc, " 'Information element non-existent or not implemented'"); break;
case 0x64: ret = logprint(mc, " 'Invalid information element contents'"); break;
case 0x65: ret = logprint(mc, " 'Message not compatible with call state'"); break;
case 0x66: ret = logprint(mc, " 'Recovery on timer expiry'"); break;
case 0x6F: ret = logprint(mc, " 'Protocol error, unspecified'"); break;
case 0x7F: ret = logprint(mc, " 'Interworking, unspecified'"); break;
default:
ret = logprint(mc, " 'unknown cause (%d)'", c);
break;
}
if (ret < 0)
return -1;
} else {
if (0 > logprint(mc, " no Value"))
return -1;
return 0;
}
if (l) {
if (0 > logprint(mc, " diagnostic:"))
return -1;
loghex(mc, p, l, 0);
}
return 0;
}
static int log_inhex(struct mController *mc, char *head, unsigned char *p, int len)
{
if (head)
if (0 > logprint(mc, "%s", head))
return -1;
loghex(mc, p, len, 0);
return 0;
}
static int log_call_state(struct mController *mc, struct l3_msg *l3m)
{
unsigned char s, *p = l3m->call_state;
int ret, c, st;
p++;
c = (*p & 0x60) >> 5;
st = *p & 0x1f;
ret = log_coding(mc, "callState", c, -1);
if (ret < 0)
return -1;
if (mc->protocol == ISDN_P_NT_S0 || mc->protocol == ISDN_P_NT_E1)
s = 'N';
else
s = 'U';
if (st > 0 && st < 26)
ret = logprint(mc, " %c%d", s, st);
else if (st == 0) {
if (l3m->pid == MISDN_PID_GLOBAL)
ret = logprint(mc, " REST 0");
else
ret = logprint(mc, " %c0", s);
} else if (l3m->pid != MISDN_PID_GLOBAL)
ret = logprint(mc, " unknown state %c%d", s, st);
else if (st == 0x1d || st == 0x1e)
ret = logprint(mc, " REST %d", st & 3);
else
ret = logprint(mc, " unknown state REST %d", st);
return ret;
}
static int log_channel_id(struct mController *mc, struct l3_msg *l3m)
{
unsigned char ift, excl, dch, *p = l3m->channel_id;
char *cu;
int ret, ch, st, l = *p++;
ift = *p & 0x20;
excl = *p & 0x08;
dch = *p & 0x04;
ch = *p & 0x3;
l--;
ret = log_coding(mc, "channelID", -1, -1);
if (ret < 0)
return -1;
ret = logprint(mc, " %s,%s", ift ? "primary" : "basic", excl ? "exclusiv" : "prefered");
if (ret < 0)
return -1;
if (dch) {
ret = logprint(mc, ",D channel");
} else if (!ift) {
switch(ch) {
case 0:
ret = logprint(mc, ",no channel");
break;
case 1:
case 2:
ret = logprint(mc, ",B%d channel", ch);
break;
case 3:
ret = logprint(mc, ",any channel");
break;
}
} else if (l < 2) {
p++;
l--;
st = *p++;
ch = *p;
ret = log_coding(mc, NULL, (*p & 0x60) >> 5, -1);
if (ret < 0)
return -1;
switch(st & 0xf) {
case 3:
cu = ",B";
break;
case 6:
cu = ",H0";
break;
case 8:
cu = ",H11";
break;
case 9:
cu = ",H12";
break;
default:
cu = ",unknown channel units";
break;
}
ret = logprint(mc, " %s %d", cu, ch & 0x7f);
} else
ret = logprint(mc, " channelIE too short");
return ret;
}
struct _lookup_table {
int v;
const char *s;
};
static struct _lookup_table _asn1_operations[] = {
{Fac_MaliciousCallId, "MaliciousCallId"},
{Fac_Begin3PTY, "Begin3PTY"},
{Fac_End3PTY, "End3PTY"},
{Fac_ActivationDiversion, "ActivationDiversion"},
{Fac_DeactivationDiversion, "DeactivationDiversion"},
{Fac_ActivationStatusNotificationDiv, "ActivationStatusNotificationDiv"},
{Fac_DeactivationStatusNotificationDiv, "DeactivationStatusNotificationDiv"},
{Fac_InterrogationDiversion, "InterrogationDiversion"},
{Fac_DiversionInformation, "DiversionInformation"},
{Fac_CallDeflection, "CallDeflection"},
{Fac_CallRerouteing, "CallRerouteing"},
{Fac_DivertingLegInformation2, "DivertingLegInformation2"},
{Fac_InterrogateServedUserNumbers, "InterrogateServedUserNumbers"},
{Fac_DivertingLegInformation1, "DivertingLegInformation1"},
{Fac_DivertingLegInformation3, "DivertingLegInformation3"},
{Fac_ChargingRequest, "ChargingRequest"},
{Fac_AOCSCurrency, "AOCSCurrency"},
{Fac_AOCSSpecialArr, "AOCSSpecialArr"},
{Fac_AOCDCurrency, "AOCDCurrency"},
{Fac_AOCDChargingUnit, "AOCDChargingUnit"},
{Fac_AOCECurrency, "AOCECurrency"},
{Fac_AOCEChargingUnit, "AOCEChargingUnit"},
{Fac_EctExecute, "EctExecute"},
{Fac_ExplicitEctExecute, "ExplicitEctExecute"},
{Fac_RequestSubaddress, "RequestSubaddress"},
{Fac_SubaddressTransfer, "SubaddressTransfer"},
{Fac_EctLinkIdRequest, "EctLinkIdRequest"},
{Fac_EctInform, "EctInform"},
{Fac_EctLoopTest, "EctLoopTest"},
{Fac_StatusRequest, "StatusRequest"},
{Fac_CallInfoRetain, "CallInfoRetain"},
{Fac_CCBSRequest, "CCBSRequest"},
{Fac_CCBSDeactivate, "CCBSDeactivate"},
{Fac_CCBSInterrogate, "CCBSInterrogate"},
{Fac_CCBSErase, "CCBSErase"},
{Fac_CCBSRemoteUserFree, "CCBSRemoteUserFree"},
{Fac_CCBSCall, "CCBSCall"},
{Fac_CCBSStatusRequest, "CCBSStatusRequest"},
{Fac_CCBSBFree, "CCBSBFree"},
{Fac_EraseCallLinkageID, "EraseCallLinkageID"},
{Fac_CCBSStopAlerting, "CCBSStopAlerting"},
{Fac_CCBS_T_Request, "CCBS_T_Request"},
{Fac_CCBS_T_Call, "CCBS_T_Call"},
{Fac_CCBS_T_Suspend, "CCBS_T_Suspend"},
{Fac_CCBS_T_Resume, "CCBS_T_Resume"},
{Fac_CCBS_T_RemoteUserFree, "CCBS_T_RemoteUserFree"},
{Fac_CCBS_T_Available, "CCBS_T_Available"},
{Fac_CCNRRequest, "CCNRRequest"},
{Fac_CCNRInterrogate, "CCNRInterrogate"},
{Fac_CCNR_T_Request, "CCNR_T_Request"},
{-1, "unknown operation"}
};
static const char *get_asn1_operation_string(int val)
{
struct _lookup_table *item = _asn1_operations;
while(item->v != -1) {
if (item->v == val)
break;
item++;
}
return item->s;
}
static int log_facility(struct mController *mc, struct l3_msg *l3m, int idx)
{
unsigned char *p = l3m->facility;
char *cp, *ops;
const char *opstr;
int id, op, ret = 0, l = *p++;
struct asn1_parm ap;
if (0 > logprint(mc, " facilityIE:"))
return -1;
ret = decodeFac(l3m->facility, &ap);
if (ret < 0) {
if (0 > logprint(mc, " error %d on decode", ret))
return -1;
if (mc->layer3[idx] & l3vHEX)
ret = logprint(mc, " not dumped ");
else
loghex(mc, p, l, 4);
} else {
ret = logprint(mc, " decode OK %s", ap.Valid ? "and valid": "but invalid");
if (ret < 0)
return -1;
if (ap.Valid) {
cp = "unknown";
ops = cp;
id = -1;
op = -1;
opstr = "";
switch(ap.comp) {
case CompInvoke:
cp = "Invoke";
id = ap.u.inv.invokeId;
op = ap.u.inv.operationValue;
ops = "operation";
opstr = get_asn1_operation_string(op);
break;
case CompReturnResult:
cp = "ReturnResult";
id = ap.u.retResult.invokeId;
op = ap.u.retResult.operationValue;
opstr = get_asn1_operation_string(op);
ops = "operation";
break;
case CompReturnError:
cp = "ReturnError";
id = ap.u.retError.invokeId;
op = ap.u.retError.errorValue;
ops = "error";
break;
case CompReject:
cp = "Reject";
id = ap.u.reject.invokeId;
op = ap.u.reject.problemValue;
ops = "problem";
break;
}
ret = logprint(mc, " %s id=%d %s %x %s", cp, id, ops, op, opstr);
}
}
return ret;
}
static int log_progress(struct mController *mc, struct l3_msg *l3m)
{
unsigned char *p = l3m->progress;
int ret, c, loc, l = *p++;
c = (*p & 0x60) >> 5;
loc = *p & 0xf;
ret = log_coding(mc, "progress", c, loc);
if (ret < 0)
return -1;
if (!l)
ret = logprint(mc, " too short");
else {
p++;
switch(*p & 0x7f) {
case 1:
ret = logprint(mc, "'Call is not end-to-end ISDN: further progress information may be available in-band'");
break;
case 2:
ret = logprint(mc, "'Destination address is non-ISDN'");
break;
case 3:
ret = logprint(mc, "'Origination address is non-ISDN'");
break;
case 4:
ret = logprint(mc, "'Call has returned to the ISDN'");
break;
case 5:
ret = logprint(mc, "'Interworking has occurred and has resulted in a telecommunication service change'");
break;
case 8:
ret = logprint(mc, "'In-band information or appropriate pattern now available'");
break;
default:
ret = logprint(mc, "'unknown progress description: %02x'", *p & 0x7f);
break;
}
}
return ret;
}
static int log_net_fac(struct mController *mc, struct l3_msg *l3m)
{
unsigned char *p = l3m->net_fac;
int ret = 0, t, pl, il, l = *p++;
il = *p++;
l--;
ret = logprint(mc, " networkSpecificFacility: ");
if (ret < 0)
return -1;
if (il) {
t = (*p & 70) >> 4;
pl = *p & 0xf;
il--;
l--;
ret = logprint(mc, "networkidentification:%d plan:%d id:", t, pl);
if (ret < 0)
return -1;
p++;
while (il && l) {
ret = logprint(mc, "%c", *p++ & 0x7f);
if (ret < 0)
return -1;
il--;
l--;
}
}
if (l)
loghex(mc, p, l, 0);
return ret;
}
static int log_notify(struct mController *mc, struct l3_msg *l3m)
{
unsigned char *p = l3m->notify;
int ret = 0;
p++;
switch(*p & 0x7f) {
case 0:
ret = logprint(mc, " notify:'user suspended'");
break;
case 1:
ret = logprint(mc, " notify:'user resumed'");
break;
case 2:
ret = logprint(mc, " notify:'bearer service changed'");
break;
default:
ret = logprint(mc, " notify:'unknown description: %02x'", *p & 0x7f);
break;
}
return ret;
}
static int log_ia5_chars(struct mController *mc, char *head, unsigned char *p, int len)
{
int ret = 0;
if (head) {
ret = logprint(mc, "%s", head);
if (ret < 0)
return -1;
}
while (len) {
ret = logprint(mc, "%c", *p++ & 0x7f);
if (ret < 0)
return -1;
len--;
}
return ret;
}
static int log_date(struct mController *mc, struct l3_msg *l3m)
{
unsigned char *p = l3m->date;
int ret = 0, l = *p++;
if (l > 5)
ret = logprint(mc, " date:'%02d.%02d.%02d %02d:%02d:%02d'", p[0], p[1], p[2], p[3], p[4], p[5]);
else
ret = logprint(mc, " date:'%02d.%02d.%02d %02d:%02d'", p[0], p[1], p[2], p[3], p[4]);
return ret;
}
static int log_number(struct mController *mc, char *head, unsigned char *p)
{
char *ts, *ps;
int ret = 0, t, pl, len = *p++;
if (head) {
ret = logprint(mc, "%s", head);
if (ret < 0)
return -1;
}
t = (*p & 0x70) >> 4;
pl = *p & 0x0f;
switch(t) {
case 0:
ts = "Unknown";
break;
case 1:
ts = "International";
break;
case 2:
ts = "National";
break;
case 3:
ts = "Network specific";
break;
case 4:
ts = "Subscriber";
break;
case 6:
ts = "Abbreviated";
break;
case 7:
ts = "reserved for ext.";
break;
default:
ts = "reserved";
break;
}
switch(pl) {
case 0:
ps = "Unknown";
break;
case 1:
ps = "ISDN/Telephony";
break;
case 3:
ps = "Data";
break;
case 4:
ps = "Telex";
break;
case 8:
ps = "National";
break;
case 9:
ps = "Private";
break;
case 15:
ps = "reserved for ext.";
break;
default:
ps = "reserved";
break;
}
ret = logprint(mc, "Type:%s Plan:%s ", ts, ps);
if (ret < 0)
return -1;
len--;
if (len && !(*p & 0x80)) {
/* TODO handle reason with IE_REDIRECTING_NR */
len--;
p++;
t = (*p & 0x60) >> 5;
pl = *p & 3;
switch(t) {
case 0:
ts = "presentation:allowed";
break;
case 1:
ts = "presentation:restricted";
break;
case 2:
ts = "number not available";
break;
case 3:
ts = "reserved";
break;
}
switch(pl) {
case 0:
ps = "user provided,not screened";
break;
case 1:
ps = "user provided,verified and passed";
break;
case 2:
ps = "user provided,verified and failed";
break;
case 3:
ps = "network provided";
break;
}
ret = logprint(mc, "%s,%s ", ts, ps);
if (ret < 0)
return -1;
}
p++;
while (len > 0) {
ret = logprint(mc, "%c", *p++ & 0x7f);
if (ret < 0)
return -1;
len--;
}
return ret;
}
static int log_signal(struct mController *mc, struct l3_msg *l3m)
{
unsigned char *p = l3m->signal;
int ret = 0;
p++;
switch(*p) {
case 0:
ret = logprint(mc, " signal:'dial tone on'");
break;
case 1:
ret = logprint(mc, " signal:'ringback tone on'");
break;
case 2:
ret = logprint(mc, " signal:'intercept tone on'");
break;
case 3:
ret = logprint(mc, " signal:'network congestion tone on'");
break;
case 4:
ret = logprint(mc, " signal:'busy tone on'");
break;
case 5:
ret = logprint(mc, " signal:'confirm tone on'");
break;
case 6:
ret = logprint(mc, " signal:'answer tone on'");
break;
case 7:
ret = logprint(mc, " signal:'call waiting tone on'");
break;
case 8:
ret = logprint(mc, " signal:'off-hook warning tone on'");
break;
case 0x3f:
ret = logprint(mc, " signal:'tones off'");
break;
case 0x40:
ret = logprint(mc, " signal:'alerting on - pattern 0'");
break;
case 0x41:
ret = logprint(mc, " signal:'alerting on - pattern 1'");
break;
case 0x42:
ret = logprint(mc, " signal:'alerting on - pattern 2'");
break;
case 0x43:
ret = logprint(mc, " signal:'alerting on - pattern 3'");
break;
case 0x44:
ret = logprint(mc, " signal:'alerting on - pattern 4'");
break;
case 0x45:
ret = logprint(mc, " signal:'alerting on - pattern 5'");
break;
case 0x46:
ret = logprint(mc, " signal:'alerting on - pattern 6'");
break;
case 0x47:
ret = logprint(mc, " signal:'alerting on - pattern 7'");
break;
case 0x4f:
ret = logprint(mc, " signal:'alerting off'");
break;
default:
ret = logprint(mc, " signal:'unknown codse: %02x'", *p);
break;
}
return ret;
}
static void logIE(struct mController *mc, struct mbuffer *mb, int mtIndex)
{
struct l3_msg *l3m = &mb->l3;
if (l3m->comprehension_req)
if (0 > logprint(mc, " comprehensionRequired"))
return;
if (l3m->more_data)
if (0 > logprint(mc, " moreData"))
return;
if (l3m->sending_complete)
if (0 > logprint(mc, " sendingComplete"))
return;
if (l3m->congestion_level)
if (0 > logprint(mc, " congestionLevel=%d", l3m->congestion_level & 0xf))
return;
if (l3m->bearer_capability && *l3m->bearer_capability)
if (0 > log_bearer_capability(mc, l3m))
return;
if (l3m->cause && *l3m->cause)
if (0 > log_cause(mc, l3m))
return;
if (l3m->call_id && *l3m->call_id)
if (0 > log_inhex(mc, " callID:", l3m->call_id + 1, *l3m->call_id))
return;
if (l3m->call_state && *l3m->call_state)
if (0 > log_call_state(mc, l3m))
return;
if (l3m->channel_id && *l3m->channel_id)
if (0 > log_channel_id(mc, l3m))
return;
if (l3m->facility && *l3m->facility)
if (0 > log_facility(mc, l3m, mtIndex))
return;
if (l3m->progress && *l3m->progress)
if (0 > log_progress(mc, l3m))
return;
if (l3m->net_fac && *l3m->net_fac)
if (0 > log_net_fac(mc, l3m))
return;
if (l3m->notify && *l3m->notify)
if (0 > log_notify(mc, l3m))
return;
if (l3m->display && *l3m->display)
if (0 > log_ia5_chars(mc, " display:", l3m->display + 1, *l3m->display))
return;
if (l3m->date && *l3m->date)
if (0 > log_date(mc, l3m))
return;
if (l3m->keypad && *l3m->keypad)
if (0 > log_ia5_chars(mc, " keypad:", l3m->keypad + 1, *l3m->keypad))
return;
if (l3m->signal && *l3m->signal)
if (0 > log_signal(mc, l3m))
return;
if (l3m->info_rate && *l3m->info_rate)
if (0 > log_inhex(mc, " info_rate:", l3m->info_rate + 1, *l3m->info_rate))
return;
if (l3m->end2end_transit && *l3m->end2end_transit)
if (0 > log_inhex(mc, " end2end_transit:", l3m->end2end_transit + 1, *l3m->end2end_transit))
return;
if (l3m->transit_delay_sel && *l3m->transit_delay_sel)
if (0 > log_inhex(mc, " transit_delay_sel:", l3m->transit_delay_sel + 1, *l3m->transit_delay_sel))
return;
if (l3m->pktl_bin_para && *l3m->pktl_bin_para)
if (0 > log_inhex(mc, " pktl_bin_para:", l3m->pktl_bin_para + 1, *l3m->pktl_bin_para))
return;
if (l3m->pktl_window && *l3m->pktl_window)
if (0 > log_inhex(mc, " pktl_window:", l3m->pktl_window + 1, *l3m->pktl_window))
return;
if (l3m->pkt_size && *l3m->pkt_size)
if (0 > log_inhex(mc, " pkt_size:", l3m->pkt_size + 1, *l3m->pkt_size))
return;
if (l3m->closed_userg && *l3m->closed_userg)
if (0 > log_inhex(mc, " closed_userg:", l3m->closed_userg + 1, *l3m->closed_userg))
return;
if (l3m->reverse_charge && *l3m->reverse_charge)
if (0 > log_inhex(mc, " reverse_charge:", l3m->reverse_charge + 1, *l3m->reverse_charge))
return;
if (l3m->connected_nr && *l3m->connected_nr)
if (0 > log_number(mc, " connected number:", l3m->connected_nr))
return;
if (l3m->calling_nr && *l3m->calling_nr)
if (0 > log_number(mc, " calling number:", l3m->calling_nr))
return;
if (l3m->called_nr && *l3m->called_nr)
if (0 > log_number(mc, " called number:", l3m->called_nr))
return;
if (l3m->redirecting_nr && *l3m->redirecting_nr)
if (0 > log_number(mc, " redirecting number:", l3m->redirecting_nr))
return;
if (l3m->redirection_nr && *l3m->redirection_nr)
if (0 > log_number(mc, " redirection number:", l3m->redirection_nr))
return;
#if 0
/* TODO implement verbose logging */
unsigned char *connected_sub; /* ie 0x4d pos 22 */
unsigned char *calling_sub; /* ie 0x6d pos 24 */
unsigned char *called_sub; /* ie 0x71 pos 26 */
unsigned char *transit_net_sel; /* ie 0x78 pos 29 */
unsigned char *restart_ind; /* ie 0x79 pos 30 */
unsigned char *llc; /* ie 0x7c pos 31 */
unsigned char *hlc; /* ie 0x7d pos 32 */
unsigned char *useruser; /* ie 0x7e pos 33 */
struct m_extie extra[8];
#endif
}
static int log_layer3(struct mController *mc, struct mbuffer *mb)
{
unsigned char *dp;
char *ip, *sp = mc->lp;
int len, ret, idx = 0;
dp = mb->data;
len = mb->len;
if (*dp != Q931_PD) {
logprint(mc, " Unknown L3 protocol found 0x%02x\n", *dp);
return 0;
}
ret = parseQ931(mb);
if (ret) {
logprint(mc, " Error %x parsing L3 message", ret);
return 0;
}
if (mb->l3h.crlen == 0)
logprint(mc, " CRef=Dummy");
else if ((mb->l3h.cr & 0x7fff) == 0)
logprint(mc, " CRef(%c)=Global", mb->l3h.cr & MISDN_PID_CR_FLAG ? 'A' : 'O');
else
logprint(mc, " CRef(%c)=(%d)", mb->l3h.cr & MISDN_PID_CR_FLAG ? 'A' : 'O', mb->l3h.cr & 0x7fff);
ip = strchr(MTidx, mb->l3.type);
if (ip)
idx = 1 + (ip - MTidx);
if (mc->layer3[idx]) {
if (ip)
logprint(mc, " %s", mTypesStr[idx]);
else
logprint(mc, " %s(%02x)", mTypesStr[idx], mb->l3.type);
if (mc->layer3[idx] & l3vVERBOSE)
logIE(mc, mb, idx);
if (mc->layer3[idx] & l3vHEX)
loghex(mc, dp, len, 4);
} else {
mc->lp = sp;
*sp = 0;
}
return 0;
}
static int log_teimanagment(struct mController *mc, struct mbuffer *mb)
{
int ri, mt, ai;
unsigned char *dp;
dp = msg_pull(mb, 1);
if (!IsUI(dp)) {
logprint(mc, " not UI but %02x frame for TEI managment procedures", *dp);
} else {
if (mb->len < 5) {
logprint(mc, " TEI managment UI frame too short only %d from 5 bytes", mb->len);
} else {
dp = msg_pull(mb, 5);
if (*dp != 0xf) {
logprint(mc, " wrong layer managment entity %02x for TEI managment procedures", *dp);
} else {
dp++;
ri = *dp++;
ri <<= 8;
ri |= *dp++;
mt = *dp++;
ai = *dp >> 1;
switch (mt) {
case 1:
logprint(mc, " TEI Identity request ri=%d ai=%d", ri, ai);
break;
case 2:
logprint(mc, " TEI Identity assigned ri=%d TEI=%d", ri, ai);
break;
case 3:
if (ai == 127)
logprint(mc, " TEI Identity denied ri=%d - no TEI available", ri);
else
logprint(mc, " TEI Identity denied ri=%d TEI=%d", ri, ai);
break;
case 4:
if (ai == 127)
logprint(mc, " TEI Identity check request for all TEI");
else
logprint(mc, " TEI Identity check request for TEI %d", ai);
break;
case 5: /* TODO multi tei report ? */
logprint(mc, " TEI Identity check response ri=%d TEI %d in use", ri, ai);
break;
case 6:
if (ai == 127)
logprint(mc, " TEI Identity remove for all TEI");
else
logprint(mc, " TEI Identity remove for TEI %d", ai);
break;
case 7:
logprint(mc, " TEI Identity verify for TEI %d", ai);
break;
default:
logprint(mc, " Invalid TEI managment type %d ri=%d ai=%d", mt, ri, ai);
return 0;
}
if (!(mc->layer2 & l2vTEI)) /* only report TEI assignmen if requested */
mc->lp = mc->logtxt;
}
}
}
return 0;
}
static int log_layer2(struct mController *mc, struct mbuffer *mb)
{
unsigned char *dp, *savep;
int ret = 0, savelen, psapi, ptei, cr, nr, ns, origin, cmd;
char *sp, pf;
if (mb->len < 3) {
logprint(mc, "L2 frame too short only %d bytes\n", mb->len);
return 0;
}
sp = mc->lp;
savep = mb->data;
savelen = mb->len;
dp = msg_pull(mb, 2);
psapi = *dp++;
ptei = *dp++;
if ((psapi & 1) || !(ptei & 1)) {
logprint(mc, "L2 frame wrong EA0/EA1 %02x/%02x\n", psapi, ptei);
return 0;
}
cr = psapi & 2;
psapi >>= 2;
ptei >>= 1;
if (mc->protocol == ISDN_P_NT_S0 || mc->protocol == ISDN_P_NT_E1)
origin = mb->h->prim == PH_DATA_REQ ? 0 : 1;
else
origin = ((mb->h->prim == PH_DATA_REQ) || (mb->h->prim == PH_DATA_E_IND)) ? 1 : 0;
switch ((origin | cr) & 3) {
case 0:
case 3:
cmd = 0;
break;
case 1:
case 2:
cmd = 1;
break;
}
if (origin)
logprint(mc, "User>Net %s", cmd ? "Cmd" : "Rsp");
else
logprint(mc, "Net>User %s", cmd ? "Cmd" : "Rsp");
if (psapi == TEI_SAPI) {
if (ptei == 127) {
if (cmd)
ret = log_teimanagment(mc, mb);
else
logprint(mc, " TEI managment frame is not a command frame");
} else
logprint(mc, " Wrong TEI %d for TEI managment procedures", ptei);
return ret;
} else if (psapi == CTRL_SAPI) {
logprint(mc, " TEI=%d", ptei);
sp = mc->lp;
} else {
if (mc->layer2 & l2vSAPI)
wprint("Controller%d: L2 frame for SAPI %d TEI %d\n", mc->mNr, psapi, ptei);
return 0;
}
dp = msg_pull(mb, 1);
if (!(*dp & 1)) { /* I-Frame */
nr = *dp++ >> 1;
ns = *dp >> 1;
pf = *dp & 1 ? 'P' : ' ';
logprint(mc, " %c I N(R)=%d N(S)=%d", pf, nr, ns);
msg_pull(mb, 1);
sp = mc->lp;
ret = log_layer3(mc, mb);
} else if (IsUI(dp)) {
pf = *dp & 0x10 ? 'P' : ' ';
logprint(mc, " %c UI", pf);
sp = mc->lp;
ret = log_layer3(mc, mb);
} else {
if (IsRR(dp)) {
if (mc->layer2 & l2vKEEPALIVE) {
dp++;
nr = *dp >> 1;
if (*dp & 1)
pf = cmd ? 'P' : 'F';
else
pf = ' ';
logprint(mc, " %c RR N(R)=%d", pf, nr);
}
} else if (IsRNR(dp)) {
if (mc->layer2 & l2vKEEPALIVE) {
dp++;
nr = *dp >> 1;
if (*dp & 1)
pf = cmd ? 'P' : 'F';
else
pf = ' ';
logprint(mc, " %c RNR N(R)=%d", pf, nr);
}
} else if (IsREJ(dp)) {
if (mc->layer2 & l2vKEEPALIVE) {
dp++;
nr = *dp >> 1;
if (*dp & 1)
pf = cmd ? 'P' : 'F';
else
pf = ' ';
logprint(mc, " %c REJ N(R)=%d", pf, nr);
}
} else if (IsSABME(dp)) {
if (mc->layer2 & (l2vCONTROL | l2vKEEPALIVE)) {
pf = *dp & 0x10 ? 'P' : ' ';
logprint(mc, " %c SABME", pf);
}
} else if (IsUA(dp)) {
if (mc->layer2 & (l2vCONTROL | l2vKEEPALIVE)) {
pf = *dp & 0x10 ? 'F' : ' ';
logprint(mc, " %c UA", pf);
}
} else if (IsDISC(dp)) {
if (mc->layer2 & (l2vCONTROL | l2vKEEPALIVE)) {
pf = *dp & 0x10 ? 'P' : ' ';
logprint(mc, " %c DISC", pf);
}
} else if (IsDM(dp)) {
if (mc->layer2 & (l2vCONTROL | l2vKEEPALIVE)) {
pf = *dp & 0x10 ? 'F' : ' ';
logprint(mc, " %c DM", pf);
}
} else if (IsFRMR(dp)) {
if (mc->layer2) {
logprint(mc, " FRMR");
}
} else {
if (mc->layer2) {
logprint(mc, " Unknown L2 type %02x", *dp);
}
}
}
if (mc->lp == sp)
mc->lp = &mc->logtxt[0];
mb->data = savep;
mb->len = savelen;
return ret;
}
static int log_layer1(struct mController *mc, struct mbuffer *mb)
{
int ret = 0;
char *pn = NULL;
switch (mb->h->prim) {
case PH_DATA_E_IND:
if (!mc->echo) { /* skip PH_DATA_E_IND if not expected */
dprint(mlDEBUG_MESSAGE, "Controller%d unexpected PH_DATA_E_IND recived\n", mc->mNr);
} else
ret = log_layer2(mc, mb);
break;
case PH_DATA_IND:
ret = log_layer2(mc, mb);
break;
case PH_DATA_REQ:
if (!mc->echo) /* skip PH_DATA_REQ if PH_DATA_E_IND are expected */
ret = log_layer2(mc, mb);
break;
case PH_ACTIVATE_IND:
pn = "ACTIVATE IND";
break;
case PH_DEACTIVATE_IND:
pn = "DEACTIVATE IND";
break;
case PH_DATA_CNF:
case PH_DEACTIVATE_CNF:
case PH_ACTIVATE_REQ:
case PH_ACTIVATE_CNF:
case PH_DEACTIVATE_REQ:
case MPH_ACTIVATE_IND:
case MPH_ACTIVATE_REQ:
case MPH_INFORMATION_REQ:
case MPH_DEACTIVATE_IND:
case MPH_DEACTIVATE_REQ:
case MPH_INFORMATION_IND:
case PH_CONTROL_REQ:
case PH_CONTROL_IND:
case PH_CONTROL_CNF:
/* ignored */
break;
default:
logprint(mc, "Unknown L1 message len=%d %04x received\n", mb->len, mb->h->prim);
break;
}
if (pn && mc->layer1) {
logprint(mc, "Layer1 %s", pn);
}
return ret;
}
static int read_socket(struct mController *mc)
{
int result;
struct msghdr mh;
struct iovec iov[1];
struct ctstamp cts;
struct mbuffer *mb;
mh.msg_name = NULL;
mh.msg_namelen = 0;
mh.msg_iov = iov;
mh.msg_iovlen = 1;
mh.msg_control = &cts;
mh.msg_controllen = sizeof(cts);
mh.msg_flags = 0;
mb = alloc_mbuffer();
if (!mb) {
eprint("Cannot allocate mbuffer - aborting\n");
return -1;
}
iov[0].iov_base = mb->head;
iov[0].iov_len = MBUFFER_DATA_SIZE;
result = recvmsg(mc->socket, &mh, 0);
if (result < 0) {
eprint("read error %s\n", strerror(errno));
free_mbuffer(mb);
} else {
mc->lp = &mc->logtxt[0];
mb->len = result;
msg_pull(mb, MISDN_HEADER_LEN);
if (mh.msg_flags) {
dprint(mlDEBUG_MESSAGE, "received message with msg_flags(%x)\n", mh.msg_flags);
}
if (cts.cmsg_type == MISDN_TIME_STAMP)
mc->tv = &cts.tv;
else
mc->tv = NULL;
result = log_layer1(mc, mb);
log_output(mc);
if (!result)
free_mbuffer(mb);
}
return result;
}
static int main_loop(int enabled)
{
int ret, rc, result = 0;
int i, idx = 0;
int polldelay;
struct mController *pc, **pollContr = alloca(enabled * sizeof(struct mController *));
struct pollfd *polls = alloca(enabled * sizeof(struct pollfd));
memset(polls, 0, enabled * sizeof(struct pollfd));
for (i = 0; i < mI_ControllerCount; i++) {
pc = &mI_Controller[i];
if (!pc->enable)
continue;
polls[idx].fd = pc->socket;
polls[idx].events = POLLIN | POLLPRI;
pollContr[idx] = pc;
wprint("poll setup %d fd %d events %x\n", idx, polls[idx].fd, polls[idx].events);
idx++;
if (idx >= enabled)
break;
}
if (idx != enabled) {
eprint("internal error %d controller enabled, but %d added to poll -aborted\n", enabled, idx);
return -1;
}
polldelay = -1;
running = 1;
while (running) {
ret = poll(polls, enabled, polldelay);
dprint(mlDEBUG_POLL, "poll ret=%d\n", ret);
if (ret < 0) {
if (errno == EINTR)
dprint(mlDEBUG_POLL, "Received signal - continue\n");
else {
wprint("Error on poll errno=%d - %s\n", errno, strerror(errno));
}
continue;
}
if (ret == 0) { /* timeout never */
running = 0;
wprint("Error poll timeout - should never happen\n");
continue;
}
for (i = 0; i < enabled; i++) {
if (ret && polls[i].revents) {
pc = pollContr[i];
rc = read_socket(pc);
if (rc < 0) {
running = 0;
break;
}
ret--;
}
if (ret == 0)
break;
}
}
return result;
}
int main(argc, argv)
int argc;
char *argv[];
{
int result;
int opt;
struct mISDNversion ver;
int i, j;
int exitcode = -1;
int _L3init = 0;
int mIsock = -1;
struct mController *pc;
int channel;
int nrEnabled;
config_file = def_config;
result = opt_parse(argc, argv);
if (result)
exit(1);
if (ml_debugmask)
mISDN_set_debug_level(ml_debugmask << 24);
mI_ControllerCount = 0;
mIsock = socket(PF_ISDN, SOCK_RAW, ISDN_P_BASE);
if (mIsock < 0) {
fprintf(stderr, "mISDNv2 not installed - %s\n", strerror(errno));
return 1;
}
result = ioctl(mIsock, IMGETVERSION, &ver);
if (result < 0) {
fprintf(stderr, "ioctl error %s\n", strerror(errno));
return 1;
}
if (ver.release & MISDN_GIT_RELEASE)
printf("mISDN kernel version %d.%02d.%d (git.misdn.eu) found\n", ver.major, ver.minor,
ver.release & ~MISDN_GIT_RELEASE);
else
printf("mISDN kernel version %d.%02d.%d found\n", ver.major, ver.minor, ver.release);
printf("mISDN user version %d.%02d.%d found\n", MISDN_MAJOR_VERSION, MISDN_MINOR_VERSION, MISDN_RELEASE);
if (ver.major != MISDN_MAJOR_VERSION) {
fprintf(stderr, "VERSION incompatible please update\n");
goto errout;
}
/* get number of stacks */
result = ioctl(mIsock, IMGETCOUNT, &mI_ControllerCount);
if (result < 0) {
fprintf(stderr, "mISDNv2 IMGETCOUNT error - %s\n", strerror(errno));
goto errout;
}
if (mI_ControllerCount < 1) {
fprintf(stderr, "mISDNv2 no controllers found\n");
goto errout;
}
defController = calloc(mI_ControllerCount + 1, sizeof(*mI_Controller));
if (!defController) {
fprintf(stderr, "no memory to allocate %d controller struct (a %zd)\n",
mI_ControllerCount, sizeof(*mI_Controller));
goto errout;
}
mI_Controller = &defController[1];
result = read_config_file(config_file);
if (result < 0) {
fprintf(stderr, "Error while proccessing config file %s - aborting\n", config_file);
goto errout;
}
for (i = 0; i < mI_ControllerCount; i++) {
pc = &mI_Controller[i];
pc->devinfo.id = i;
pc->socket = -1;
result = ioctl(mIsock, IMGETDEVINFO, &pc->devinfo);
if (result < 0) {
fprintf(stderr, "mISDNv2 IMGETDEVINFO error controller %d - %s\n", i + 1, strerror(errno));
goto errout;
}
printf(" id: %d\n", pc->devinfo.id);
printf(" Dprotocols: %08x\n", pc->devinfo.Dprotocols);
printf(" Bprotocols: %08x\n", pc->devinfo.Bprotocols);
printf(" protocol: %d\n", pc->devinfo.protocol);
printf(" channelmap: ");
for (j = MISDN_CHMAP_SIZE - 1; j >= 0; j--)
printf("%02x", pc->devinfo.channelmap[j]);
printf("\n");
printf(" nrbchan: %d\n", pc->devinfo.nrbchan);
printf(" name: %s\n", pc->devinfo.name);
if (pc->devinfo.protocol == ISDN_P_NONE)
pc->protocol = ISDN_P_TE_S0;
else
pc->protocol = pc->devinfo.protocol;
}
nrEnabled = 0;
for (i = 0; i < mI_ControllerCount; i++) {
pc = &mI_Controller[i];
if (!pc->enable)
continue;
pc->socket = socket(PF_ISDN, SOCK_DGRAM, pc->protocol);
if (pc->socket < 0) {
fprintf(stderr, "mISDNv2 cannot open a mISDN socket for controller %d protocol %d\n", i + 1, pc->protocol);
goto errout;
}
pc->addr.family = AF_ISDN;
pc->addr.dev = pc->mNr;
/* try echo channel first if enabled */
channel = pc->echo;
while (channel >= 0) {
pc->addr.channel = (unsigned char)channel;
result = bind(pc->socket, (struct sockaddr *)&pc->addr, sizeof(pc->addr));
if (result < 0) {
if (channel == 0) {
fprintf(stderr, "mISDNv2 controller %d cannot bind to socket\n", i + 1);
goto errout;
}
close(pc->socket);
pc->socket = socket(PF_ISDN, SOCK_DGRAM, pc->protocol);
if (pc->socket < 0) {
fprintf(stderr, "mISDNv2 cannot open socket again for controller %d protocol %d\n",
i + 1, pc->protocol);
goto errout;
}
channel--;
} else
break;
}
pc->echo = (pc->addr.channel == 1);
opt = 1;
result = setsockopt(pc->socket, SOL_MISDN, MISDN_TIME_STAMP, &opt, sizeof(opt));
if (result < 0) {
/* none fatal */
fprintf(stderr, "mISDNv2 controller %d setsockopt error %s\n", i + 1, strerror(errno));
}
nrEnabled++;
}
init_layer3(2 * (1 + mI_ControllerCount), &myfn);
_L3init = 1;
iprint("mISDNv2 logger enabled %d/%d controller\n", nrEnabled, mI_ControllerCount);
if (!nrEnabled) {
fprintf(stderr, "No controllers are enabled for logging - abort\n");
goto errout;
}
dprint(mlDEBUG_BASIC, "do_daemon=%d config=%s\n", do_daemon, config_file);
if (do_daemon) {
result = daemon(0, 0);
if (result < 0) {
fprintf(stderr, "cannot run as daemon\n");
goto errout;
}
openlog("mISDNlogger", LOG_ODELAY, LOG_DAEMON);
}
if (0 > open_logfile())
goto errout;
/* setup signal handler */
memset(&mysig_term, 0, sizeof(mysig_term));
mysig_term.sa_handler = termHandler;
result = sigaction(SIGTERM, &mysig_term, NULL);
if (result) {
wprint("Error to setup signal handler for SIGTERM - %s\n", strerror(errno));
}
result = sigaction(SIGHUP, &mysig_term, NULL);
if (result) {
wprint("Error to setup signal handler for SIGHUP - %s\n", strerror(errno));
}
result = sigaction(SIGINT, &mysig_term, NULL);
if (result) {
wprint("Error to setup signal handler for SIGINT - %s\n", strerror(errno));
}
memset(&mysig_dump, 0, sizeof(mysig_dump));
mysig_dump.sa_handler = dumpHandler;
result = sigaction(SIGUSR1, &mysig_dump, NULL);
if (result) {
wprint("Error to setup signal handler for SIGUSR1 - %s\n", strerror(errno));
}
result = sigaction(SIGUSR2, &mysig_dump, NULL);
if (result) {
wprint("Error to setup signal handler for SIGUSR2 - %s\n", strerror(errno));
}
iprint("started for for %d/%d controller\n", nrEnabled, mI_ControllerCount);
exitcode = main_loop(nrEnabled);
iprint("logger mainloop ended");
errout:
if (mIsock > 0)
close(mIsock);
if (mI_Controller) {
for (i = 0; i < mI_ControllerCount; i++) {
pc = &mI_Controller[i];
if (pc->socket > 0) {
close(pc->socket);
}
}
}
if (_L3init)
cleanup_layer3();
if (defController) {
close_logfile();
free(defController);
}
return exitcode;
}