blob: f555e364bc7be5a6ebb903c3303f46144ffd6327 [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. */
88 unsigned int pre_hdr_len = ((uint8_t*)hdr) - pdu->data;
89 memmove(pdu->data + sizeof(*hdr), pdu->data, pre_hdr_len);
90
91 msgb_pull(pdu, sizeof(*hdr));
92
Neels Hofmeyr85f94012018-10-08 03:36:45 +020093 LOGP(DIUUP, LOGL_DEBUG, "(%s) IuUP stripping IuUP header from RTP data\n", cn->name);
94 cn->cfg.rx_payload(pdu, cn->cfg.node_priv);
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +020095
96 return 0;
97}
98
Neels Hofmeyr85f94012018-10-08 03:36:45 +020099static int tx_init_ack(struct osmo_iuup_cn *cn, struct msgb *src_pdu)
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200100{
101 /* Send Initialization Ack PDU back to the sender */
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200102 int rc;
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200103 struct msgb *ack = msgb_alloc(4096, "IuUP Initialization Ack");
104 OSMO_ASSERT(ack);
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200105 ack->dst = src_pdu->dst;
106
107 /* Just copy the RTP header that was sent... TODO: tweak some RTP values?? */
108 memcpy(msgb_put(ack, sizeof(struct rtp_hdr)), src_pdu->data, sizeof(struct rtp_hdr));
109
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200110 osmo_iuup_make_init_ack(ack);
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200111
112 LOGP(DIUUP, LOGL_DEBUG, "(%s) Sending Initialization ACK %p\n", cn->name, cn->cfg.node_priv);
113 rc = cn->cfg.tx_msg(ack, cn->cfg.node_priv);
114 msgb_free(ack);
115 return rc;
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200116}
117
118static int rx_control(struct osmo_iuup_cn *cn, struct msgb *pdu,
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200119 struct osmo_iuup_hdr_ctrl *hdr)
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200120{
121 switch (hdr->procedure) {
122 case OSMO_IUUP_PROC_INITIALIZATION:
123 switch (hdr->ack_nack) {
124 case OSMO_IUUP_ACKNACK_PROCEDURE:
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200125 LOGP(DIUUP, LOGL_INFO, "(%s) Rx IuUP Initialization, sending ACK\n", cn->name);
126 cn->rtp_payload_type = ((struct rtp_hdr*)pdu->data)->payload_type;
127 return tx_init_ack(cn, pdu);
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200128
129 default:
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200130 LOGP(DIUUP, LOGL_DEBUG, "(%s) Rx IuUP Initialization, unhandled ack_nack = %d\n",
131 cn->name, hdr->ack_nack);
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200132 break;
133 }
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200134 /* Continue to log "unexpected procedure" below. */
135 break;
136
137 case OSMO_IUUP_PROC_ERROR_EVENT:
138 {
139 union osmo_iuup_hdr_ctrl_payload *p = (void*)hdr->payload;
140 LOGP(DIUUP, LOGL_ERROR,
141 "(%s) Rx IuUP Error Event: distance=%u, cause=%u=\"%s\"\n",
142 cn->name, p->error_event.error_distance, p->error_event.error_cause,
143 osmo_iuup_error_cause_name(p->error_event.error_cause));
144 return 0;
145 }
146
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200147 default:
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200148 break;
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200149 }
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200150 LOG_IUUP_CN(cn, LOGL_ERROR,
151 "Rx control PDU with unexpected procedure: 0x%x acknack=0x%x\n",
152 hdr->procedure, hdr->ack_nack);
153 return -EINVAL;
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200154}
155
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200156/* Feed a received PDU to the IuUP CN node. This function takes ownership of the msgb, it must not be
157 * freed by the caller. */
158int osmo_iuup_cn_rx_pdu(struct osmo_iuup_cn *cn, struct msgb *pdu)
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200159{
160 struct osmo_iuup_hdr_ctrl *is_ctrl;
161 struct osmo_iuup_hdr_data *is_data;
162 int rc;
163
164 rc = osmo_iuup_classify(true, cn->name, pdu, &is_ctrl, &is_data);
165 if (rc)
166 return rc;
167
168 if (is_ctrl)
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200169 return rx_control(cn, pdu, is_ctrl);
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200170 if (is_data)
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200171 return rx_data(cn, pdu, is_data);
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200172 return rc;
173}
174
175static uint8_t next_frame_nr(struct osmo_iuup_cn *cn)
176{
177 uint8_t frame_nr = cn->next_frame_nr;
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200178 cn->next_frame_nr = (frame_nr + 1) & 0x0f;
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200179 return frame_nr;
180}
181
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200182/* Send this RTP packet to the IuUP peer: add IuUP header and call the tx_msg() to transmit the resulting
183 * message to the IuUP peer.
184 * Returns 0 on success, negative on error. */
185int osmo_iuup_cn_tx_payload(struct osmo_iuup_cn *cn, struct msgb *pdu)
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200186{
187 struct rtp_hdr *rtp_was, *rtp;
188 struct osmo_iuup_hdr_data *iuup_hdr;
189
190 /* Splice an IuUP header in between RTP header and payload data */
191 rtp_was = (void*)pdu->data;
192
193 /* copy the RTP header part backwards by the size needed for the IuUP header */
194 rtp = (void*)msgb_push(pdu, sizeof(*iuup_hdr));
195 memmove(rtp, rtp_was, sizeof(*rtp));
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200196
197 /* Send the same payload type to the peer (erm...) */
198 rtp->payload_type = cn->rtp_payload_type;
199
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200200 iuup_hdr = (void*)rtp->data;
201
202 *iuup_hdr = (struct osmo_iuup_hdr_data){
203 .pdu_type = OSMO_IUUP_PDU_DATA_WITH_CRC,
204 .frame_nr = next_frame_nr(cn),
205 .frame_good = OSMO_IUUP_FRAME_GOOD,
206 };
207
208 osmo_iuup_set_checksums((uint8_t*)iuup_hdr, pdu->tail - (uint8_t*)iuup_hdr);
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200209 LOGP(DIUUP, LOGL_DEBUG, "(%s) IuUP inserting IuUP header in RTP data (frame nr %u)\n",
210 cn->name, iuup_hdr->frame_nr);
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200211
Neels Hofmeyr85f94012018-10-08 03:36:45 +0200212 return cn->cfg.tx_msg(pdu, cn->cfg.node_priv);
Neels Hofmeyr3243c7c2018-09-30 05:01:20 +0200213}