blob: 1c5599b57adf4900659c1563496860a04b411508 [file] [log] [blame]
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +02001/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
2/* IuUP Core Network side protocol handling, minimal implementation */
3
4/*
5 * (C) 2018 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
6 * All Rights Reserved
7 *
8 * Author: Neels Hofmeyr <neels@hofmeyr.de>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU Affero General Public License as published by
12 * the Free Software Foundation; either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Affero General Public License for more details.
19 *
20 * You should have received a copy of the GNU Affero General Public License
21 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 *
23 */
24
25#include <talloc.h>
26#include <errno.h>
27#include <string.h>
28#include <arpa/inet.h>
29
30#include <osmocom/core/utils.h>
31#include <osmocom/core/logging.h>
32#include <osmocom/core/msgb.h>
33
34#include <osmocom/netif/rtp.h>
35
36#include <osmocom/mgcp/iuup_cn_node.h>
37#include <osmocom/mgcp/iuup_protocol.h>
38
39#include <osmocom/mgcp/debug.h>
40
41#define LOG_IUUP_CN(cn, level, fmt, args...) \
Neels Hofmeyr85f94012018-10-08 03:36:45 +020042 LOGP(DIUUP, level, "(%s) " fmt, (cn)->name, ## args)
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +020043
efistokla18c6372018-12-25 14:52:30 +020044#define AMR_HEADER_LENGTH 2
45#define AMR_COMFORT_NOISE_PAYLOAD_LENGTH 5
46
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +020047struct osmo_iuup_cn {
48 struct osmo_iuup_cn_cfg cfg;
49 char *name;
50 uint8_t next_frame_nr;
Neels Hofmeyr85f94012018-10-08 03:36:45 +020051 int rtp_payload_type;
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +020052};
53
54struct osmo_iuup_cn *osmo_iuup_cn_init(void *ctx, struct osmo_iuup_cn_cfg *cfg,
55 const char *name_fmt, ...)
56{
57 va_list ap;
58 struct osmo_iuup_cn *cn = talloc_zero(ctx, struct osmo_iuup_cn);
59 OSMO_ASSERT(cn);
60
61 cn->cfg = *cfg;
62
63 if (!name_fmt)
64 name_fmt = "-";
65
66 va_start(ap, name_fmt);
67 cn->name = talloc_vasprintf(cn, name_fmt, ap);
68 va_end(ap);
69
Neels Hofmeyr85f94012018-10-08 03:36:45 +020070 LOGP(DIUUP, LOGL_INFO, "(%s) Initializing IuUP node\n", cn->name);
71
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +020072 if (!osmo_identifier_valid(cn->name)) {
Neels Hofmeyr85f94012018-10-08 03:36:45 +020073 LOGP(DIUUP, LOGL_ERROR, "Attempting to set illegal id for IuUP CN instance: %s\n",
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +020074 osmo_quote_str(cn->name, -1));
75 talloc_free(cn);
76 return NULL;
77 }
78
79 return cn;
80}
81
82void osmo_iuup_cn_free(struct osmo_iuup_cn *cn)
83{
84 talloc_free(cn);
85}
86
87static int rx_data(struct osmo_iuup_cn *cn, struct msgb *pdu,
Neels Hofmeyr85f94012018-10-08 03:36:45 +020088 struct osmo_iuup_hdr_data *hdr)
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +020089{
efistokla18c6372018-12-25 14:52:30 +020090 /* Strip the IuUP bits from the middle of the buffer by writing the RTP
91 * header forward by the length of IuUP header minus the length of AMR
92 * header. Replace the rest of IuUP header with AMR header */
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +020093 unsigned int pre_hdr_len = ((uint8_t*)hdr) - pdu->data;
efistoklf2c15232018-12-24 23:21:06 +020094
efistokla18c6372018-12-25 14:52:30 +020095 int is_comfort_noise = ((pdu->len - pre_hdr_len - sizeof(*hdr)) == AMR_COMFORT_NOISE_PAYLOAD_LENGTH);
efistoklf2c15232018-12-24 23:21:06 +020096
efistokla18c6372018-12-25 14:52:30 +020097 memmove(pdu->data + sizeof(*hdr) - AMR_HEADER_LENGTH, pdu->data, pre_hdr_len);
efistokl3a4cddb2018-12-24 23:25:55 +020098 ((uint8_t*)hdr)[2] = 0x70;
efistoklf2c15232018-12-24 23:21:06 +020099 ((uint8_t*)hdr)[3] = is_comfort_noise ? 0x44 : 0x3c;
efistokla18c6372018-12-25 14:52:30 +0200100 msgb_pull(pdu, sizeof(*hdr) - AMR_HEADER_LENGTH);
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200101
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200102 LOGP(DIUUP, LOGL_DEBUG, "(%s) IuUP stripping IuUP header from RTP data\n", cn->name);
103 cn->cfg.rx_payload(pdu, cn->cfg.node_priv);
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200104
105 return 0;
106}
107
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200108static int tx_init_ack(struct osmo_iuup_cn *cn, struct msgb *src_pdu)
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200109{
110 /* Send Initialization Ack PDU back to the sender */
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200111 int rc;
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200112 struct msgb *ack = msgb_alloc(4096, "IuUP Initialization Ack");
113 OSMO_ASSERT(ack);
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200114 ack->dst = src_pdu->dst;
115
116 /* Just copy the RTP header that was sent... TODO: tweak some RTP values?? */
117 memcpy(msgb_put(ack, sizeof(struct rtp_hdr)), src_pdu->data, sizeof(struct rtp_hdr));
118
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200119 osmo_iuup_make_init_ack(ack);
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200120
121 LOGP(DIUUP, LOGL_DEBUG, "(%s) Sending Initialization ACK %p\n", cn->name, cn->cfg.node_priv);
122 rc = cn->cfg.tx_msg(ack, cn->cfg.node_priv);
123 msgb_free(ack);
124 return rc;
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200125}
126
127static int rx_control(struct osmo_iuup_cn *cn, struct msgb *pdu,
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200128 struct osmo_iuup_hdr_ctrl *hdr)
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200129{
130 switch (hdr->procedure) {
131 case OSMO_IUUP_PROC_INITIALIZATION:
132 switch (hdr->ack_nack) {
133 case OSMO_IUUP_ACKNACK_PROCEDURE:
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200134 LOGP(DIUUP, LOGL_INFO, "(%s) Rx IuUP Initialization, sending ACK\n", cn->name);
135 cn->rtp_payload_type = ((struct rtp_hdr*)pdu->data)->payload_type;
136 return tx_init_ack(cn, pdu);
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200137
138 default:
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200139 LOGP(DIUUP, LOGL_DEBUG, "(%s) Rx IuUP Initialization, unhandled ack_nack = %d\n",
140 cn->name, hdr->ack_nack);
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200141 break;
142 }
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200143 /* Continue to log "unexpected procedure" below. */
144 break;
145
146 case OSMO_IUUP_PROC_ERROR_EVENT:
147 {
148 union osmo_iuup_hdr_ctrl_payload *p = (void*)hdr->payload;
149 LOGP(DIUUP, LOGL_ERROR,
150 "(%s) Rx IuUP Error Event: distance=%u, cause=%u=\"%s\"\n",
151 cn->name, p->error_event.error_distance, p->error_event.error_cause,
152 osmo_iuup_error_cause_name(p->error_event.error_cause));
153 return 0;
154 }
155
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200156 default:
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200157 break;
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200158 }
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200159 LOG_IUUP_CN(cn, LOGL_ERROR,
160 "Rx control PDU with unexpected procedure: 0x%x acknack=0x%x\n",
161 hdr->procedure, hdr->ack_nack);
162 return -EINVAL;
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200163}
164
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200165/* Feed a received PDU to the IuUP CN node. This function takes ownership of the msgb, it must not be
166 * freed by the caller. */
167int osmo_iuup_cn_rx_pdu(struct osmo_iuup_cn *cn, struct msgb *pdu)
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200168{
169 struct osmo_iuup_hdr_ctrl *is_ctrl;
170 struct osmo_iuup_hdr_data *is_data;
171 int rc;
172
173 rc = osmo_iuup_classify(true, cn->name, pdu, &is_ctrl, &is_data);
174 if (rc)
175 return rc;
176
177 if (is_ctrl)
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200178 return rx_control(cn, pdu, is_ctrl);
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200179 if (is_data)
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200180 return rx_data(cn, pdu, is_data);
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200181 return rc;
182}
183
184static uint8_t next_frame_nr(struct osmo_iuup_cn *cn)
185{
186 uint8_t frame_nr = cn->next_frame_nr;
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200187 cn->next_frame_nr = (frame_nr + 1) & 0x0f;
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200188 return frame_nr;
189}
190
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200191/* Send this RTP packet to the IuUP peer: add IuUP header and call the tx_msg() to transmit the resulting
192 * message to the IuUP peer.
193 * Returns 0 on success, negative on error. */
194int osmo_iuup_cn_tx_payload(struct osmo_iuup_cn *cn, struct msgb *pdu)
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200195{
196 struct rtp_hdr *rtp_was, *rtp;
197 struct osmo_iuup_hdr_data *iuup_hdr;
198
199 /* Splice an IuUP header in between RTP header and payload data */
200 rtp_was = (void*)pdu->data;
201
efistokla18c6372018-12-25 14:52:30 +0200202 /* copy the RTP header part backwards by the size needed for the IuUP header
203 * minus AMR header bytes from the front of RTP payload */
204 rtp = (void*)msgb_push(pdu, sizeof(*iuup_hdr) - AMR_HEADER_LENGTH);
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200205 memmove(rtp, rtp_was, sizeof(*rtp));
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200206
207 /* Send the same payload type to the peer (erm...) */
208 rtp->payload_type = cn->rtp_payload_type;
209
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200210 iuup_hdr = (void*)rtp->data;
211
212 *iuup_hdr = (struct osmo_iuup_hdr_data){
213 .pdu_type = OSMO_IUUP_PDU_DATA_WITH_CRC,
214 .frame_nr = next_frame_nr(cn),
215 .frame_good = OSMO_IUUP_FRAME_GOOD,
216 };
217
218 osmo_iuup_set_checksums((uint8_t*)iuup_hdr, pdu->tail - (uint8_t*)iuup_hdr);
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200219 LOGP(DIUUP, LOGL_DEBUG, "(%s) IuUP inserting IuUP header in RTP data (frame nr %u)\n",
220 cn->name, iuup_hdr->frame_nr);
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200221
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200222 return cn->cfg.tx_msg(pdu, cn->cfg.node_priv);
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200223}