Change-Id: I4fdcebf3b006a72c5318a9105a2be2cc7057dca1
This commit is contained in:
Neels Hofmeyr
2018-10-08 03:36:45 +02:00
parent 3243c7cdd9
commit 85f9401810
11 changed files with 290 additions and 160 deletions

View File

@@ -29,6 +29,7 @@
/* Debug Areas of the code */
enum {
DRTP,
DIUUP,
Debug_LastEntry,
};

View File

@@ -10,22 +10,23 @@
* -----------------
*/
#pragma once
struct osmo_iuup_cn;
struct msgb;
typedef int (*osmo_iuup_data_cb_t)(struct msgb *msg, void *node_priv, void *pdu_priv);
typedef int (*osmo_iuup_data_cb_t)(struct msgb *msg, void *node_priv);
struct osmo_iuup_cn_cfg {
void *node_priv;
/* When an IuUP PDU containing voice payload has been received, this callback is invoked to pass
* the voice payload towards the Core Network, msgb_l3() pointing at the payload. */
/* When the IuUP peer sent a voice packet, the clean RTP without the IuUP header is fed to this
* callback. */
osmo_iuup_data_cb_t rx_payload;
/* IuUP handler sends a PDU to the IuUP peer (e.g. the RNC) */
/* IuUP handler requests that a PDU shall be sent to the IuUP peer (e.g. the RNC).
* It is guaranteed that the msgb->dst pointer is preserved or copied from the msgb that
* originated the request. */
osmo_iuup_data_cb_t tx_msg;
};
@@ -35,12 +36,6 @@ struct osmo_iuup_cn *osmo_iuup_cn_init(void *ctx, struct osmo_iuup_cn_cfg *cfg,
const char *name_fmt, ...);
void osmo_iuup_cn_free(struct osmo_iuup_cn *cn);
/* Encapsulate voice stream payload in IuUP and, if appropriate, call the tx_msg() to transmit the
* resulting message to the IuUP peer. msgb_l3() should point at the payload data.
* pdu_priv is transparently passed on to tx_msg().
* Returns 0 on success, negative on error. */
int osmo_iuup_cn_tx_payload(struct osmo_iuup_cn *cn, struct msgb *payload, void *pdu_priv);
int osmo_iuup_cn_tx_payload(struct osmo_iuup_cn *cn, struct msgb *payload);
/* Feed a received PDU to the IuUP CN node. This function takes ownership of the msgb, it must not be
* freed by the caller. */
int osmo_iuup_cn_rx_pdu(struct osmo_iuup_cn *cn, struct msgb *pdu, void *pdu_priv);
int osmo_iuup_cn_rx_pdu(struct osmo_iuup_cn *cn, struct msgb *pdu);

View File

