blob: e0eec63402064dbefe752fd71522118d06ed93a8 [file] [log] [blame]
Philipp Maier87bd9be2017-08-22 16:35:41 +02001/* Message connection list handling */
2
3/*
4 * (C) 2017 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
5 * All Rights Reserved
6 *
7 * Author: Philipp Maier
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU Affero General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Affero General Public License for more details.
18 *
19 * You should have received a copy of the GNU Affero General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 *
22 */
23
24#include <osmocom/mgcp/mgcp_conn.h>
25#include <osmocom/mgcp/mgcp_internal.h>
Neels Hofmeyr67793542017-09-08 04:25:16 +020026#include <osmocom/mgcp/mgcp_common.h>
Philipp Maierfde78ad2017-10-13 12:05:37 +020027#include <osmocom/mgcp/mgcp_ep.h>
Philipp Maier87bd9be2017-08-22 16:35:41 +020028
29/* Reset codec state and free memory */
30static void mgcp_rtp_codec_reset(struct mgcp_rtp_codec *codec)
31{
32 codec->payload_type = -1;
33 codec->subtype_name = NULL;
34 codec->audio_name = NULL;
35 codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
36 codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
37 codec->rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
38 codec->channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS;
39
40 /* see also mgcp_sdp.c, mgcp_set_audio_info() */
41 talloc_free(codec->subtype_name);
42 talloc_free(codec->audio_name);
43}
44
45/* Reset states, free memory, set defaults and reset codec state */
46static void mgcp_rtp_conn_reset(struct mgcp_conn_rtp *conn)
47{
48 struct mgcp_rtp_end *end = &conn->end;
49
50 conn->type = MGCP_RTP_DEFAULT;
51 conn->osmux.allocated_cid = -1;
52
53 end->rtp.fd = -1;
54 end->rtcp.fd = -1;
55 end->local_port = 0;
56 end->packets_rx = 0;
57 end->octets_rx = 0;
58 end->packets_tx = 0;
59 end->octets_tx = 0;
60 end->dropped_packets = 0;
61 end->rtp_port = end->rtcp_port = 0;
62 talloc_free(end->fmtp_extra);
63 end->fmtp_extra = NULL;
64
65 /* Set default values */
66 end->frames_per_packet = 0; /* unknown */
67 end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS;
68 end->output_enabled = 0;
69
70 mgcp_rtp_codec_reset(&end->codec);
71 mgcp_rtp_codec_reset(&end->alt_codec);
72}
73
74/*! allocate a new connection list entry.
75 * \param[in] ctx talloc context
76 * \param[in] endp associated endpoint
77 * \param[in] id identification number of the connection
78 * \param[in] type connection type (e.g. MGCP_CONN_TYPE_RTP)
79 * \returns pointer to allocated connection, NULL on error */
80struct mgcp_conn *mgcp_conn_alloc(void *ctx, struct mgcp_endpoint *endp,
81 uint32_t id, enum mgcp_conn_type type,
82 char *name)
83{
84 struct mgcp_conn *conn;
85 OSMO_ASSERT(endp);
86 OSMO_ASSERT(endp->conns.next != NULL && endp->conns.prev != NULL);
87 OSMO_ASSERT(strlen(name) < sizeof(conn->name));
88
89 /* Do not allow more then two connections */
90 if (llist_count(&endp->conns) >= endp->type->max_conns)
91 return NULL;
92
93 /* Prevent duplicate connection IDs */
94 if (mgcp_conn_get(endp, id))
95 return NULL;
96
97 /* Create new connection and add it to the list */
98 conn = talloc_zero(ctx, struct mgcp_conn);
99 if (!conn)
100 return NULL;
101 conn->endp = endp;
102 conn->type = type;
103 conn->mode = MGCP_CONN_NONE;
104 conn->mode_orig = MGCP_CONN_NONE;
105 conn->id = id;
106 conn->u.rtp.conn = conn;
107 strcpy(conn->name, name);
108
109 switch (type) {
110 case MGCP_CONN_TYPE_RTP:
111 mgcp_rtp_conn_reset(&conn->u.rtp);
112 break;
113 default:
114 /* NOTE: This should never be called with an
115 * invalid type, its up to the programmer
116 * to ensure propery types */
117 OSMO_ASSERT(false);
118 }
119
120 llist_add(&conn->entry, &endp->conns);
121
122 return conn;
123}
124
125/*! find a connection by its ID.
126 * \param[in] endp associated endpoint
127 * \param[in] id identification number of the connection
128 * \returns pointer to allocated connection, NULL if not found */
129struct mgcp_conn *mgcp_conn_get(struct mgcp_endpoint *endp, uint32_t id)
130{
131 OSMO_ASSERT(endp);
132 OSMO_ASSERT(endp->conns.next != NULL && endp->conns.prev != NULL);
133
134 struct mgcp_conn *conn;
135
136 llist_for_each_entry(conn, &endp->conns, entry) {
137 if (conn->id == id)
138 return conn;
139 }
140
141 return NULL;
142}
143
144/*! find an RTP connection by its ID.
145 * \param[in] endp associated endpoint
146 * \param[in] id identification number of the connection
147 * \returns pointer to allocated connection, NULL if not found */
148struct mgcp_conn_rtp *mgcp_conn_get_rtp(struct mgcp_endpoint *endp, uint32_t id)
149{
150 OSMO_ASSERT(endp);
151 OSMO_ASSERT(endp->conns.next != NULL && endp->conns.prev != NULL);
152
153 struct mgcp_conn *conn;
154
155 conn = mgcp_conn_get(endp, id);
156 if (!conn)
157 return NULL;
158
159 if (conn->type == MGCP_CONN_TYPE_RTP)
160 return &conn->u.rtp;
161
162 return NULL;
163}
164
165/*! free a connection by its ID.
166 * \param[in] endp associated endpoint
167 * \param[in] id identification number of the connection */
168void mgcp_conn_free(struct mgcp_endpoint *endp, uint32_t id)
169{
170 OSMO_ASSERT(endp);
171 OSMO_ASSERT(endp->conns.next != NULL && endp->conns.prev != NULL);
172
173 struct mgcp_conn *conn;
174
175 conn = mgcp_conn_get(endp, id);
176 if (!conn)
177 return;
178
179 switch (conn->type) {
180 case MGCP_CONN_TYPE_RTP:
181 osmux_disable_conn(&conn->u.rtp);
182 osmux_release_cid(&conn->u.rtp);
183 mgcp_free_rtp_port(&conn->u.rtp.end);
184 break;
185 default:
186 /* NOTE: This should never be called with an
187 * invalid type, its up to the programmer
188 * to ensure propery types */
189 OSMO_ASSERT(false);
190 }
191
192 llist_del(&conn->entry);
193 talloc_free(conn);
194}
195
196/*! free oldest connection in the list.
197 * \param[in] endp associated endpoint */
198void mgcp_conn_free_oldest(struct mgcp_endpoint *endp)
199{
200 OSMO_ASSERT(endp);
201 OSMO_ASSERT(endp->conns.next != NULL && endp->conns.prev != NULL);
202
203 struct mgcp_conn *conn;
204
205 if (llist_empty(&endp->conns))
206 return;
207
208 conn = llist_last_entry(&endp->conns, struct mgcp_conn, entry);
209 if (!conn)
210 return;
211
212 mgcp_conn_free(endp, conn->id);
213}
214
215/*! free all connections at once.
216 * \param[in] endp associated endpoint */
217void mgcp_conn_free_all(struct mgcp_endpoint *endp)
218{
219 OSMO_ASSERT(endp);
220 OSMO_ASSERT(endp->conns.next != NULL && endp->conns.prev != NULL);
221
222 struct mgcp_conn *conn;
223 struct mgcp_conn *conn_tmp;
224
225 /* Drop all items in the list */
226 llist_for_each_entry_safe(conn, conn_tmp, &endp->conns, entry) {
227 mgcp_conn_free(endp, conn->id);
228 }
229
230 return;
231}
232
233/*! dump basic connection information to human readble string.
234 * \param[in] conn to dump
235 * \returns human readble string */
236char *mgcp_conn_dump(struct mgcp_conn *conn)
237{
238 static char str[256];
239
240 if (!conn) {
241 snprintf(str, sizeof(str), "(null connection)");
242 return str;
243 }
244
245 switch (conn->type) {
246 case MGCP_CONN_TYPE_RTP:
247 /* Dump RTP connection */
248 snprintf(str, sizeof(str), "(%s/rtp, id:%u, ip:%s, "
249 "rtp:%u rtcp:%u)",
250 conn->name,
251 conn->id,
252 inet_ntoa(conn->u.rtp.end.addr),
253 ntohs(conn->u.rtp.end.rtp_port),
254 ntohs(conn->u.rtp.end.rtcp_port));
255 break;
256
257 default:
258 /* Should not happen, we should be able to dump
259 * every possible connection type. */
260 snprintf(str, sizeof(str), "(unknown connection type)");
261 break;
262 }
263
264 return str;
265}
266
267/*! find destination connection on a specific endpoint.
268 * \param[in] conn to search a destination for
269 * \returns destination connection, NULL on failure */
270struct mgcp_conn *mgcp_find_dst_conn(struct mgcp_conn *conn)
271{
272 struct mgcp_endpoint *endp;
273 struct mgcp_conn *partner_conn;
274 endp = conn->endp;
275
276 /*! NOTE: This simply works by grabbing the first connection that is
277 * not the supplied connection, which is suitable for endpoints that
278 * do not serve more than two connections. */
279
280 llist_for_each_entry(partner_conn, &endp->conns, entry) {
281 if (conn != partner_conn) {
282 return partner_conn;
283 }
284 }
285
286 return NULL;
287}