blob: 6a7e3b53912168cc41f8342b7f89a153ce5bb561 [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
44struct osmo_iuup_cn {
45 struct osmo_iuup_cn_cfg cfg;
46 char *name;
47 uint8_t next_frame_nr;
Neels Hofmeyr85f94012018-10-08 03:36:45 +020048 int rtp_payload_type;
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +020049};
50
51struct osmo_iuup_cn *osmo_iuup_cn_init(void *ctx, struct osmo_iuup_cn_cfg *cfg,
52 const char *name_fmt, ...)
53{
54 va_list ap;
55 struct osmo_iuup_cn *cn = talloc_zero(ctx, struct osmo_iuup_cn);
56 OSMO_ASSERT(cn);
57
58 cn->cfg = *cfg;
59
60 if (!name_fmt)
61 name_fmt = "-";
62
63 va_start(ap, name_fmt);
64 cn->name = talloc_vasprintf(cn, name_fmt, ap);
65 va_end(ap);
66
Neels Hofmeyr85f94012018-10-08 03:36:45 +020067 LOGP(DIUUP, LOGL_INFO, "(%s) Initializing IuUP node\n", cn->name);
68
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +020069 if (!osmo_identifier_valid(cn->name)) {
Neels Hofmeyr85f94012018-10-08 03:36:45 +020070 LOGP(DIUUP, LOGL_ERROR, "Attempting to set illegal id for IuUP CN instance: %s\n",
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +020071 osmo_quote_str(cn->name, -1));
72 talloc_free(cn);
73 return NULL;
74 }
75
76 return cn;
77}
78
79void osmo_iuup_cn_free(struct osmo_iuup_cn *cn)
80{
81 talloc_free(cn);
82}
83
84static int rx_data(struct osmo_iuup_cn *cn, struct msgb *pdu,
Neels Hofmeyr85f94012018-10-08 03:36:45 +020085 struct osmo_iuup_hdr_data *hdr)
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +020086{
87 /* Remove the IuUP bit from the middle of the buffer by writing the RTP header forward. */
efistoklfdfa1842018-12-21 20:25:43 +020088 /* And append AMR 12.2k header "0xf03c". - AD HOC fix */
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +020089 unsigned int pre_hdr_len = ((uint8_t*)hdr) - pdu->data;
efistoklf2c15232018-12-24 23:21:06 +020090
91 int is_comfort_noise = ((pdu->len - pre_hdr_len) == 9);
92
efistoklfdfa1842018-12-21 20:25:43 +020093 memmove(pdu->data + sizeof(*hdr) - 2, pdu->data, pre_hdr_len);
94 ((uint8_t*)hdr)[2] = 0xf0;
efistoklf2c15232018-12-24 23:21:06 +020095 ((uint8_t*)hdr)[3] = is_comfort_noise ? 0x44 : 0x3c;
efistoklfdfa1842018-12-21 20:25:43 +020096 msgb_pull(pdu, sizeof(*hdr) - 2);
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +020097
Neels Hofmeyr85f94012018-10-08 03:36:45 +020098 LOGP(DIUUP, LOGL_DEBUG, "(%s) IuUP stripping IuUP header from RTP data\n", cn->name);
99 cn->cfg.rx_payload(pdu, cn->cfg.node_priv);
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200100
101 return 0;
102}
103
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200104static int tx_init_ack(struct osmo_iuup_cn *cn, struct msgb *src_pdu)
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200105{
106 /* Send Initialization Ack PDU back to the sender */
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200107 int rc;
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200108 struct msgb *ack = msgb_alloc(4096, "IuUP Initialization Ack");
109 OSMO_ASSERT(ack);
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200110 ack->dst = src_pdu->dst;
111
112 /* Just copy the RTP header that was sent... TODO: tweak some RTP values?? */
113 memcpy(msgb_put(ack, sizeof(struct rtp_hdr)), src_pdu->data, sizeof(struct rtp_hdr));
114
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200115 osmo_iuup_make_init_ack(ack);
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200116
117 LOGP(DIUUP, LOGL_DEBUG, "(%s) Sending Initialization ACK %p\n", cn->name, cn->cfg.node_priv);
118 rc = cn->cfg.tx_msg(ack, cn->cfg.node_priv);
119 msgb_free(ack);
120 return rc;
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200121}
122
123static int rx_control(struct osmo_iuup_cn *cn, struct msgb *pdu,
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200124 struct osmo_iuup_hdr_ctrl *hdr)
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200125{
126 switch (hdr->procedure) {
127 case OSMO_IUUP_PROC_INITIALIZATION:
128 switch (hdr->ack_nack) {
129 case OSMO_IUUP_ACKNACK_PROCEDURE:
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200130 LOGP(DIUUP, LOGL_INFO, "(%s) Rx IuUP Initialization, sending ACK\n", cn->name);
131 cn->rtp_payload_type = ((struct rtp_hdr*)pdu->data)->payload_type;
132 return tx_init_ack(cn, pdu);
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200133
134 default:
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200135 LOGP(DIUUP, LOGL_DEBUG, "(%s) Rx IuUP Initialization, unhandled ack_nack = %d\n",
136 cn->name, hdr->ack_nack);
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200137 break;
138 }
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200139 /* Continue to log "unexpected procedure" below. */
140 break;
141
142 case OSMO_IUUP_PROC_ERROR_EVENT:
143 {
144 union osmo_iuup_hdr_ctrl_payload *p = (void*)hdr->payload;
145 LOGP(DIUUP, LOGL_ERROR,
146 "(%s) Rx IuUP Error Event: distance=%u, cause=%u=\"%s\"\n",
147 cn->name, p->error_event.error_distance, p->error_event.error_cause,
148 osmo_iuup_error_cause_name(p->error_event.error_cause));
149 return 0;
150 }
151
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200152 default:
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200153 break;
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200154 }
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200155 LOG_IUUP_CN(cn, LOGL_ERROR,
156 "Rx control PDU with unexpected procedure: 0x%x acknack=0x%x\n",
157 hdr->procedure, hdr->ack_nack);
158 return -EINVAL;
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200159}
160
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200161/* Feed a received PDU to the IuUP CN node. This function takes ownership of the msgb, it must not be
162 * freed by the caller. */
163int osmo_iuup_cn_rx_pdu(struct osmo_iuup_cn *cn, struct msgb *pdu)
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200164{
165 struct osmo_iuup_hdr_ctrl *is_ctrl;
166 struct osmo_iuup_hdr_data *is_data;
167 int rc;
168
169 rc = osmo_iuup_classify(true, cn->name, pdu, &is_ctrl, &is_data);
170 if (rc)
171 return rc;
172
173 if (is_ctrl)
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200174 return rx_control(cn, pdu, is_ctrl);
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200175 if (is_data)
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200176 return rx_data(cn, pdu, is_data);
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200177 return rc;
178}
179
180static uint8_t next_frame_nr(struct osmo_iuup_cn *cn)
181{
182 uint8_t frame_nr = cn->next_frame_nr;
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200183 cn->next_frame_nr = (frame_nr + 1) & 0x0f;
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200184 return frame_nr;
185}
186
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200187/* Send this RTP packet to the IuUP peer: add IuUP header and call the tx_msg() to transmit the resulting
188 * message to the IuUP peer.
189 * Returns 0 on success, negative on error. */
190int osmo_iuup_cn_tx_payload(struct osmo_iuup_cn *cn, struct msgb *pdu)
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200191{
192 struct rtp_hdr *rtp_was, *rtp;
193 struct osmo_iuup_hdr_data *iuup_hdr;
194
195 /* Splice an IuUP header in between RTP header and payload data */
196 rtp_was = (void*)pdu->data;
197
198 /* copy the RTP header part backwards by the size needed for the IuUP header */
efistoklfdfa1842018-12-21 20:25:43 +0200199 /* also strips 2 bytes from the front of RTP payload - AMR header - AD HOC fix */
200 rtp = (void*)msgb_push(pdu, sizeof(*iuup_hdr) - 2);
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200201 memmove(rtp, rtp_was, sizeof(*rtp));
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200202
203 /* Send the same payload type to the peer (erm...) */
204 rtp->payload_type = cn->rtp_payload_type;
205
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200206 iuup_hdr = (void*)rtp->data;
207
208 *iuup_hdr = (struct osmo_iuup_hdr_data){
209 .pdu_type = OSMO_IUUP_PDU_DATA_WITH_CRC,
210 .frame_nr = next_frame_nr(cn),
211 .frame_good = OSMO_IUUP_FRAME_GOOD,
212 };
213
214 osmo_iuup_set_checksums((uint8_t*)iuup_hdr, pdu->tail - (uint8_t*)iuup_hdr);
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200215 LOGP(DIUUP, LOGL_DEBUG, "(%s) IuUP inserting IuUP header in RTP data (frame nr %u)\n",
216 cn->name, iuup_hdr->frame_nr);
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200217
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200218 return cn->cfg.tx_msg(pdu, cn->cfg.node_priv);
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200219}