@@ -21,6 +21,9 @@ enum osmo_iuup_acknack {
enum osmo_iuup_procedure {
OSMO_IUUP_PROC_INITIALIZATION = 0,
OSMO_IUUP_PROC_RATE_CONTROL = 1,
OSMO_IUUP_PROC_TIME_ALIGNMENT = 2,
OSMO_IUUP_PROC_ERROR_EVENT = 3,
};
enum osmo_iuup_frame_good {
@@ -40,10 +43,6 @@ struct osmo_iuup_hdr_ctrl {
payload_crc_hi:2;
uint8_t payload_crc_lo;
uint8_t payload[0];
uint8_t spare:3,
iptis_present:1,
subflows:3,
chain:1;
#elif OSMO_IS_LITTLE_ENDIAN
uint8_t frame_nr:2,
ack_nack:2,
@@ -54,12 +53,38 @@ struct osmo_iuup_hdr_ctrl {
header_crc:6;
uint8_t payload_crc_lo;
uint8_t payload[0];
#endif
} __attribute__((packed));
union osmo_iuup_hdr_ctrl_payload {
struct {
#if OSMO_IS_BIG_ENDIAN
uint8_t spare:3,
iptis_present:1,
subflows:3,
chain:1;
#elif OSMO_IS_LITTLE_ENDIAN
uint8_t spare:3,
iptis_present:1,
subflows:3,
chain:1;
#endif
} __attribute__((packed));
} initialization;
struct {
#if OSMO_IS_BIG_ENDIAN
uint8_t error_distance:2,
error_cause:6;
#elif OSMO_IS_LITTLE_ENDIAN
uint8_t error_cause:6,
error_distance:2;
#endif
} error_event;
};
extern const struct value_string osmo_iuup_error_cause_names[];
static inline const char *osmo_iuup_error_cause_name(uint8_t val)
{ return get_value_string(osmo_iuup_error_cause_names, val); }
struct osmo_iuup_hdr_data {
#if OSMO_IS_BIG_ENDIAN

View File

@@ -23,18 +23,28 @@
#pragma once
#include <osmocom/core/msgb.h>
struct sockaddr_in;
struct mgcp_conn;
struct mgcp_conn_rtp;
struct mgcp_endpoint;
struct osmo_rtp_msg_ctx {
int proto;
struct mgcp_conn_rtp *conn_src;
struct sockaddr_in *from_addr;
};
#define OSMO_RTP_MSG_CTX(MSGB) (*(struct osmo_rtp_msg_ctx**)&((MSGB)->dst))
#define LOG_ENDP(endp, level, fmt, args...) \
LOGP(DRTP, level, "%x@ " fmt, ENDPOINT_NUMBER(endp), ## args)
/* Callback type for RTP dispatcher functions
(e.g mgcp_dispatch_rtp_bridge_cb, see below) */
typedef int (*mgcp_dispatch_rtp_cb) (int proto, struct sockaddr_in *addr,
struct msgb *payload,
struct mgcp_conn *conn);
/* Callback type for RTP dispatcher functions (e.g mgcp_dispatch_rtp_bridge_cb, see below).
* The fields OSMO_RTP_MSG_PROTO, OSMO_RTP_MSG_CONN_SRC, OSMO_RTP_MSG_FROM_ADDR should be set
* appropriately on the msg. */
typedef int (*mgcp_dispatch_rtp_cb) (struct msgb *msg);
/* Callback type for endpoint specific cleanup actions. This function
* is automatically executed when a connection is freed (see mgcp_conn_free()

View File

@@ -271,8 +271,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
struct msgb *msg, struct mgcp_conn_rtp *conn_src,
struct mgcp_conn_rtp *conn_dst);
int mgcp_send_dummy(struct mgcp_endpoint *endp, struct mgcp_conn_rtp *conn);
int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr,
struct msgb *payload, struct mgcp_conn *conn);
int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg);
void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn);
int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port,
struct mgcp_conn_rtp *conn);

View File

@@ -39,12 +39,13 @@
#include <osmocom/mgcp/debug.h>
#define LOG_IUUP_CN(cn, level, fmt, args...) \
LOGP(DRTP, level, "(%s) " fmt, (cn)->name, ## args)
LOGP(DIUUP, level, "(%s) " fmt, (cn)->name, ## args)
struct osmo_iuup_cn {
struct osmo_iuup_cn_cfg cfg;
char *name;
uint8_t next_frame_nr;
int rtp_payload_type;
};
struct osmo_iuup_cn *osmo_iuup_cn_init(void *ctx, struct osmo_iuup_cn_cfg *cfg,
@@ -63,8 +64,10 @@ struct osmo_iuup_cn *osmo_iuup_cn_init(void *ctx, struct osmo_iuup_cn_cfg *cfg,
cn->name = talloc_vasprintf(cn, name_fmt, ap);
va_end(ap);
LOGP(DIUUP, LOGL_INFO, "(%s) Initializing IuUP node\n", cn->name);
if (!osmo_identifier_valid(cn->name)) {
LOGP(DLGLOBAL, LOGL_ERROR, "Attempting to set illegal id for IuUP CN instance: %s\n",
LOGP(DIUUP, LOGL_ERROR, "Attempting to set illegal id for IuUP CN instance: %s\n",
osmo_quote_str(cn->name, -1));
talloc_free(cn);
return NULL;
@@ -79,7 +82,7 @@ void osmo_iuup_cn_free(struct osmo_iuup_cn *cn)
}
static int rx_data(struct osmo_iuup_cn *cn, struct msgb *pdu,
struct osmo_iuup_hdr_data *hdr, void *pdu_priv)
struct osmo_iuup_hdr_data *hdr)
{
/* Remove the IuUP bit from the middle of the buffer by writing the RTP header forward. */
unsigned int pre_hdr_len = ((uint8_t*)hdr) - pdu->data;
@@ -87,42 +90,72 @@ static int rx_data(struct osmo_iuup_cn *cn, struct msgb *pdu,
msgb_pull(pdu, sizeof(*hdr));
cn->cfg.rx_payload(pdu, cn->cfg.node_priv, pdu_priv);
LOGP(DIUUP, LOGL_DEBUG, "(%s) IuUP stripping IuUP header from RTP data\n", cn->name);
cn->cfg.rx_payload(pdu, cn->cfg.node_priv);
return 0;
}
static int tx_init_ack(struct osmo_iuup_cn *cn, void *pdu_priv)
static int tx_init_ack(struct osmo_iuup_cn *cn, struct msgb *src_pdu)
{
/* Send Initialization Ack PDU back to the sender */
int rc;
struct msgb *ack = msgb_alloc(4096, "IuUP Initialization Ack");
OSMO_ASSERT(ack);
ack->dst = src_pdu->dst;
/* Just copy the RTP header that was sent... TODO: tweak some RTP values?? */
memcpy(msgb_put(ack, sizeof(struct rtp_hdr)), src_pdu->data, sizeof(struct rtp_hdr));
osmo_iuup_make_init_ack(ack);
return cn->cfg.tx_msg(ack, cn->cfg.node_priv, pdu_priv);
LOGP(DIUUP, LOGL_DEBUG, "(%s) Sending Initialization ACK %p\n", cn->name, cn->cfg.node_priv);
rc = cn->cfg.tx_msg(ack, cn->cfg.node_priv);
msgb_free(ack);
return rc;
}
static int rx_control(struct osmo_iuup_cn *cn, struct msgb *pdu,
struct osmo_iuup_hdr_ctrl *hdr, void *pdu_priv)
struct osmo_iuup_hdr_ctrl *hdr)
{
switch (hdr->procedure) {
case OSMO_IUUP_PROC_INITIALIZATION:
switch (hdr->ack_nack) {
case OSMO_IUUP_ACKNACK_PROCEDURE:
return tx_init_ack(cn, pdu_priv);
LOGP(DIUUP, LOGL_INFO, "(%s) Rx IuUP Initialization, sending ACK\n", cn->name);
cn->rtp_payload_type = ((struct rtp_hdr*)pdu->data)->payload_type;
return tx_init_ack(cn, pdu);
default:
LOGP(DIUUP, LOGL_DEBUG, "(%s) Rx IuUP Initialization, unhandled ack_nack = %d\n",
cn->name, hdr->ack_nack);
break;
}
/* fall thru */
/* Continue to log "unexpected procedure" below. */
break;
case OSMO_IUUP_PROC_ERROR_EVENT:
{
union osmo_iuup_hdr_ctrl_payload *p = (void*)hdr->payload;
LOGP(DIUUP, LOGL_ERROR,
"(%s) Rx IuUP Error Event: distance=%u, cause=%u=\"%s\"\n",
cn->name, p->error_event.error_distance, p->error_event.error_cause,
osmo_iuup_error_cause_name(p->error_event.error_cause));
return 0;
}
default:
LOG_IUUP_CN(cn, LOGL_ERROR,
"Rx control PDU with unexpected procedure: 0x%x acknack=0x%x\n",
hdr->procedure, hdr->ack_nack);
return -EINVAL;
break;
}
LOG_IUUP_CN(cn, LOGL_ERROR,
"Rx control PDU with unexpected procedure: 0x%x acknack=0x%x\n",
hdr->procedure, hdr->ack_nack);
return -EINVAL;
}
int osmo_iuup_cn_rx_pdu(struct osmo_iuup_cn *cn, struct msgb *pdu, void *pdu_priv)
/* Feed a received PDU to the IuUP CN node. This function takes ownership of the msgb, it must not be
* freed by the caller. */
int osmo_iuup_cn_rx_pdu(struct osmo_iuup_cn *cn, struct msgb *pdu)
{
struct osmo_iuup_hdr_ctrl *is_ctrl;
struct osmo_iuup_hdr_data *is_data;
@@ -133,20 +166,23 @@ int osmo_iuup_cn_rx_pdu(struct osmo_iuup_cn *cn, struct msgb *pdu, void *pdu_pri
return rc;
if (is_ctrl)
return rx_control(cn, pdu, is_ctrl, pdu_priv);
return rx_control(cn, pdu, is_ctrl);
if (is_data)
return rx_data(cn, pdu, is_data, pdu_priv);
return rx_data(cn, pdu, is_data);
return rc;
}
static uint8_t next_frame_nr(struct osmo_iuup_cn *cn)
{
uint8_t frame_nr = cn->next_frame_nr;
cn->next_frame_nr = (cn->next_frame_nr + 1) % 0x0f;
cn->next_frame_nr = (frame_nr + 1) & 0x0f;
return frame_nr;
}
int osmo_iuup_cn_tx_payload(struct osmo_iuup_cn *cn, struct msgb *pdu, void *pdu_priv)
/* Send this RTP packet to the IuUP peer: add IuUP header and call the tx_msg() to transmit the resulting
* message to the IuUP peer.
* Returns 0 on success, negative on error. */
int osmo_iuup_cn_tx_payload(struct osmo_iuup_cn *cn, struct msgb *pdu)
{
struct rtp_hdr *rtp_was, *rtp;
struct osmo_iuup_hdr_data *iuup_hdr;
@@ -157,6 +193,10 @@ int osmo_iuup_cn_tx_payload(struct osmo_iuup_cn *cn, struct msgb *pdu, void *pdu
/* copy the RTP header part backwards by the size needed for the IuUP header */
rtp = (void*)msgb_push(pdu, sizeof(*iuup_hdr));
memmove(rtp, rtp_was, sizeof(*rtp));
/* Send the same payload type to the peer (erm...) */
rtp->payload_type = cn->rtp_payload_type;
iuup_hdr = (void*)rtp->data;
*iuup_hdr = (struct osmo_iuup_hdr_data){
@@ -166,6 +206,8 @@ int osmo_iuup_cn_tx_payload(struct osmo_iuup_cn *cn, struct msgb *pdu, void *pdu
};
osmo_iuup_set_checksums((uint8_t*)iuup_hdr, pdu->tail - (uint8_t*)iuup_hdr);
LOGP(DIUUP, LOGL_DEBUG, "(%s) IuUP inserting IuUP header in RTP data (frame nr %u)\n",
cn->name, iuup_hdr->frame_nr);
return cn->cfg.tx_msg(pdu, cn->cfg.node_priv, pdu_priv);
return cn->cfg.tx_msg(pdu, cn->cfg.node_priv);
}

View File

@@ -152,7 +152,7 @@ void osmo_iuup_set_checksums(uint8_t *iuup_header_and_payload, unsigned int head
/* Validate minimum message sizes, IuUP PDU type, header- and payload checksums. If it is a Control
* Procedure PDU, return the header position in is_ctrl, if it is a Data PDU, return the header position
* in is_data. If log_errors is true, log on DRTP with the given log label for context. Return NULL in
* in is_data. If log_errors is true, log on DIUUP with the given log label for context. Return NULL in
* both is_ctrl and is_data, and return a negative error code if the PDU could not be identified as a
* valid RTP PDU containing an IuUP part. */
int osmo_iuup_classify(bool log_errors,
@@ -169,7 +169,7 @@ int osmo_iuup_classify(bool log_errors,
#define ERR(fmt, args...) do { \
if (log_errors) \
LOGP(DRTP, LOGL_ERROR, "(%s) " fmt, log_label? : "-", ## args); \
LOGP(DIUUP, LOGL_ERROR, "(%s) " fmt, log_label? : "-", ## args); \
return -EINVAL; \
} while (0)
@@ -240,6 +240,7 @@ bool osmo_iuup_is_init(struct msgb *pdu)
&& is_ctrl->ack_nack == OSMO_IUUP_ACKNACK_PROCEDURE;
}
/* Append an IuUP Initialization ACK message */
void osmo_iuup_make_init_ack(struct msgb *ack)
{
/* Send Initialization Ack PDU back to the sender */
@@ -256,3 +257,30 @@ void osmo_iuup_make_init_ack(struct msgb *ack)
osmo_iuup_set_checksums((uint8_t*)hdr, sizeof(*hdr));
}
const struct value_string osmo_iuup_error_cause_names[] = {
{ 0, "CRC error of frame header" },
{ 1, "CRC error of frame payload" },
{ 2, "Unexpected frame number" },
{ 3, "Frame loss" },
{ 4, "PDU type unknown" },
{ 5, "Unknown procedure" },
{ 6, "Unknown reserved value" },
{ 7, "Unknown field" },
{ 8, "Frame too short" },
{ 9, "Missing fields" },
{ 16, "Unexpected PDU type" },
{ 17, "spare" },
{ 18, "Unexpected procedure" },
{ 19, "Unexpected RFCI" },
{ 20, "Unexpected value" },
{ 42, "Initialisation failure" },
{ 43, "Initialisation failure (network error, timer expiry)" },
{ 44, "Initialisation failure (Iu UP function error, repeated NACK)" },
{ 45, "Rate control failure" },
{ 46, "Error event failure" },
{ 47, "Time Alignment not supported" },
{ 48, "Requested Time Alignment not possible" },
{ 49, "Iu UP Mode version not supported" },
{}
};

View File

@@ -57,10 +57,7 @@ enum rtp_proto {
MGCP_PROTO_RTCP,
};
static int rx_rtp(struct mgcp_conn_rtp *conn_src, struct msgb *payload,
enum rtp_proto proto, struct sockaddr_in *from_addr);
static int tx_rtp(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst,
enum rtp_proto proto, struct sockaddr_in *from_addr, struct msgb *payload);
static int rx_rtp(struct msgb *msg);
/*! Determine the local rtp bind IP-address.
* \param[out] addr caller provided memory to store the resulting IP-Address
@@ -494,8 +491,8 @@ static int mgcp_patch_pt(struct mgcp_conn_rtp *conn_src,
uint8_t pt_in;
int pt_out;
OSMO_ASSERT(msgb_l3len(msg) >= sizeof(struct rtp_hdr));
rtp_hdr = (struct rtp_hdr *)msgb_l3(msg);
OSMO_ASSERT(msg->len >= sizeof(struct rtp_hdr));
rtp_hdr = (struct rtp_hdr *)msg->data;
pt_in = rtp_hdr->payload_type;
pt_out = mgcp_codec_pt_translate(conn_src, conn_dst, pt_in);
@@ -523,12 +520,12 @@ void mgcp_patch_and_count(struct mgcp_endpoint *endp,
uint32_t timestamp, ssrc;
struct rtp_hdr *rtp_hdr;
int payload = rtp_end->codec->payload_type;
unsigned int len = msgb_l3len(msg);
unsigned int len = msg->len;
if (len < sizeof(*rtp_hdr))
return;
rtp_hdr = (struct rtp_hdr *)msgb_l3(msg);
rtp_hdr = (struct rtp_hdr *)msg->data;
seq = ntohs(rtp_hdr->sequence);
timestamp = ntohl(rtp_hdr->timestamp);
arrival_time = get_current_ts(rtp_end->codec->rate);
@@ -668,7 +665,7 @@ static void forward_data(int fd, struct mgcp_rtp_tap *tap, struct msgb *msg)
if (!tap->enabled)
return;
rc = sendto(fd, msgb_l3(msg), msgb_l3len(msg), 0, (struct sockaddr *)&tap->forward,
rc = sendto(fd, msg->data, msg->len, 0, (struct sockaddr *)&tap->forward,
sizeof(tap->forward));
if (rc < 0)
@@ -754,11 +751,11 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
} else if (is_rtp) {
int cont;
int nbytes = 0;
int buflen = msgb_l3len(msg);
int buflen = msg->len;
do {
/* Run transcoder */
cont = endp->cfg->rtp_processing_cb(endp, rtp_end,
msgb_l3(msg), &buflen,
(char*)msg->data, &buflen,
RTP_BUF_SIZE);
if (cont < 0)
break;
@@ -778,6 +775,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
forward_data(rtp_end->rtp.fd, &conn_src->tap_out,
msg);
#if 0
/* FIXME: HACK HACK HACK. See OS#2459.
* The ip.access nano3G needs the first RTP payload's first two bytes to read hex
* 'e400', or it will reject the RAB assignment. It seems to not harm other femto
@@ -785,7 +783,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
*/
if (!rtp_state->patched_first_rtp_payload
&& conn_src->conn->mode == MGCP_CONN_LOOPBACK) {
uint8_t *data = ((uint8_t *)msgb_l3(msg)) + 12;
uint8_t *data = msg->data + 12;
if (data[0] == 0xe0) {
data[0] = 0xe4;
data[1] = 0x00;
@@ -796,11 +794,13 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
ENDPOINT_NUMBER(endp));
}
}
#endif
len = mgcp_udp_send(rtp_end->rtp.fd,
&rtp_end->addr,
rtp_end->rtp_port,
msgb_l3(msg), msgb_l3len(msg));
if (conn_dst->iuup)
len = osmo_iuup_cn_tx_payload(conn_dst->iuup, msg);
else
len = mgcp_udp_send(rtp_end->rtp.fd, &rtp_end->addr, rtp_end->rtp_port,
(char*)msg->data, msg->len);
if (len <= 0)
return len;
@@ -823,7 +823,7 @@ int mgcp_send(struct mgcp_endpoint *endp, int is_rtp, struct sockaddr_in *addr,
len = mgcp_udp_send(rtp_end->rtcp.fd,
&rtp_end->addr,
rtp_end->rtcp_port, msgb_l3(msg), msgb_l3len(msg));
rtp_end->rtcp_port, (char*)msg->data, msg->len);
rate_ctr_inc(&conn_dst->rate_ctr_group->ctr[RTP_PACKETS_TX_CTR]);
rate_ctr_add(&conn_dst->rate_ctr_group->ctr[RTP_OCTETS_TX_CTR], len);
@@ -914,7 +914,7 @@ static int check_rtp_destin(struct mgcp_conn_rtp *conn)
* and IP-address for outgoing data. */
if (strcmp(inet_ntoa(conn->end.addr), "0.0.0.0") == 0 && conn->end.rtp_port == 0) {
LOGP(DRTP, LOGL_DEBUG,
"endpoint:0x%x destination IP-address and rtp port is (not yet) known\n",
"endpoint:0x%x destination IP-address and rtp port is not known (yet)\n",
ENDPOINT_NUMBER(endp));
return -1;
}
@@ -943,23 +943,22 @@ static int check_rtcp(struct mgcp_conn_rtp *conn_src, struct msgb *msg)
struct rtcp_hdr *hdr;
unsigned int len;
uint8_t type;
unsigned int buf_size = msgb_l3len(msg);
/* RTPC packets that are just a header without data do not make
* any sense. */
if (buf_size < sizeof(struct rtcp_hdr)) {
if (msg->len < sizeof(struct rtcp_hdr)) {
LOG_CONN_RTP(conn_src, LOGL_ERROR, "RTCP packet too short (%u < %zu)\n",
buf_size, sizeof(struct rtcp_hdr));
msg->len, sizeof(struct rtcp_hdr));
return -EINVAL;
}
/* Make sure that the length of the received packet does not exceed
* the available buffer size */
hdr = (struct rtcp_hdr *)msgb_l3(msg);
hdr = (struct rtcp_hdr *)msg->data;
len = (osmo_ntohs(hdr->length) + 1) * 4;
if (len > buf_size) {
if (len > msg->len) {
LOG_CONN_RTP(conn_src, LOGL_ERROR, "RTCP header length exceeds packet size (%u > %u)\n",
len, buf_size);
len, msg->len);
return -EINVAL;
}
@@ -981,9 +980,9 @@ static int check_rtp(struct mgcp_conn_rtp *conn_src, struct msgb *msg)
size_t min_size = sizeof(struct rtp_hdr);
if (conn_src->iuup)
min_size += sizeof(struct osmo_iuup_hdr_data);
if (msgb_l3len(msg) < min_size) {
if (msg->len < min_size) {
LOG_CONN_RTP(conn_src, LOGL_ERROR, "RTP packet too short (%u < %zu)\n",
msgb_l3len(msg), min_size);
msg->len, min_size);
return -1;
}
@@ -998,11 +997,11 @@ static int check_rtp(struct mgcp_conn_rtp *conn_src, struct msgb *msg)
/* Send RTP data. Possible options are standard RTP packet
* transmission or trsmission via an osmux connection */
static int mgcp_send_rtp(int proto, struct sockaddr_in *addr,
struct msgb *payload,
struct mgcp_conn_rtp *conn_src,
struct mgcp_conn_rtp *conn_dst)
static int mgcp_send_rtp(struct mgcp_conn_rtp *conn_dst, struct msgb *msg)
{
enum rtp_proto proto = OSMO_RTP_MSG_CTX(msg)->proto;
struct mgcp_conn_rtp *conn_src = OSMO_RTP_MSG_CTX(msg)->conn_src;
struct sockaddr_in *from_addr = OSMO_RTP_MSG_CTX(msg)->from_addr;
struct mgcp_endpoint *endp = conn_src->conn->endp;
LOGP(DRTP, LOGL_DEBUG, "endpoint:0x%x destin conn:%s\n",
@@ -1023,14 +1022,14 @@ static int mgcp_send_rtp(int proto, struct sockaddr_in *addr,
"using mgcp_send() to forward data directly\n",
ENDPOINT_NUMBER(endp));
return mgcp_send(endp, proto == MGCP_PROTO_RTP,
addr, payload, conn_src, conn_dst);
from_addr, msg, conn_src, conn_dst);
case MGCP_OSMUX_BSC_NAT:
case MGCP_OSMUX_BSC:
LOGP(DRTP, LOGL_DEBUG,
"endpoint:0x%x endpoint type is MGCP_OSMUX_BSC_NAT, "
"using osmux_xfrm_to_osmux() to forward data through OSMUX\n",
ENDPOINT_NUMBER(endp));
return osmux_xfrm_to_osmux(msgb_l3(payload), msgb_l3len(payload), conn_dst);
return osmux_xfrm_to_osmux((char*)msg->data, msg->len, conn_dst);
}
/* If the data has not been handled/forwarded until here, it will
@@ -1050,9 +1049,10 @@ static int mgcp_send_rtp(int proto, struct sockaddr_in *addr,
* \param[in] buf_size size data length of buf
* \param[in] conn originating connection
* \returns 0 on success, -1 on ERROR */
int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr,
struct msgb *payload, struct mgcp_conn *conn)
int mgcp_dispatch_rtp_bridge_cb(struct msgb *msg)
{
struct mgcp_conn_rtp *conn_src = OSMO_RTP_MSG_CTX(msg)->conn_src;
struct mgcp_conn *conn = conn_src->conn;
struct mgcp_conn *conn_dst;
struct mgcp_endpoint *endp;
endp = conn->endp;
@@ -1096,7 +1096,7 @@ int mgcp_dispatch_rtp_bridge_cb(int proto, struct sockaddr_in *addr,
}
/* Dispatch RTP packet to destination RTP connection */
return tx_rtp(&conn->u.rtp, &conn_dst->u.rtp, proto, addr, payload);
return mgcp_send_rtp(&conn_dst->u.rtp, msg);
}
/*! cleanup an endpoint when a connection on an RTP bridge endpoint is removed.
@@ -1120,14 +1120,7 @@ void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *co
static bool is_dummy_msg(enum rtp_proto proto, struct msgb *msg)
{
return msgb_l3len(msg) == 1 && ((char*)msgb_l3(msg))[0] == MGCP_DUMMY_LOAD;
}
int rx_rtp_from_iuup(struct msgb *msg, void *node_priv, void *pdu_priv)
{
struct mgcp_conn_rtp *conn_src = node_priv;
struct sockaddr_in *from_addr = pdu_priv;
return rx_rtp(conn_src, msg, MGCP_PROTO_RTP, from_addr);
return msg->len == 1 && msg->data[0] == MGCP_DUMMY_LOAD;
}
struct pdu_ctx {
@@ -1135,23 +1128,64 @@ struct pdu_ctx {
struct mgcp_conn_rtp *conn_src;
};
int tx_pdu_from_iuup(struct msgb *msg, void *node_priv, void *pdu_priv)
/* IuUP CN node has stripped an IuUP header and forwards RTP data to distribute to the peers. */
int iuup_rx_payload(struct msgb *msg, void *node_priv)
{
struct mgcp_conn_rtp *conn_dst = node_priv;
struct pdu_ctx *p = pdu_priv;
return mgcp_send_rtp(MGCP_PROTO_RTP, p->from_addr, msg, p->conn_src, conn_dst);
struct mgcp_conn_rtp *conn_src = OSMO_RTP_MSG_CTX(msg)->conn_src;
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "iuup_rx_payload(%u bytes)\n", msg->len);
return rx_rtp(msg);
}
static void init_iuup(struct mgcp_conn_rtp *conn_src)
/* IuUP CN node has composed a message that contains an IuUP header and asks us to send to the IuUP peer.
*/
int iuup_tx_msg(struct msgb *msg, void *node_priv)
{
const struct in_addr zero_addr = {};
struct mgcp_conn_rtp *conn_src = OSMO_RTP_MSG_CTX(msg)->conn_src;
struct mgcp_conn_rtp *conn_dst = node_priv;
struct sockaddr_in *from_addr = OSMO_RTP_MSG_CTX(msg)->from_addr;
struct mgcp_rtp_end *rtp_end = &conn_dst->end;
struct in_addr to_addr = rtp_end->addr;
uint16_t to_port = rtp_end->rtp_port;
if (conn_src == conn_dst
&& !memcmp(&zero_addr, &to_addr, sizeof(zero_addr)) && !to_port) {
LOG_CONN_RTP(conn_dst, LOGL_DEBUG, "iuup_tx_msg(): direct IuUP reply\n");
/* IuUP wants to send a message back to the same peer that sent an RTP package, but there
* is no address configured for that peer yet. It is probably an IuUP Initialization ACK
* reply. Use the sender address to send the reply.
*
* During 3G RAB Assignment, a 3G cell might first probe the MGW and expect an IuUP
* Initialization ACK before it replies to the MSC with a successful RAB Assignment; only
* after that reply does MSC officially know which RTP address+port the 3G cell wants to
* use and can tell this MGW about it, so this "loopback" is, for some 3G cells, the only
* chance we have to get a successful RAB Assignment done (particularly the nano3G does
* this). */
to_addr = from_addr->sin_addr;
to_port = from_addr->sin_port;
}
LOG_CONN_RTP(conn_dst, LOGL_DEBUG, "iuup_tx_msg(%u bytes) to %s:%u\n", msg->len,
inet_ntoa(to_addr), ntohs(to_port));
return mgcp_udp_send(rtp_end->rtp.fd, &to_addr, to_port, (char*)msg->data, msg->len);
}
static void iuup_init(struct mgcp_conn_rtp *conn_src)
{
struct osmo_iuup_cn_cfg cfg = {
.node_priv = conn_src,
.rx_payload = rx_rtp_from_iuup,
.tx_msg = tx_pdu_from_iuup,
.rx_payload = iuup_rx_payload,
.tx_msg = iuup_tx_msg,
};
osmo_iuup_cn_init(conn_src, &cfg, "%d@ I:%s",
ENDPOINT_NUMBER(conn_src->conn->endp), conn_src->conn->id);
if (conn_src->iuup) {
LOG_CONN_RTP(conn_src, LOGL_NOTICE, "Rx IuUP init, but already initialized. Ignoring.\n");
return;
}
conn_src->iuup = osmo_iuup_cn_init(conn_src->conn, &cfg, "endp_%d_conn_%s",
ENDPOINT_NUMBER(conn_src->conn->endp), conn_src->conn->id);
}
/* Handle incoming RTP data from NET */
@@ -1170,6 +1204,7 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
socklen_t slen = sizeof(addr);
int ret;
enum rtp_proto proto;
struct osmo_rtp_msg_ctx mc;
struct msgb *msg = msgb_alloc_headroom(RTP_BUF_SIZE + OSMO_IUUP_HEADROOM,
OSMO_IUUP_HEADROOM, "RTP-rx");
@@ -1188,12 +1223,11 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
return -1;
}
/* By default, indicate that RTP payload starts right from the buffer's beginning. */
msg->l3h = msgb_put(msg, ret);
msgb_put(msg, ret);
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "%s: rx %u bytes from %s:%u\n",
proto == MGCP_PROTO_RTP ? "RTP" : "RTPC",
msgb_l3len(msg), inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
msg->len, inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
if ((proto == MGCP_PROTO_RTP && check_rtp(conn_src, msg))
|| (proto == MGCP_PROTO_RTCP && check_rtcp(conn_src, msg))) {
@@ -1206,32 +1240,47 @@ static int rtp_data_net(struct osmo_fd *fd, unsigned int what)
return 0;
}
mc = (struct osmo_rtp_msg_ctx){
.proto = proto,
.conn_src = conn_src,
.from_addr = &addr,
};
OSMO_RTP_MSG_CTX(msg) = &mc;
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "msg ctx: %d %p %s\n",
OSMO_RTP_MSG_CTX(msg)->proto,
OSMO_RTP_MSG_CTX(msg)->conn_src,
osmo_hexdump((void*)OSMO_RTP_MSG_CTX(msg)->from_addr, sizeof(struct sockaddr_in)));
/* Increment RX statistics */
rate_ctr_inc(&conn_src->rate_ctr_group->ctr[RTP_PACKETS_RX_CTR]);
rate_ctr_add(&conn_src->rate_ctr_group->ctr[RTP_OCTETS_RX_CTR], msgb_l3len(msg));
rate_ctr_add(&conn_src->rate_ctr_group->ctr[RTP_OCTETS_RX_CTR], msg->len);
/* FIXME: count RTP and RTCP separately, also count IuUP payload-less separately */
/* Forward a copy of the RTP data to a debug ip/port */
forward_data(fd->fd, &conn_src->tap_in, msg);
if (proto == MGCP_PROTO_RTP && osmo_iuup_is_init(msg))
init_iuup(conn_src);
iuup_init(conn_src);
if (conn_src->iuup && proto == MGCP_PROTO_RTP)
return osmo_iuup_cn_rx_pdu(conn_src->iuup, msg, &addr);
return osmo_iuup_cn_rx_pdu(conn_src->iuup, msg);
else
return rx_rtp(conn_src, msg, proto, &addr);
return rx_rtp(msg);
msgb_free(msg);
}
static int rx_rtp(struct mgcp_conn_rtp *conn_src, struct msgb *payload,
enum rtp_proto proto, struct sockaddr_in *from_addr)
static int rx_rtp(struct msgb *msg)
{
struct mgcp_endpoint *endp = conn_src->conn->endp;
struct mgcp_trunk_config *tcfg = endp->tcfg;
struct mgcp_conn_rtp *conn_src = OSMO_RTP_MSG_CTX(msg)->conn_src;
struct sockaddr_in *from_addr = OSMO_RTP_MSG_CTX(msg)->from_addr;
struct mgcp_conn *conn = conn_src->conn;
struct mgcp_trunk_config *tcfg = conn->endp->tcfg;
LOG_CONN_RTP(conn_src, LOGL_DEBUG, "rx_rtp(%u bytes)\n", msg->len);
/* Check if the connection is in loopback mode, if yes, just send the
* incoming data back to the origin */
if (conn_src->conn->mode == MGCP_CONN_LOOPBACK) {
if (conn->mode == MGCP_CONN_LOOPBACK) {
/* When we are in loopback mode, we loop back all incoming
* packets back to their origin. We will use the originating
* address data from the UDP packet header to patch the
@@ -1240,7 +1289,7 @@ static int rx_rtp(struct mgcp_conn_rtp *conn_src, struct msgb *payload,
conn_src->end.addr = from_addr->sin_addr;
conn_src->end.rtp_port = from_addr->sin_port;
}
return tx_rtp(conn_src, conn_src, proto, from_addr, payload);
return mgcp_send_rtp(conn_src, msg);
}
/* Check if the origin of the RTP packet seems plausible */
@@ -1249,16 +1298,7 @@ static int rx_rtp(struct mgcp_conn_rtp *conn_src, struct msgb *payload,
/* Execute endpoint specific implementation that handles the
* dispatching of the RTP data */
return conn_src->conn->endp->type->dispatch_rtp_cb(proto, from_addr, payload, conn_src->conn);
}
static int tx_rtp(struct mgcp_conn_rtp *conn_src, struct mgcp_conn_rtp *conn_dst,
enum rtp_proto proto, struct sockaddr_in *from_addr, struct msgb *payload)
{
if (conn_dst->iuup && proto == MGCP_PROTO_RTP)
return osmo_iuup_cn_tx_payload(conn_dst->iuup, payload, from_addr);
else
return mgcp_send_rtp(proto, from_addr, payload, conn_src, conn_dst);
return conn->endp->type->dispatch_rtp_cb(msg);
}
/*! set IP Type of Service parameter.

View File

@@ -244,6 +244,12 @@ static const struct log_info_cat log_categories[] = {
.color = "\033[1;30m",
.enabled = 1,.loglevel = LOGL_NOTICE,
},
[DIUUP] = {
.name = "DIUUP",
.description = "IuUP within RTP stream handling",
.color = "\033[1;31m",
.enabled = 1,.loglevel = LOGL_NOTICE,
},
};
const struct log_info log_info = {

View File

@@ -27,12 +27,12 @@ struct msgb *msgb_from_hex(const char *label, const char *hex)
}
const char *expect_rx_payload = NULL;
int rx_payload(struct msgb *msg, void *node_priv, void *pdu_priv)
int rx_payload(struct msgb *msg, void *node_priv)
{
printf("rx_payload() invoked by iuup_cn!\n");
printf(" [IuUP] -RTP->\n");
printf("%s\n", dump(msg));
printf("node_priv=%p pdu_priv=%p\n", node_priv, pdu_priv);
printf("node_priv=%p\n", node_priv);
if (!expect_rx_payload) {
printf("ERROR: did not expect rx_payload()\n");
exit(-1);
@@ -41,18 +41,17 @@ int rx_payload(struct msgb *msg, void *node_priv, void *pdu_priv)
exit(-1);
} else
printf("ok: matches expected msg\n");
msgb_free(msg);
expect_rx_payload = NULL;
return 0;
}
const char *expect_tx_msg = NULL;
int tx_msg(struct msgb *msg, void *node_priv, void *pdu_priv)
int tx_msg(struct msgb *msg, void *node_priv)
{
printf("tx_msg() invoked by iuup_cn!\n");
printf(" <-PDU- [IuUP]\n");
printf("%s\n", dump(msg));
printf("node_priv=%p pdu_priv=%p\n", node_priv, pdu_priv);
printf("node_priv=%p\n", node_priv);
if (!expect_tx_msg) {
printf("ERROR: did not expect tx_msg()\n");
exit(-1);
@@ -61,29 +60,26 @@ int tx_msg(struct msgb *msg, void *node_priv, void *pdu_priv)
exit(-1);
} else
printf("ok: matches expected msg\n");
msgb_free(msg);
expect_tx_msg = NULL;
return 0;
}
static int rx_pdu(struct osmo_iuup_cn *cn, struct msgb *msg, void *pdu_priv)
static int rx_pdu(struct osmo_iuup_cn *cn, struct msgb *msg)
{
int rc;
printf(" -PDU-> [IuUP]\n");
printf("%s\n", dump(msg));
printf("pdu_priv=%p\n", pdu_priv);
rc = osmo_iuup_cn_rx_pdu(cn, msg, pdu_priv);
rc = osmo_iuup_cn_rx_pdu(cn, msg);
printf("rc=%d\n", rc);
return rc;
}
static int tx_payload(struct osmo_iuup_cn *cn, struct msgb *msg, void *pdu_priv)
static int tx_payload(struct osmo_iuup_cn *cn, struct msgb *msg)
{
int rc;
printf(" [IuUP] <-RTP-\n");
printf("%s\n", dump(msg));
printf("pdu_priv=%p\n", pdu_priv);
rc = osmo_iuup_cn_tx_payload(cn, msg, pdu_priv);
rc = osmo_iuup_cn_tx_payload(cn, msg);
printf("rc=%d\n", rc);
return rc;
}
@@ -91,7 +87,6 @@ static int tx_payload(struct osmo_iuup_cn *cn, struct msgb *msg, void *pdu_priv)
void test_cn_session()
{
void *node_priv = (void*)0x2342;
void *pdu_priv = (void*)0xfeed;
struct osmo_iuup_cn_cfg cfg = {
.node_priv = node_priv,
@@ -103,13 +98,13 @@ void test_cn_session()
OSMO_ASSERT(cn);
printf("\nSend IuUP Initialization. Expecting direct tx_msg() of the Initialization Ack\n");
expect_tx_msg = "e400240000";
expect_tx_msg = "8060dc5219495e3f00010111" /* RTP header */
"e4002400"; /* IuUP Init Ack */
rx_pdu(cn,
msgb_from_hex("IuUP-Init",
"8060dc5219495e3f00010111" /* <- RTP header */
"e000df99" /* <- IuUP header */
"160051673c01270000820000001710000100" /* IuUP params */),
pdu_priv);
"160051673c01270000820000001710000100" /* IuUP params */));
#define RTP_HEADER "8060944c6256042c00010102"
#define IUUP_HEADER "0100e2b3"
@@ -119,28 +114,23 @@ void test_cn_session()
expect_rx_payload = RTP_HEADER RTP_PAYLOAD;
rx_pdu(cn,
msgb_from_hex("IuUP-Data",
RTP_HEADER IUUP_HEADER RTP_PAYLOAD),
pdu_priv);
RTP_HEADER IUUP_HEADER RTP_PAYLOAD));
printf("\nTransmit RTP. Expecting tx_msg() with inserted IuUP header\n");
expect_tx_msg = RTP_HEADER "000002b3" RTP_PAYLOAD;
tx_payload(cn,
msgb_from_hex("RTP data", RTP_HEADER RTP_PAYLOAD),
pdu_priv);
msgb_from_hex("RTP data", RTP_HEADER RTP_PAYLOAD));
printf("\nMore RTP, each time the Frame Nr advances, causing a new header CRC.\n");
expect_tx_msg = RTP_HEADER "0100e2b3" RTP_PAYLOAD;
tx_payload(cn,
msgb_from_hex("RTP data", RTP_HEADER RTP_PAYLOAD),
pdu_priv);
msgb_from_hex("RTP data", RTP_HEADER RTP_PAYLOAD));
expect_tx_msg = RTP_HEADER "02007eb3" RTP_PAYLOAD;
tx_payload(cn,
msgb_from_hex("RTP data", RTP_HEADER RTP_PAYLOAD),
pdu_priv);
msgb_from_hex("RTP data", RTP_HEADER RTP_PAYLOAD));
expect_tx_msg = RTP_HEADER "03009eb3" RTP_PAYLOAD;
tx_payload(cn,
msgb_from_hex("RTP data", RTP_HEADER RTP_PAYLOAD),
pdu_priv);
msgb_from_hex("RTP data", RTP_HEADER RTP_PAYLOAD));
printf("All done.\n");
}

