osmo-cc-misdn-endpoint/src/isdn/bridge.c

893 lines
24 KiB
C

/* HFC PCM bridging server and client
*
* (C) 2022 by Andreas Eversberg <jolly@eversberg.eu>
* 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 <http://www.gnu.org/licenses/>.
*/
/*
* 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 <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <ifaddrs.h>
#include <errno.h>
#include "../libdebug/debug.h"
#include <mISDN/mbuffer.h>
#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);
}