/*****************************************************************************\ ** ** ** isdnbridge ** ** ** **---------------------------------------------------------------------------** ** Copyright: Andreas Eversberg (GPL) ** ** ** ** user space utility to bridge two mISDN ports via layer 1 ** ** ** \*****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include int portcount = 0; /* counts all open ports for finding pair */ int mISDNsocket = -1; /* quit flag */ int quit = 0; /* option stuff */ int nooutput = 0; int traffic = 0; int debug = 0; pid_t dsp_pid = 0; /* mISDN port structure list */ struct mISDNport { struct mISDNport *next, *prev; int count; /* port count */ int portnum; /* port number */ int l1link; /* if l1 is available (only works with nt-mode) */ time_t l1establish; /* time until establishing after link failure */ int ntmode; /* is TRUE if port is nt mode */ int pri; /* is TRUE if port is a primary rate interface */ int d_sock; int b_num; /* number of ports */ int b_sock[256]; int b_state[256]; /* state 0 = IDLE */ unsigned char que_frm[2048]; /* queue while layer 1 is down */ int que_len; }; struct mISDNport *mISDNport_first = NULL; enum { B_STATE_IDLE, B_STATE_ACTIVATING, B_STATE_ACTIVE, B_STATE_DEACTIVATING }; /* * show state */ static void show_state(struct mISDNport *m1) { struct mISDNport *m2 = m1; if (nooutput) return; if (m1->count & 1) m1 = m1->prev; else m2 = m2->next; printf("Port %2d %s <-> Port %2d %s\n", m1->portnum, (m1->l1link)?"ACTIVE":"inactive", m2->portnum, (m2->l1link)?"ACTIVE":"inactive"); } /* * show traffic */ static void show_traffic(struct mISDNport *m1, unsigned char *data, int len) { struct mISDNport *m2 = m1; int right, i; if (nooutput) return; if (m1->count & 1) { m1 = m1->prev; right = 0; } else { m2 = m2->next; right = 1; } printf("Port %2d %s Port %2d :", m1->portnum, right?"-->":"<--", m2->portnum); i = 0; while(i < len) { printf(" %02x", data[i]); i++; } printf("\n"); } /* * debug output */ #define PDEBUG(fmt, arg...) _printdebug(__FUNCTION__, __LINE__, fmt, ## arg) static void _printdebug(const char *function, int line, const char *fmt, ...) { char buffer[4096]; va_list args; if (!debug || nooutput) return; va_start(args,fmt); vsnprintf(buffer, sizeof(buffer)-1, fmt, args); buffer[sizeof(buffer)-1]=0; va_end(args); printf("%s, line %d: %s", function, line, buffer); } /* * signal handler to interrupt main loop */ static void sighandler(int sigset) { if (sigset == SIGHUP) return; if (sigset == SIGPIPE) return; fprintf(stderr, "Signal received: %d\n", sigset); if (!quit) quit = 1; } /* * send control information to the channel (dsp-module) */ static void ph_control(int sock, int c1, int c2) { unsigned char data[MISDN_HEADER_LEN+sizeof(int)+sizeof(int)]; struct mISDNhead *hh = (struct mISDNhead *)data; int len; int *d = (int *)(data + MISDN_HEADER_LEN); hh->prim = PH_CONTROL_REQ; hh->id = 0; len = MISDN_HEADER_LEN + sizeof(unsigned long)*2; *d++ = c1; *d++ = c2; len = sendto(sock, hh, len, 0, NULL, 0); if (len <= 0) fprintf(stderr, "Failed to send to socket %d\n", sock); } void ph_control_block(int sock, int c1, void *c2, int c2_len) { unsigned char data[MISDN_HEADER_LEN+sizeof(int)+c2_len]; struct mISDNhead *hh = (struct mISDNhead *)data; int len; int *d = (int *)(data + MISDN_HEADER_LEN); hh->prim = PH_CONTROL_REQ; hh->id = 0; len = MISDN_HEADER_LEN + sizeof(unsigned long) + c2_len; *d++ = c1; memcpy(d, c2, c2_len); len = sendto(sock, hh, len, 0, NULL, 0); if (len <= 0) fprintf(stderr, "Failed to send to socket %d\n", sock); } /* * activate / deactivate bchannel */ static void bchannel_activate(struct mISDNport *mISDNport, int i) { struct mISDNhead hh; int len; /* we must activate if we are deactivated */ if (mISDNport->b_state[i] == B_STATE_IDLE) { /* activate bchannel */ PDEBUG("activating bchannel (index %d), because currently idle.\n", i); hh.prim = PH_ACTIVATE_REQ; hh.id = 0; len = sendto(mISDNport->b_sock[i], &hh, MISDN_HEADER_LEN, 0, NULL, 0); if (len <= 0) fprintf(stderr, "Failed to send to socket %d of port %d channel index %d\n", mISDNport->b_sock[i], mISDNport->portnum, i); mISDNport->b_state[i] = B_STATE_ACTIVATING; return; } /* if we are active, we configure our channel */ if (mISDNport->b_state[i] == B_STATE_ACTIVE) { /* it is an error if this channel is not associated with a port object */ PDEBUG("during activation, we set rxoff.\n"); ph_control(mISDNport->b_sock[i], DSP_RECEIVE_OFF, 0); PDEBUG("during activation, we add conference to %d.\n", ((mISDNport->count&(~1)) << 23) + (i<<16) + dsp_pid); ph_control(mISDNport->b_sock[i], DSP_CONF_JOIN, ((mISDNport->count&(~1)) << 23) + (i<<16) + dsp_pid); #if 0 if (sadks->crypt) { PDEBUG("during activation, we set crypt to crypt=%d.\n", mISDNport->b_port[i]->p_m_crypt); ph_control_block(mISDNport->b_addr[i], BF_ENABLE_KEY, mISDNport->b_port[i]->p_m_crypt_key, mISDNport->b_port[i]->p_m_crypt_key_len); } #endif } } static void bchannel_deactivate(struct mISDNport *mISDNport, int i) { struct mISDNhead hh; int len; if (mISDNport->b_state[i] == B_STATE_ACTIVE) { ph_control(mISDNport->b_sock[i], DSP_CONF_SPLIT, 0); ph_control(mISDNport->b_sock[i], DSP_RECEIVE_ON, 0); /* deactivate bchannel */ PDEBUG("deactivating bchannel (index %d), because currently active.\n", i); hh.prim = PH_DEACTIVATE_REQ; hh.id = 0; len = sendto(mISDNport->b_sock[i], &hh, MISDN_HEADER_LEN, 0, NULL, 0); if (len <= 0) fprintf(stderr, "Failed to send to socket %d of port %d channel index %d\n", mISDNport->b_sock[i], mISDNport->portnum, i); mISDNport->b_state[i] = B_STATE_DEACTIVATING; return; } } /* * main loop for processing messages from mISDN device */ int mISDN_handler(void) { struct mISDNport *mISDNport; int i; unsigned char data[2048]; struct mISDNhead *hh = (struct mISDNhead *)data; int len; int work = 0; /* handle all ports */ mISDNport = mISDNport_first; while(mISDNport) { len = recv(mISDNport->d_sock, data, sizeof(data), 0); if (len >= (int)MISDN_HEADER_LEN) { work = 1; /* d-message */ switch(hh->prim) { case PH_ACTIVATE_CNF: case PH_ACTIVATE_IND: PDEBUG("Received PH_ACTIVATE for port %d.\n", mISDNport->portnum); if (!mISDNport->l1link) { mISDNport->l1link = 1; show_state(mISDNport); } if (mISDNport->que_len) { PDEBUG("Data in que, due to inactive link on port %d.\n", mISDNport->portnum); len = sendto(mISDNport->d_sock, mISDNport->que_frm, mISDNport->que_len, 0, NULL, 0); if (len <= 0) fprintf(stderr, "Failed to send to socket %d of port %d\n", mISDNport->d_sock, mISDNport->portnum); mISDNport->que_len = 0; } break; case PH_DEACTIVATE_CNF: case PH_DEACTIVATE_IND: PDEBUG("Received PH_DEACTIVATE for port %d.\n", mISDNport->portnum); if (mISDNport->l1link) { mISDNport->l1link = 0; show_state(mISDNport); } mISDNport->que_len = 0; break; case PH_CONTROL_CNF: case PH_CONTROL_IND: PDEBUG("Received PH_CONTROL for port %d.\n", mISDNport->portnum); break; case PH_DATA_IND: if (traffic) show_traffic(mISDNport, data + MISDN_HEADER_LEN, len-MISDN_HEADER_LEN); PDEBUG("GOT data from %s port %d prim 0x%x id 0x%x\n", (mISDNport->ntmode)?"NT":"TE", mISDNport->portnum, hh->prim, hh->id); if (mISDNport->count & 1) { if (mISDNport->prev == NULL) { printf("soft error, no prev where expected.\n"); exit (0); } /* sending to previous port */ PDEBUG("sending to %s port %d prim 0x%x id 0x%x\n", (mISDNport->prev->ntmode)?"NT":"TE", mISDNport->prev->portnum, hh->prim, hh->id); hh->prim = PH_DATA_REQ; if (mISDNport->prev->l1link) { len = sendto(mISDNport->prev->d_sock, data, len, 0, NULL, 0); if (len <= 0) fprintf(stderr, "Failed to send to socket %d of port %d\n", mISDNport->d_sock, mISDNport->portnum); } else { PDEBUG("layer 1 is down, so we queue and activate link.\n"); memcpy(mISDNport->prev->que_frm, data, len); mISDNport->prev->que_len = len; hh->prim = PH_ACTIVATE_REQ; hh->id = 0; len = sendto(mISDNport->prev->d_sock, data, MISDN_HEADER_LEN, 0, NULL, 0); if (len <= 0) fprintf(stderr, "Failed to send to socket %d of port %d\n", mISDNport->d_sock, mISDNport->portnum); } } else { if (mISDNport->next == NULL) { printf("soft error, no next where expected.\n"); exit (0); } /* sending to next port */ PDEBUG("sending to %s port %d prim 0x%x id 0x%x\n", (mISDNport->next->ntmode)?"NT":"TE", mISDNport->next->portnum, hh->prim, hh->id); hh->prim = PH_DATA_REQ; if (mISDNport->next->l1link) { len = sendto(mISDNport->next->d_sock, data, len, 0, NULL, 0); if (len <= 0) fprintf(stderr, "Failed to send to socket %d of port %d\n", mISDNport->d_sock, mISDNport->portnum); } else { PDEBUG("layer 1 is down, so we queue and activate link.\n"); memcpy(mISDNport->next->que_frm, data, len); mISDNport->next->que_len = len; hh->prim = PH_ACTIVATE_REQ; hh->id = 0; len = sendto(mISDNport->next->d_sock, data, MISDN_HEADER_LEN, 0, NULL, 0); if (len <= 0) fprintf(stderr, "Failed to send to socket %d of port %d\n", mISDNport->d_sock, mISDNport->portnum); } } break; case PH_DATA_CNF: //PDEBUG("GOT confirm from %s port %d prim 0x%x id 0x%x\n", (mISDNport->ntmode)?"NT":"TE", mISDNport->portnum, hh->prim, hh->id); break; case PH_DATA_REQ: //PDEBUG("GOT strange PH_DATA REQUEST from %s port %d prim 0x%x id 0x%x 0x%x\n", (mISDNport->ntmode)?"NT":"TE", mISDNport->portnum, hh->prim, hh->id); break; default: break; } } i = 0; while(i < mISDNport->b_num) { len = recv(mISDNport->b_sock[i], data, sizeof(data), 0); if (len >= (int)MISDN_HEADER_LEN) { work = 1; /* b-message */ switch(hh->prim) { /* we don't care about confirms, we use rx data to sync tx */ case PH_DATA_CNF: //case DL_DATA_CNF: break; /* we receive audio data, we respond to it AND we send tones */ case PH_DATA_IND: case DL_DATA_IND: PDEBUG("got B-channel data, this should not happen all the time. (just a few until DSP release tx-data are ok)\n"); break; case PH_CONTROL_IND: break; case PH_ACTIVATE_IND: case DL_ESTABLISH_IND: case PH_ACTIVATE_CNF: case DL_ESTABLISH_CNF: PDEBUG("DL_ESTABLISH confirm: bchannel is now activated (port %d index %i).\n", mISDNport->portnum, i); mISDNport->b_state[i] = B_STATE_ACTIVE; bchannel_activate(mISDNport, i); break; case PH_DEACTIVATE_IND: case DL_RELEASE_IND: case PH_DEACTIVATE_CNF: case DL_RELEASE_CNF: PDEBUG("DL_RELEASE confirm: bchannel is now de-activated (port %d index %i).\n", mISDNport->portnum, i); mISDNport->b_state[i] = B_STATE_IDLE; break; default: PDEBUG("unknown bchannel data (port %d index %i).\n", mISDNport->portnum, i); } } i++; } mISDNport = mISDNport->next; } return(work); } /* * global function to add a new card (port) */ struct mISDNport *mISDN_port_open(int port, int nt_mode, int hdlc) { int ret; struct mISDNhead hh; struct mISDNport *mISDNport, **mISDNportp, *mISDNport_prev; int i, cnt; int bri = 0, pri = 0, pots = 0; int nt = 0, te = 0; struct mISDN_devinfo devinfo; unsigned long on = 1; struct sockaddr_mISDN addr; /* query port's requirements */ ret = ioctl(mISDNsocket, IMGETCOUNT, &cnt); if (ret < 0) { fprintf(stderr, "Cannot get number of mISDN devices. (ioctl IMGETCOUNT failed ret=%d)\n", ret); return(NULL); } if (cnt <= 0) { fprintf(stderr, "Found no card. Please be sure to load card drivers.\n"); return(NULL); } if (port < 0) { fprintf(stderr, "Port number cannot be negative\n"); return(NULL); } devinfo.id = port; ret = ioctl(mISDNsocket, IMGETDEVINFO, &devinfo); if (ret < 0) { fprintf(stderr, "Cannot get device information for port %d. (ioctl IMGETDEVINFO failed ret=%d)\n", port, ret); return(NULL); } /* output the port info */ if (devinfo.Dprotocols & (1 << ISDN_P_TE_S0)) { bri = 1; te = 1; } if (devinfo.Dprotocols & (1 << ISDN_P_NT_S0)) { bri = 1; nt = 1; } if (devinfo.Dprotocols & (1 << ISDN_P_TE_E1)) { pri = 1; te = 1; } if (devinfo.Dprotocols & (1 << ISDN_P_NT_E1)) { pri = 1; nt = 1; } #ifdef ISDN_P_FXS if (devinfo.Dprotocols & (1 << ISDN_P_FXS)) { pots = 1; te = 1; } #endif #ifdef ISDN_P_FXO if (devinfo.Dprotocols & (1 << ISDN_P_FXO)) { pots = 1; nt = 1; } #endif if (bri && pri) { fprintf(stderr, "Port %d supports BRI and PRI?? What kind of controller is that?. (Can't use this!)\n", port); return(NULL); } if (pots && !bri && !pri) { fprintf(stderr, "Port %d supports POTS, we can't!\n", port); return(NULL); } if (!bri && !pri) { fprintf(stderr, "Port %d does not support BRI nor PRI!\n", port); return(NULL); } if (!nt && !te) { fprintf(stderr, "Port %d does not support NT-mode nor TE-mode!\n", port); return(NULL); } if (!nt && nt_mode) { fprintf(stderr, "Port %d does not support NT-mode as requested!\n", port); return(NULL); } if (!te && !nt_mode) { fprintf(stderr, "Port %d does not support TE-mode as requested!\n", port); return(NULL); } /* add mISDNport structure */ mISDNport = mISDNport_first; mISDNportp = &mISDNport_first; mISDNport_prev = NULL; while(mISDNport) { mISDNport_prev=mISDNport; mISDNportp = &mISDNport->next; mISDNport = mISDNport->next; } mISDNport = (struct mISDNport *)calloc(1, sizeof(struct mISDNport)); if (!mISDNport) { fprintf(stderr, "Cannot alloc mISDNport structure\n"); return(NULL); } memset(mISDNport, 0, sizeof(struct mISDNport)); *mISDNportp = mISDNport; mISDNport->prev = mISDNport_prev; /* allocate ressources of port */ mISDNport->count = portcount++; mISDNport->portnum = port; mISDNport->b_num = devinfo.nrbchan; mISDNport->ntmode = nt_mode; mISDNport->pri = pri; /* open dchannel */ if (nt_mode) mISDNport->d_sock = socket(PF_ISDN, SOCK_DGRAM, pri?ISDN_P_NT_E1:ISDN_P_NT_S0); else mISDNport->d_sock = socket(PF_ISDN, SOCK_DGRAM, pri?ISDN_P_TE_E1:ISDN_P_TE_S0); if (mISDNport->d_sock < 0) { return(NULL); } /* set nonblocking io */ ret = ioctl(mISDNport->d_sock, FIONBIO, &on); if (ret < 0) { fprintf(stderr, "Error: Failed to set dchannel-socket into nonblocking IO\n"); return(NULL); } /* bind socket to dchannel */ memset(&addr, 0, sizeof(addr)); addr.family = AF_ISDN; addr.dev = mISDNport->portnum; addr.channel = 0; ret = bind(mISDNport->d_sock, (struct sockaddr *)&addr, sizeof(addr)); if (ret < 0) { fprintf(stderr, "Error: Failed to bind dchannel-socket.\n"); return(NULL); } PDEBUG("Port %d (%s) opened with %d b-channels.\n", port, devinfo.name, mISDNport->b_num); /* open bchannels */ i = 0; while(i < mISDNport->b_num) { mISDNport->b_sock[i] = -1; i++; } i = 0; while(i < mISDNport->b_num) { mISDNport->b_sock[i] = socket(PF_ISDN, SOCK_DGRAM, (hdlc)?ISDN_P_B_L2DSPHDLC:ISDN_P_B_L2DSP); if (mISDNport->b_sock[i] < 0) { fprintf(stderr, "Error: Failed to open bchannel-socket for index %d with mISDN-DSP layer. Did you load mISDNdsp.ko?\n", i); return(NULL); } /* set nonblocking io */ ret = ioctl(mISDNport->b_sock[i], FIONBIO, &on); if (ret < 0) { fprintf(stderr, "Error: Failed to set bchannel-socket index %d into nonblocking IO\n", i); return(NULL); } /* bind socket to bchannel */ memset(&addr, 0, sizeof(addr)); addr.family = AF_ISDN; addr.dev = mISDNport->portnum; addr.channel = i+1+(i>=15); ret = bind(mISDNport->b_sock[i], (struct sockaddr *)&addr, sizeof(addr)); if (ret < 0) { fprintf(stderr, "Error: Failed to bind bchannel-socket for index %d with mISDN-DSP layer. Did you load mISDNdsp.ko?\n", i); return(NULL); } i++; } /* try to activate link layer 1 */ hh.prim = PH_ACTIVATE_REQ; hh.id = 0; ret = sendto(mISDNport->d_sock, &hh, MISDN_HEADER_LEN, 0, NULL, 0); if (ret <= 0) fprintf(stderr, "Failed to send to socket %d of port %d\n", mISDNport->d_sock, mISDNport->portnum); /* initially, we assume that the link is down */ mISDNport->l1link = 0; PDEBUG("using 'mISDN_dsp.o' module\n"); printf("Port %d (%s) %s %s %d b-channels\n", mISDNport->portnum, devinfo.name, (mISDNport->ntmode)?"NT-mode":"TE-mode", pri?"PRI":"BRI", mISDNport->b_num); return(mISDNport); } /* * global function to free ALL cards (ports) */ void mISDN_port_close(void) { struct mISDNport *mISDNport, *mISDNporttemp; int i; /* free all ports */ mISDNport = mISDNport_first; while(mISDNport) { i = 0; while(i < mISDNport->b_num) { bchannel_deactivate(mISDNport, i); PDEBUG("freeing %s port %d bchannel (index %d).\n", (mISDNport->ntmode)?"NT":"TE", mISDNport->portnum, i); if (mISDNport->b_sock[i]) close(mISDNport->b_sock[i]); i++; } PDEBUG("freeing d-stack.\n"); if (mISDNport->d_sock) close(mISDNport->d_sock); mISDNporttemp = mISDNport; mISDNport = mISDNport->next; memset(mISDNporttemp, 0, sizeof(struct mISDNport)); free(mISDNporttemp); } mISDNport_first = NULL; } /* * main routine and loop */ int main(int argc, char *argv[]) { struct mISDNport *mISDNport_a, *mISDNport_b; int i, j, nt_a, nt_b; int forking = 0, hdlc = 0; dsp_pid = getpid(); if (argc <= 1) { usage: printf("Usage: %s [--