/* HFC PCM bridging server and client * * (C) 2022 by Andreas Eversberg * 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 3 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, see . */ /* * How does it work: * * The time slot assignment is done by a server process that runs on a Unix * socket. The server is implemented in this application and is started by * the first instance of this application. Each instance of this application * connects to the server. If all instances of this applications disconnect * (exit) from the server, it will terminate itself. * * If a call is made, the client need to check if it can bridge: * * - There must be no 3pty call, this would not allow bridging. * Note: Transmitting tones will override bridge inside HFC card, so no * disconnect is required. * - The RTP port of the originator (interface -> osmo-cc) must be known. * - The terminator (osmo-cc -> interface) knows that the originator's RTP IP * is on the local machine. * * The client will then tell the server about card number, port of the card * and channel of the port, together with the originator's RTP port. If there * is another client with the same originator's RTP port, PCM time slots will * be assigned to both clients. Then the clients will connect to the time * slots that are given by the server. If the clients are on different cards, * only one time slot will be assigned, because PCM bus has two lines (banks) * for both directions. If the clients are on the same card, two time slots * will be assigned, one for each direction. This is caused by a hardware * limitation. * * If the client terminates the call or the call can no longer be bridged, it * telles the server that it cannot bridge. The server will unassign the time * slots to both clients, if they were assigned. Then the clients will * disconnect from time slots, if connected. * * If the client terminates socket connection to the server (crash), the * server will remove all assigned slots that were allocated to this clients. * */ #include #include #include #include #include #include #include #include #include #include #include #include "../libdebug/debug.h" #include #include "isdn.h" #include "bridge.h" #define SOCKET_NAME "isdn_hfc_bridge_service_bla_blub_peng" #define SOCKET_VERSION 0x4fc4fc01 /* message from client to server */ struct msg_channel_info { /* version magic */ uint32_t version; /* physical identity */ int card; int port; int channel; /* rtp stream identity */ uint16_t orig_rtp_port; /* can we bridge? */ int can_bridge; }; /* message from server to client */ struct msg_bridge_order { /* version magic */ uint32_t version; /* physical identity */ int card; int port; int channel; /* pcm connection */ int pcm_bridge; int rx_slot; int tx_slot; int rx_bank; int tx_bank; }; static int num_slots; /* * server */ struct conn { struct conn *next; struct osmo_fd ofd; int rx_index; struct msg_channel_info rx_msg; }; struct channel { struct channel *next; /* client realation */ int socket; /* physical identity */ int card; int port; int channel; /* rtp stream identity */ uint16_t orig_rtp_port; /* pcm connection */ int pcm_bridge; int rx_slot; int tx_slot; int rx_bank; int tx_bank; }; static struct osmo_fd server_ofd = { .fd = -1 }; static struct timer server_timer; static struct conn *conn_list = NULL; static struct channel *channel_list = NULL; static void free_channel(struct channel *c) { struct channel **c_p; c_p = &channel_list; while (*c_p) { if (*c_p == c) break; c_p = &((*c_p)->next); } *c_p = c->next; free(c); } static void free_all_channels(void) { struct channel *c, *c2; c = channel_list; while (c) { c2 = c; c = c->next; free(c2); } channel_list = NULL; } static void free_connection(struct conn *c) { struct conn **c_p; c_p = &conn_list; while (*c_p) { if (*c_p == c) break; c_p = &((*c_p)->next); } *c_p = c->next; free(c); } static void free_all_connections(void) { struct conn *c, *c2; c = conn_list; while (c) { PDEBUG(DISDN, DEBUG_INFO, "Free pending bridge connection.\n"); c2 = c; osmo_fd_unregister(&c->ofd); close(c->ofd.fd); c = c->next; free(c2); } conn_list = NULL; } static int bridge_socket_server_cb(struct osmo_fd *ofd, unsigned int when); /* function to open the server socket */ static int open_server_socket(int daemon) { struct sockaddr_un sock_address; int rc; memset(&sock_address, 0, sizeof(sock_address)); sock_address.sun_family = AF_UNIX; strcpy(sock_address.sun_path + 1, SOCKET_NAME); rc = socket(PF_UNIX, SOCK_STREAM, 0); if (rc < 0) { PDEBUG(DISDN, DEBUG_ERROR, "Failed to create UNIX socket.\n"); return rc; } server_ofd.fd = rc; server_ofd.cb = bridge_socket_server_cb; server_ofd.data = NULL; server_ofd.when = OSMO_FD_READ; osmo_fd_register(&server_ofd); rc = bind(server_ofd.fd, (struct sockaddr *)(&sock_address), sizeof(struct sockaddr_un)); if (rc < 0) { if (!daemon || errno != EADDRINUSE) PDEBUG(DISDN, DEBUG_INFO, "Failed to bind UNIX socket with path '%s' (errno = %d (%s)).\n", SOCKET_NAME, errno, strerror(errno)); osmo_fd_unregister(&server_ofd); close(server_ofd.fd); return -EADDRINUSE; } rc = listen(server_ofd.fd, 10); if (rc < 0) { PDEBUG(DISDN, DEBUG_INFO, "Failed to listen to UNIX socket with path '%s' (errno = %d (%s)).\n", SOCKET_NAME, errno, strerror(errno)); osmo_fd_unregister(&server_ofd); close(server_ofd.fd); return rc; } return 0; } static int bridge_socket_conn_cb(struct osmo_fd *ofd, unsigned int when); static int bridge_socket_server_cb(struct osmo_fd __attribute__((unused)) *ofd, unsigned int when) { struct sockaddr_un sock_address; socklen_t sock_len = sizeof(sock_address); struct conn *c; int rc; if ((when & OSMO_FD_READ)) { rc = accept(server_ofd.fd, (struct sockaddr *)&sock_address, &sock_len); if (rc > 0) { PDEBUG(DISDN, DEBUG_INFO, "Connection from bridge socket client.\n"); /* create connection */ c = calloc(1, sizeof(*c)); if (!c) { PDEBUG(DISDN, DEBUG_ERROR, "No mem!\n"); abort(); } c->next = conn_list; conn_list = c; c->ofd.fd = rc; c->ofd.cb = bridge_socket_conn_cb; c->ofd.data = c; c->ofd.when = OSMO_FD_READ; osmo_fd_register(&c->ofd); /* reset rx buffer */ c->rx_index = 0; } } return 0; } static void rx_channel_info(int socket, int card, int port, int channel, uint16_t orig_rtp_port, int can_bridge); /* close server socket and remove all channels that are associated with it */ void close_server_connection(struct osmo_fd *ofd) { struct channel *c; /* safely remove all channels by calling rx_channel_info, which also removes bridges from remote channels */ do { c = channel_list; while (c) { if (c->socket == ofd->fd) break; c = c->next; } if (c) { c->socket = -1; rx_channel_info(-1, c->card, c->port, c->channel, c->orig_rtp_port, 0); } } while (c); osmo_fd_unregister(ofd); close(ofd->fd); } /* read message from server socket */ static int bridge_socket_conn_cb(struct osmo_fd *ofd, unsigned int when) { struct conn *c = ofd->data; int rc; if ((when & OSMO_FD_READ)) { rc = recv(c->ofd.fd, ((uint8_t *)&c->rx_msg) + c->rx_index, sizeof(c->rx_msg) - c->rx_index, 0); if (rc > 0) { c->rx_index += rc; if (c->rx_index == sizeof(c->rx_msg)) { PDEBUG(DISDN, DEBUG_DEBUG, "Message from bridge socket client.\n"); /* different version ? */ if (c->rx_msg.version != SOCKET_VERSION) { PDEBUG(DISDN, DEBUG_ERROR, "Bridge client uses different version than bridge server. Please update all applications to use same server version\n"); return 0; } /* process message and reset buffer index */ rx_channel_info(c->ofd.fd, c->rx_msg.card, c->rx_msg.port, c->rx_msg.channel, c->rx_msg.orig_rtp_port, c->rx_msg.can_bridge); c->rx_index = 0; return 0; } } else if (rc == 0 || errno != EAGAIN) { PDEBUG(DISDN, DEBUG_DEBUG, "Close from bridge socket client.\n"); close_server_connection(&c->ofd); /* destroy connection */ free_connection(c); } } return 0; } static void sighandler(int sigset) { PDEBUG(DISDN, DEBUG_DEBUG, "Signal %d received.\n", sigset); } static int quit = 0; static void server_exit_timeout(void __attribute__((unused)) *data) { if (!conn_list) { PDEBUG(DISDN, DEBUG_DEBUG, "All clients gone, exitting.\n"); quit = 1; return; } timer_start(&server_timer, 0.3); } static void server_show_timeout(void __attribute__((unused)) *data) { struct channel *ch = channel_list; if (ch) printf("Connections:\n"); while (ch) { printf("Card=%d Port=%d Channel=%d Bridge=%d\n", ch->card, ch->port, ch->channel, ch->pcm_bridge); ch = ch->next; } timer_start(&server_timer, 1.0); } /* open socket, wait for connections and read incoming messages */ void bridge_socket_server_child(int slots, int daemon) { int rc; num_slots = slots; rc = open_server_socket(daemon); if (rc < 0) { if (rc == -EADDRINUSE) PDEBUG(DISDN, DEBUG_INFO, "Bridging socket is already running by a different instance.\n"); return; } PDEBUG(DISDN, DEBUG_DEBUG, "Created bridging socket server.\n"); if (daemon) { signal(SIGINT, sighandler); signal(SIGHUP, sighandler); signal(SIGTERM, sighandler); signal(SIGPIPE, sighandler); } if (daemon) { timer_init(&server_timer, server_exit_timeout, NULL); timer_start(&server_timer, 0.3); } else { timer_init(&server_timer, server_show_timeout, NULL); timer_start(&server_timer, 1.0); } while(!quit) { double timeout; timeout = process_timer(); osmo_fd_select(timeout); } timer_exit(&server_timer); free_all_channels(); free_all_connections(); osmo_fd_unregister(&server_ofd); close(server_ofd.fd); } /* create socket server thread and return 0 on success, return -errno on failure */ int brigde_socket_server(int slots) { pid_t pid; pid = fork(); if (pid < 0) { PDEBUG(DISDN, DEBUG_ERROR, "fork() failed: errno=%d\n", errno); return -EINVAL; } if (pid == 0) { /* child */ pid = fork(); if (pid < 0) { PDEBUG(DISDN, DEBUG_ERROR, "fork() failed: errno=%d\n", errno); exit(0); } if (pid == 0) { /* grandchild */ bridge_socket_server_child(slots, 1); } /* exit child */ exit(0); } usleep(100000); return 0; } void tx_bridge_order(int socket, int card, int port, int channel, int pcm_bridge, int rx_slot, int tx_slot, int rx_bank, int tx_bank); /* receive channel info client */ static void rx_channel_info(int socket, int card, int port, int channel, uint16_t orig_rtp_port, int can_bridge) { struct channel *c, *o, *t; /* first check if channel already exists */ c = channel_list; while (c) { if (c->card == card && c->port == port && c->channel == channel) break; c = c->next; } if (c) { /* we are already can bridge */ if (can_bridge) { PDEBUG(DISDN, DEBUG_DEBUG, "There is no change in bridge, do nothing.\n"); return; } /* we must remove bridge, if remote channel exists */ o = channel_list; while (o) { if (o != c && o->orig_rtp_port == c->orig_rtp_port) break; o = o->next; } /* remote channel exists, so remove bridge */ if (o) { if (o->pcm_bridge && o->socket > 0) { PDEBUG(DISDN, DEBUG_DEBUG, "We cannot bridge anymore, remove remote channel's bridge.\n"); tx_bridge_order(o->socket, o->card, o->port, o->channel, 0, 0, 0, 0, 0); o->pcm_bridge = 0; } } /* now we remove our channel */ if (c->pcm_bridge && c->socket > 0) { PDEBUG(DISDN, DEBUG_DEBUG, "We cannot bridge anymore, remove our bridge.\n"); tx_bridge_order(c->socket, c->card, c->port, c->channel, 0, 0, 0, 0, 0); c->pcm_bridge = 0; } PDEBUG(DISDN, DEBUG_DEBUG, "Freeing our channel.\n"); free_channel(c); return; } /* if channel does not exists and we cannot bridge, do nothing */ if (!can_bridge) { PDEBUG(DISDN, DEBUG_DEBUG, "There is no change in bridge, do nothing.\n"); return; } /* channel does not exist, so we create it */ /* create connection */ PDEBUG(DISDN, DEBUG_DEBUG, "Creating our channel.\n"); c = calloc(1, sizeof(*c)); if (!c) { PDEBUG(DISDN, DEBUG_ERROR, "No mem!\n"); abort(); } c->next = channel_list; channel_list = c; c->socket = socket; c->card = card; c->port = port; c->channel = channel; c->orig_rtp_port = orig_rtp_port; c->pcm_bridge = 0; /* if there is a remote channel, select pcm slots and send bridge order */ o = channel_list; while (o) { if (o != c && o->orig_rtp_port == c->orig_rtp_port) break; o = o->next; } /* no other channel, so we do not need to send a bridge order */ if (!o) { PDEBUG(DISDN, DEBUG_DEBUG, "We can bridge, but there is no remote that can bridge (yet)..\n"); return; } PDEBUG(DISDN, DEBUG_DEBUG, "We can bridge, and remote can bridge, so we assign time slots.\n"); /* make a list of allocated time slots */ uint8_t pcm_slot[num_slots]; memset(pcm_slot, 0, sizeof(pcm_slot)); t = channel_list; while (t) { if (t->pcm_bridge) { pcm_slot[t->tx_slot] = 1; pcm_slot[t->rx_slot] = 1; } t = t->next; } /* hunt for free slot */ if (c->card != o->card) { int s; /* different card, so we need one slot */ for (s = 0; s < num_slots; s++) { if (!pcm_slot[s]) break; } if (s < num_slots) { /* cross banks */ c->pcm_bridge = 1; c->rx_slot = s; c->tx_slot = s; c->rx_bank = 0; c->tx_bank = 1; o->pcm_bridge = 1; o->rx_slot = s; o->tx_slot = s; o->rx_bank = 1; o->tx_bank = 0; } else { PDEBUG(DISDN, DEBUG_NOTICE, "No single free slots, cannot set bridge.\n"); return; } } else { int s, s1, s2; /* same card, so we need two slot */ s1 = s2 = -1; for (s = 0; s < num_slots; s++) { if (!pcm_slot[s]) { if (s1 < 0) s1 = s; else if (s2 < 0) { s2 = s; break; } } } if (s < num_slots) { /* cross slots */ c->pcm_bridge = 1; c->rx_slot = s1; c->tx_slot = s2; c->rx_bank = 0; c->tx_bank = 0; o->pcm_bridge = 1; o->rx_slot = s2; o->tx_slot = s1; o->rx_bank = 0; o->tx_bank = 0; } else { PDEBUG(DISDN, DEBUG_NOTICE, "No two free slots, cannot set bridge.\n"); return; } } /* bridge order to both ends */ tx_bridge_order(c->socket, c->card, c->port, c->channel, c->pcm_bridge, c->rx_slot, c->tx_slot, c->rx_bank, c->tx_bank); tx_bridge_order(o->socket, o->card, o->port, o->channel, o->pcm_bridge, o->rx_slot, o->tx_slot, o->rx_bank, o->tx_bank); } /* send bridge order towards client */ void tx_bridge_order(int socket, int card, int port, int channel, int pcm_bridge, int rx_slot, int tx_slot, int rx_bank, int tx_bank) { struct msg_bridge_order msg; int __attribute__((unused)) rc; memset(&msg, 0, sizeof(msg)); msg.version = SOCKET_VERSION; msg.card = card; msg.port = port; msg.channel = channel; msg.pcm_bridge = pcm_bridge; msg.rx_slot = rx_slot; msg.tx_slot = tx_slot; msg.rx_bank = rx_bank; msg.tx_bank = tx_bank; rc = send(socket, ((uint8_t *)&msg), sizeof(msg), 0); } /* * client */ static struct osmo_fd client_ofd = { .fd = -1 }; int client_rx_index = 0; struct msg_bridge_order client_rx_msg; static int bridge_socket_client_cb(struct osmo_fd *ofd, unsigned int when); /* function to open the client socket */ int bridge_socket_client(isdn_t *isdn_ep) { struct sockaddr_un sock_address; int rc; memset(&sock_address, 0, sizeof(sock_address)); sock_address.sun_family = AF_UNIX; strcpy(sock_address.sun_path + 1, SOCKET_NAME); rc = socket(PF_UNIX, SOCK_STREAM, 0); if (rc < 0) { PDEBUG(DISDN, DEBUG_ERROR, "Failed to create UNIX socket.\n"); return rc; } client_ofd.fd = rc; client_ofd.cb = bridge_socket_client_cb; client_ofd.data = isdn_ep; client_ofd.when = OSMO_FD_READ; osmo_fd_register(&client_ofd); rc = connect(client_ofd.fd, (struct sockaddr *)(&sock_address), sizeof(struct sockaddr_un)); if (rc < 0) { PDEBUG(DISDN, DEBUG_DEBUG, "Failed to connect to UNIX socket with path '%s' (errno = %d (%s)).\n", SOCKET_NAME, errno, strerror(errno)); osmo_fd_unregister(&client_ofd); close(client_ofd.fd); client_ofd.fd = -1; return -errno; } PDEBUG(DISDN, DEBUG_DEBUG, "Created bridging socket client.\n"); return 0; } void rx_bridge_order(isdn_t *isdn_ep, int card, int port, int channel, int pcm_bridge, int rx_slot, int tx_slot, int rx_bank, int tx_bank); /* read message from client socket */ static int bridge_socket_client_cb(struct osmo_fd *ofd, unsigned int when) { isdn_t *isdn_ep = ofd->data; int rc; if ((when & OSMO_FD_READ)) { /* read message until complete */ rc = recv(client_ofd.fd, ((uint8_t *)&client_rx_msg) + client_rx_index, sizeof(client_rx_msg) - client_rx_index, 0); if (rc > 0) { client_rx_index += rc; if (client_rx_index == sizeof(client_rx_msg)) { PDEBUG(DISDN, DEBUG_DEBUG, "Message from bridge socket server.\n"); /* different version ? */ if (client_rx_msg.version != SOCKET_VERSION) { PDEBUG(DISDN, DEBUG_ERROR, "Bridge server uses different version than bridge client. Please update all applications to use same server version\n"); return 0; } /* process message and reset buffer index */ rx_bridge_order(isdn_ep, client_rx_msg.card, client_rx_msg.port, client_rx_msg.channel, client_rx_msg.pcm_bridge, client_rx_msg.rx_slot, client_rx_msg.tx_slot, client_rx_msg.rx_bank, client_rx_msg.tx_bank); client_rx_index = 0; } } else if (rc == 0 || errno != EAGAIN) { PDEBUG(DISDN, DEBUG_DEBUG, "Close from bridge socket client.\n"); osmo_fd_unregister(&client_ofd); close(client_ofd.fd); client_ofd.fd = -1; } } return 0; } /* check if ip address is one of the local interfaces */ int check_local_address(sa_family_t family, struct sockaddr *addr) { struct ifaddrs *ifaddr, *ifa; int ret = 0; int rc; rc = getifaddrs(&ifaddr); if (rc < 0) { PDEBUG(DISDN, DEBUG_ERROR, "Failed to read list of interface addresses.\n"); return 0; } for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) { if (!ifa->ifa_addr) continue; if (family != ifa->ifa_addr->sa_family) continue; if (family == AF_INET) { if (!memcmp(&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr, &((struct sockaddr_in *)addr)->sin_addr, sizeof(struct in_addr))) { ret = 1; break; } } if (family == AF_INET6) { if (!memcmp(&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr, &((struct sockaddr_in6 *)addr)->sin6_addr, sizeof(struct in6_addr))) { ret = 1; break; } } } freeifaddrs(ifaddr); return ret; } void rx_bridge_order(isdn_t *isdn_ep, int card, int port, int channel, int pcm_bridge, int rx_slot, int tx_slot, int rx_bank, int tx_bank) { call_t *call; if (!isdn_ep->bridge_possible || isdn_ep->bridge_cardnum != card || isdn_ep->bridge_portnum != port) { PDEBUG(DISDN, DEBUG_ERROR, "Client received bridge order that does not match our port/port, please fix!\n"); } /* hunt for call */ call = isdn_ep->call_list; while (call) { if (call->b_channel == channel) break; call = call->next; } if (!call) { PDEBUG(DISDN, DEBUG_DEBUG, "Client received bridge order that does not belong to an active call, ignoring!\n"); return; } if (pcm_bridge) PDEBUG(DISDN, DEBUG_DEBUG, "Client received bridge connect order to connect to PCM slots (%d,%d,%d,%d).\n", rx_slot, tx_slot, rx_bank, tx_bank); else PDEBUG(DISDN, DEBUG_DEBUG, "Client received bridge disconnect order to disconnect to PCM slots.\n"); bchannel_bridge(call, pcm_bridge, rx_slot, tx_slot, rx_bank, tx_bank); } static void tx_channel_info(int socket, int card, int port, int channel, uint16_t orig_rtp_port, int can_bridge); void bridge_socket_client_update(call_t *call, int enable) { struct osmo_cc_session_media *media; struct sockaddr_storage sa; struct sockaddr_in6 *sa6; struct sockaddr_in *sa4; sa_family_t family = 0; //make GCC happy uint16_t orig_rtp_port = 0; int can_bridge = 0; int rc; /* no client */ if (client_ofd.fd <= 0) return; PDEBUG(DISDN, DEBUG_INFO, "We got called with enable=%d\n", enable); /* do we have HFC card ? */ if (!call->isdn_ep->bridge_possible && enable) { PDEBUG(DISDN, DEBUG_INFO, "Our card is not an HFC card, cannot use hardware bridging.\n"); return; } if (!call->b_channel) { PDEBUG(DISDN, DEBUG_DEBUG, "Cannot check bridge, no bchannel currently assigned.\n"); return; } /* check if essential structures are set */ if (!call->cc_session || !call->cc_session->media_list) { PDEBUG(DISDN, DEBUG_DEBUG, "Codec/media not set, not ready for bridging.\n"); return; } media = call->cc_session->media_list; /* check if codec negotiation is complete */ if (!media->description.port_local || (call->direction == DIRECTION_TERMINATOR && !media->description.port_remote)) { PDEBUG(DISDN, DEBUG_DEBUG, "Codec negotiation is not complete, cannot bridge yet.\n"); return; } if (call->direction == DIRECTION_TERMINATOR) { /* is the remote interface equal to the one of our interfaces? */ switch (media->connection_data_remote.addrtype) { case osmo_cc_session_addrtype_ipv4: family = AF_INET; memset(&sa, 0, sizeof(sa)); sa4 = (struct sockaddr_in *)&sa; sa4->sin_family = family; rc = inet_pton(AF_INET, media->connection_data_remote.address, &sa4->sin_addr); if (rc < 1) { PDEBUG(DISDN, DEBUG_ERROR, "inet_pton failed, please fix!\n"); return; } break; case osmo_cc_session_addrtype_ipv6: family = AF_INET6; memset(&sa, 0, sizeof(sa)); sa6 = (struct sockaddr_in6 *)&sa; sa6->sin6_family = family; rc = inet_pton(AF_INET6, media->connection_data_remote.address, &sa6->sin6_addr); if (rc < 1) { PDEBUG(DISDN, DEBUG_ERROR, "inet_pton failed, please fix!\n"); return; } break; case osmo_cc_session_addrtype_unknown: PDEBUG(DISDN, DEBUG_DEBUG, "Unsupported address type '%s'.\n", media->connection_data_remote.addrtype_name); return; } rc = check_local_address(family, (struct sockaddr *)&sa); if (rc < 1) { PDEBUG(DISDN, DEBUG_DEBUG, "Remote RTP peer is not on this machine, cannot use hardware bridging.\n"); return; } } else { PDEBUG(DISDN, DEBUG_DEBUG, "We are originator, we may use hardware bridging.\n"); } /* check if 3PTY */ if (call->conference_3pty) { PDEBUG(DISDN, DEBUG_DEBUG, "Whe have 3PTY conference, cannot bridge while this is going on.\n"); goto send; } can_bridge = enable; send: /* only send on change */ if (can_bridge && !call->can_bridge) { PDEBUG(DISDN, DEBUG_INFO, "We tell the server we can do hardware bridging.\n"); } else if (!can_bridge && call->can_bridge) { PDEBUG(DISDN, DEBUG_INFO, "We tell the server we cannot do hardware bridging anymore.\n"); } else return; call->can_bridge = can_bridge; /* get port */ if (call->direction == DIRECTION_ORIGINATOR) orig_rtp_port = media->description.port_local; else orig_rtp_port = media->description.port_remote; tx_channel_info(client_ofd.fd, call->isdn_ep->bridge_cardnum, call->isdn_ep->bridge_portnum, call->b_channel, orig_rtp_port, can_bridge); } static void tx_channel_info(int socket, int card, int port, int channel, uint16_t orig_rtp_port, int can_bridge) { struct msg_channel_info msg; int __attribute__((unused)) rc; memset(&msg, 0, sizeof(msg)); msg.version = SOCKET_VERSION; msg.card = card; msg.port = port; msg.channel = channel; msg.orig_rtp_port = orig_rtp_port; msg.can_bridge = can_bridge; rc = send(socket, ((uint8_t *)&msg), sizeof(msg), 0); }