blob: fda7a7afd52034660745e292c675d949e614d442 [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;
efistoklfdfa1842018-12-21 20:25:43 +020090 memmove(pdu->data + sizeof(*hdr) - 2, pdu->data, pre_hdr_len);
91 ((uint8_t*)hdr)[2] = 0xf0;
92 ((uint8_t*)hdr)[3] = 0x3c;
93 msgb_pull(pdu, sizeof(*hdr) - 2);
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +020094
Neels Hofmeyr85f94012018-10-08 03:36:45 +020095 LOGP(DIUUP, LOGL_DEBUG, "(%s) IuUP stripping IuUP header from RTP data\n", cn->name);
96 cn->cfg.rx_payload(pdu, cn->cfg.node_priv);
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +020097
98 return 0;
99}
100
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200101static int tx_init_ack(struct osmo_iuup_cn *cn, struct msgb *src_pdu)
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200102{
103 /* Send Initialization Ack PDU back to the sender */
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200104 int rc;
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200105 struct msgb *ack = msgb_alloc(4096, "IuUP Initialization Ack");
106 OSMO_ASSERT(ack);
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200107 ack->dst = src_pdu->dst;
108
109 /* Just copy the RTP header that was sent... TODO: tweak some RTP values?? */
110 memcpy(msgb_put(ack, sizeof(struct rtp_hdr)), src_pdu->data, sizeof(struct rtp_hdr));
111
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200112 osmo_iuup_make_init_ack(ack);
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200113
114 LOGP(DIUUP, LOGL_DEBUG, "(%s) Sending Initialization ACK %p\n", cn->name, cn->cfg.node_priv);
115 rc = cn->cfg.tx_msg(ack, cn->cfg.node_priv);
116 msgb_free(ack);
117 return rc;
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200118}
119
120static int rx_control(struct osmo_iuup_cn *cn, struct msgb *pdu,
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200121 struct osmo_iuup_hdr_ctrl *hdr)
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200122{
123 switch (hdr->procedure) {
124 case OSMO_IUUP_PROC_INITIALIZATION:
125 switch (hdr->ack_nack) {
126 case OSMO_IUUP_ACKNACK_PROCEDURE:
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200127 LOGP(DIUUP, LOGL_INFO, "(%s) Rx IuUP Initialization, sending ACK\n", cn->name);
128 cn->rtp_payload_type = ((struct rtp_hdr*)pdu->data)->payload_type;
129 return tx_init_ack(cn, pdu);
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200130
131 default:
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200132 LOGP(DIUUP, LOGL_DEBUG, "(%s) Rx IuUP Initialization, unhandled ack_nack = %d\n",
133 cn->name, hdr->ack_nack);
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200134 break;
135 }
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200136 /* Continue to log "unexpected procedure" below. */
137 break;
138
139 case OSMO_IUUP_PROC_ERROR_EVENT:
140 {
141 union osmo_iuup_hdr_ctrl_payload *p = (void*)hdr->payload;
142 LOGP(DIUUP, LOGL_ERROR,
143 "(%s) Rx IuUP Error Event: distance=%u, cause=%u=\"%s\"\n",
144 cn->name, p->error_event.error_distance, p->error_event.error_cause,
145 osmo_iuup_error_cause_name(p->error_event.error_cause));
146 return 0;
147 }
148
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200149 default:
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200150 break;
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200151 }
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200152 LOG_IUUP_CN(cn, LOGL_ERROR,
153 "Rx control PDU with unexpected procedure: 0x%x acknack=0x%x\n",
154 hdr->procedure, hdr->ack_nack);
155 return -EINVAL;
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200156}
157
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200158/* Feed a received PDU to the IuUP CN node. This function takes ownership of the msgb, it must not be
159 * freed by the caller. */
160int osmo_iuup_cn_rx_pdu(struct osmo_iuup_cn *cn, struct msgb *pdu)
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200161{
162 struct osmo_iuup_hdr_ctrl *is_ctrl;
163 struct osmo_iuup_hdr_data *is_data;
164 int rc;
165
166 rc = osmo_iuup_classify(true, cn->name, pdu, &is_ctrl, &is_data);
167 if (rc)
168 return rc;
169
170 if (is_ctrl)
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200171 return rx_control(cn, pdu, is_ctrl);
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200172 if (is_data)
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200173 return rx_data(cn, pdu, is_data);
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200174 return rc;
175}
176
177static uint8_t next_frame_nr(struct osmo_iuup_cn *cn)
178{
179 uint8_t frame_nr = cn->next_frame_nr;
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200180 cn->next_frame_nr = (frame_nr + 1) & 0x0f;
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200181 return frame_nr;
182}
183
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200184/* Send this RTP packet to the IuUP peer: add IuUP header and call the tx_msg() to transmit the resulting
185 * message to the IuUP peer.
186 * Returns 0 on success, negative on error. */
187int osmo_iuup_cn_tx_payload(struct osmo_iuup_cn *cn, struct msgb *pdu)
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200188{
189 struct rtp_hdr *rtp_was, *rtp;
190 struct osmo_iuup_hdr_data *iuup_hdr;
191
192 /* Splice an IuUP header in between RTP header and payload data */
193 rtp_was = (void*)pdu->data;
194
195 /* copy the RTP header part backwards by the size needed for the IuUP header */
efistoklfdfa1842018-12-21 20:25:43 +0200196 /* also strips 2 bytes from the front of RTP payload - AMR header - AD HOC fix */
197 rtp = (void*)msgb_push(pdu, sizeof(*iuup_hdr) - 2);
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200198 memmove(rtp, rtp_was, sizeof(*rtp));
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200199
200 /* Send the same payload type to the peer (erm...) */
201 rtp->payload_type = cn->rtp_payload_type;
202
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200203 iuup_hdr = (void*)rtp->data;
204
205 *iuup_hdr = (struct osmo_iuup_hdr_data){
206 .pdu_type = OSMO_IUUP_PDU_DATA_WITH_CRC,
207 .frame_nr = next_frame_nr(cn),
208 .frame_good = OSMO_IUUP_FRAME_GOOD,
209 };
210
211 osmo_iuup_set_checksums((uint8_t*)iuup_hdr, pdu->tail - (uint8_t*)iuup_hdr);
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200212 LOGP(DIUUP, LOGL_DEBUG, "(%s) IuUP inserting IuUP header in RTP data (frame nr %u)\n",
213 cn->name, iuup_hdr->frame_nr);
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200214
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200215 return cn->cfg.tx_msg(pdu, cn->cfg.node_priv);
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200216}