View File

@@ -2,11 +2,10 @@
Send IuUP Initialization. Expecting direct tx_msg() of the Initialization Ack
-PDU-> [IuUP]
8060dc5219495e3f00010111e000df99160051673c01270000820000001710000100
pdu_priv=0xfeed
tx_msg() invoked by iuup_cn!
<-PDU- [IuUP]
e400240000
node_priv=0x2342 pdu_priv=0xfeed
8060dc5219495e3f00010111e4002400
node_priv=0x2342
ok: matches expected msg
rc=0
@@ -14,51 +13,46 @@ Receive payload encapsulated in IuUP. Expecting rx_payload() of just RTP packet
i.e. should strip away 0100e2b3
-PDU-> [IuUP]
8060944c6256042c000101020100e2b36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
pdu_priv=0xfeed
rx_payload() invoked by iuup_cn!
[IuUP] -RTP->
8060944c6256042c000101026cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
node_priv=0x2342 pdu_priv=0xfeed
node_priv=0x2342
ok: matches expected msg
rc=0
Transmit RTP. Expecting tx_msg() with inserted IuUP header
[IuUP] <-RTP-
8060944c6256042c000101026cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
pdu_priv=0xfeed
tx_msg() invoked by iuup_cn!
<-PDU- [IuUP]
8060944c6256042c00010102000002b36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
node_priv=0x2342 pdu_priv=0xfeed
node_priv=0x2342
ok: matches expected msg
rc=0
More RTP, each time the Frame Nr advances, causing a new header CRC.
[IuUP] <-RTP-
8060944c6256042c000101026cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
pdu_priv=0xfeed
tx_msg() invoked by iuup_cn!
<-PDU- [IuUP]
8060944c6256042c000101020100e2b36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
node_priv=0x2342 pdu_priv=0xfeed
node_priv=0x2342
ok: matches expected msg
rc=0
[IuUP] <-RTP-
8060944c6256042c000101026cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
pdu_priv=0xfeed
tx_msg() invoked by iuup_cn!
<-PDU- [IuUP]
8060944c6256042c0001010202007eb36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
node_priv=0x2342 pdu_priv=0xfeed
node_priv=0x2342
ok: matches expected msg
rc=0
[IuUP] <-RTP-
8060944c6256042c000101026cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
pdu_priv=0xfeed
tx_msg() invoked by iuup_cn!
<-PDU- [IuUP]
8060944c6256042c0001010203009eb36cfb23bc46d18180c3e5ffe040045600005a7d35b625b80005fff03214ced0
node_priv=0x2342 pdu_priv=0xfeed
node_priv=0x2342
ok: matches expected msg
rc=0
All done.