blob: bfb009d0c36ef18574b873342973a3f754cb24f0 [file] [log] [blame]
Neels Hofmeyr306dc092018-09-30 05:01:20 +02001/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */
2/* IuUP Core Network side protocol, 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 <errno.h>
26#include <osmocom/mgcp/iuup_protocol.h>
27#include <osmocom/mgcp/debug.h>
28#include <osmocom/netif/rtp.h>
29
30/* Calculating two bytes of CRC is ok to do by a loop */
31static uint8_t header_crc6(const uint8_t *hdr)
32{
33 int bit;
34 /* Polynomial: D^6 + D^5 + D^3 + D^2 + D^1 + 1
35 * that's 1101111 or 0x6f;
36 * align its lowest bit with a uint16_t's highest bit: */
37 uint32_t polynomial = 0x6f << 15; // 00110111 10000000 00000000
38 uint32_t remainder = ( ((uint32_t)hdr[0]) << 8 | hdr[1] ) << 6;
39
40 for (bit = 15; bit >= 0; bit--)
41 {
42 if (remainder & (0x40 << bit))
43 remainder ^= polynomial;
44 polynomial >>= 1;
45 }
46
47 return remainder;
48}
49
50/*
51 * Charles Michael Heard's CRC-10 code, from
52 *
53 * http://web.archive.org/web/20061005231950/http://cell-relay.indiana.edu/cell-relay/publications/software/CRC/crc10.html
54 *
55 * with the CRC table initialized with values computed by
56 * his "gen_byte_crc10_table()" routine, rather than by calling that
57 * routine at run time, and with various data type cleanups.
58 */
59static const uint16_t byte_crc10_table[256] = {
60 0x0000, 0x0233, 0x0255, 0x0066, 0x0299, 0x00aa, 0x00cc, 0x02ff,
61 0x0301, 0x0132, 0x0154, 0x0367, 0x0198, 0x03ab, 0x03cd, 0x01fe,
62 0x0031, 0x0202, 0x0264, 0x0057, 0x02a8, 0x009b, 0x00fd, 0x02ce,
63 0x0330, 0x0103, 0x0165, 0x0356, 0x01a9, 0x039a, 0x03fc, 0x01cf,
64 0x0062, 0x0251, 0x0237, 0x0004, 0x02fb, 0x00c8, 0x00ae, 0x029d,
65 0x0363, 0x0150, 0x0136, 0x0305, 0x01fa, 0x03c9, 0x03af, 0x019c,
66 0x0053, 0x0260, 0x0206, 0x0035, 0x02ca, 0x00f9, 0x009f, 0x02ac,
67 0x0352, 0x0161, 0x0107, 0x0334, 0x01cb, 0x03f8, 0x039e, 0x01ad,
68 0x00c4, 0x02f7, 0x0291, 0x00a2, 0x025d, 0x006e, 0x0008, 0x023b,
69 0x03c5, 0x01f6, 0x0190, 0x03a3, 0x015c, 0x036f, 0x0309, 0x013a,
70 0x00f5, 0x02c6, 0x02a0, 0x0093, 0x026c, 0x005f, 0x0039, 0x020a,
71 0x03f4, 0x01c7, 0x01a1, 0x0392, 0x016d, 0x035e, 0x0338, 0x010b,
72 0x00a6, 0x0295, 0x02f3, 0x00c0, 0x023f, 0x000c, 0x006a, 0x0259,
73 0x03a7, 0x0194, 0x01f2, 0x03c1, 0x013e, 0x030d, 0x036b, 0x0158,
74 0x0097, 0x02a4, 0x02c2, 0x00f1, 0x020e, 0x003d, 0x005b, 0x0268,
75 0x0396, 0x01a5, 0x01c3, 0x03f0, 0x010f, 0x033c, 0x035a, 0x0169,
76 0x0188, 0x03bb, 0x03dd, 0x01ee, 0x0311, 0x0122, 0x0144, 0x0377,
77 0x0289, 0x00ba, 0x00dc, 0x02ef, 0x0010, 0x0223, 0x0245, 0x0076,
78 0x01b9, 0x038a, 0x03ec, 0x01df, 0x0320, 0x0113, 0x0175, 0x0346,
79 0x02b8, 0x008b, 0x00ed, 0x02de, 0x0021, 0x0212, 0x0274, 0x0047,
80 0x01ea, 0x03d9, 0x03bf, 0x018c, 0x0373, 0x0140, 0x0126, 0x0315,
81 0x02eb, 0x00d8, 0x00be, 0x028d, 0x0072, 0x0241, 0x0227, 0x0014,
82 0x01db, 0x03e8, 0x038e, 0x01bd, 0x0342, 0x0171, 0x0117, 0x0324,
83 0x02da, 0x00e9, 0x008f, 0x02bc, 0x0043, 0x0270, 0x0216, 0x0025,
84 0x014c, 0x037f, 0x0319, 0x012a, 0x03d5, 0x01e6, 0x0180, 0x03b3,
85 0x024d, 0x007e, 0x0018, 0x022b, 0x00d4, 0x02e7, 0x0281, 0x00b2,
86 0x017d, 0x034e, 0x0328, 0x011b, 0x03e4, 0x01d7, 0x01b1, 0x0382,
87 0x027c, 0x004f, 0x0029, 0x021a, 0x00e5, 0x02d6, 0x02b0, 0x0083,
88 0x012e, 0x031d, 0x037b, 0x0148, 0x03b7, 0x0184, 0x01e2, 0x03d1,
89 0x022f, 0x001c, 0x007a, 0x0249, 0x00b6, 0x0285, 0x02e3, 0x00d0,
90 0x011f, 0x032c, 0x034a, 0x0179, 0x0386, 0x01b5, 0x01d3, 0x03e0,
91 0x021e, 0x002d, 0x004b, 0x0278, 0x0087, 0x02b4, 0x02d2, 0x00e1
92};
93
94static uint16_t crc10(uint16_t crc10_accum, const uint8_t *payload, unsigned int payload_len)
95{
96 int i;
97
98 for (i = 0; i < payload_len; i++) {
99 crc10_accum = ((crc10_accum << 8) & 0x300)
100 ^ byte_crc10_table[(crc10_accum >> 2) & 0xff]
101 ^ payload[i];
102 }
103 return crc10_accum;
104}
105
106/* When a payload of a multiple of bytes has run through, we need to still feed 10 bits of zeros into the
107 * CRC10 to get the payload's checksum result that we can send to a peer. That can't be done with above
108 * table, because it acts as if full 16 bits are fed. This stops after 10 bits. */
109static uint16_t crc10_remainder(uint16_t crc10_accum)
110{
111 int bit;
112 /* Polynomial: D^10 + D^9 + D^5 + D^4 + D^1 + 1
113 * that's 11000110011 or 0x633;
114 * align its lowest bit with a 10bit value's highest bit: */
115 uint32_t polynomial = 0x633 << 9; // 1100 01100110 00000000
116 uint32_t remainder = ((uint32_t)crc10_accum) << 10;
117
118 /* Run on 10 bits */
119 for (bit = 9; bit >= 0; bit--)
120 {
121 if (remainder & ((1 << 10) << bit))
122 remainder ^= polynomial;
123 polynomial >>= 1;
124 }
125
126 return remainder & 0x3ff;
127}
128
129static uint16_t payload_crc10(const uint8_t *payload, unsigned int payload_len)
130{
131 uint16_t crc10_accum = crc10(0, payload, payload_len);
132 return crc10_remainder(crc10_accum);
133}
134
135/* Given an IuUP PDU data block, write the correct header and payload CRC checksums at the right places.
136 */
137void osmo_iuup_set_checksums(uint8_t *iuup_header_and_payload, unsigned int header_and_payload_len)
138{
139 /* For both data and ctrl, the checksums and payload are at the same offset */
140 struct osmo_iuup_hdr_data *hdr = (void*)iuup_header_and_payload;
141 uint16_t crc;
142 unsigned int payload_len;
143
144 hdr->header_crc = header_crc6(iuup_header_and_payload);
145
146 payload_len = iuup_header_and_payload + header_and_payload_len - hdr->payload;
147 crc = payload_crc10(hdr->payload, payload_len);
148 hdr->payload_crc_hi = (crc >> 8) & 0x3;
149 hdr->payload_crc_lo = crc & 0xff;
150
151}
152
153/* Validate minimum message sizes, IuUP PDU type, header- and payload checksums. If it is a Control
154 * Procedure PDU, return the header position in is_ctrl, if it is a Data PDU, return the header position
155 * in is_data. If log_errors is true, log on DIUUP with the given log label for context. Return NULL in
156 * both is_ctrl and is_data, and return a negative error code if the PDU could not be identified as a
157 * valid RTP PDU containing an IuUP part. */
158int osmo_iuup_classify(bool log_errors,
159 const char *log_label,
160 struct msgb *pdu,
161 struct osmo_iuup_hdr_ctrl **is_ctrl,
162 struct osmo_iuup_hdr_data **is_data)
163{
164 struct rtp_hdr *rtp = (void*)pdu->data;
165 struct osmo_iuup_hdr_ctrl *hdr = (void*)rtp->data;
166 unsigned int payload_len;
167 uint16_t crc_calculated;
168 uint16_t crc_from_peer;
169
170#define ERR(fmt, args...) do { \
171 if (log_errors) \
172 LOGP(DIUUP, LOGL_ERROR, "(%s) " fmt, log_label? : "-", ## args); \
173 return -EINVAL; \
174 } while (0)
175
176 if (is_ctrl)
177 *is_ctrl = NULL;
178 if (is_data)
179 *is_data = NULL;
180
181 /* We need at least a header of 4 bytes. The osmo_iuup_hdr_ctrl already includes a byte of
182 * payload, so use osmo_iuup_hdr_data to check the minimum here. */
183 if (pdu->len < (sizeof(*rtp) + sizeof(struct osmo_iuup_hdr_data)))
184 ERR("IuUP PDU too short: %u\n", pdu->len);
185
186 /* Let's not validate checksums if the header type isn't sane */
187 switch (hdr->pdu_type) {
188 case OSMO_IUUP_PDU_DATA_WITH_CRC:
189 /* If the caller isn't interested in data PDUs, cut short here. */
190 if (!is_data)
191 return 0;
192 break;
193 case OSMO_IUUP_PDU_CONTROL_PROCEDURE:
194 /* If the caller isn't interested in control PDUs, cut short here. */
195 if (!is_ctrl)
196 return 0;
197 if (pdu->len < (sizeof(*rtp) + sizeof(struct osmo_iuup_hdr_ctrl)))
198 ERR("IuUP control PDU too short: %u\n", pdu->len);
199 break;
200 default:
201 ERR("IuUP with invalid type: %u\n", hdr->pdu_type);
202 }
203
204 /* For both data and ctrl, the checksums and payload are at the same offset */
205
206 crc_calculated = header_crc6((uint8_t*)hdr);
207 if (crc_calculated != hdr->header_crc)
208 ERR("IuUP PDU with invalid header CRC (peer sent 0x%x, calculated 0x%x)\n",
209 hdr->header_crc, crc_calculated);
210
211 payload_len = pdu->tail - hdr->payload;
212 crc_calculated = payload_crc10(hdr->payload, payload_len);
213 crc_from_peer = (((uint16_t)hdr->payload_crc_hi) << 8) | hdr->payload_crc_lo;
214 if (crc_from_peer != crc_calculated)
215 ERR("IuUP PDU with invalid payload CRC (peer sent 0x%x, calculated 0x%x)\n",
216 crc_from_peer, crc_calculated);
217
218 switch (hdr->pdu_type) {
219 case OSMO_IUUP_PDU_DATA_WITH_CRC:
220 if (is_data)
221 *is_data = (void*)hdr;
222 return 0;
223 case OSMO_IUUP_PDU_CONTROL_PROCEDURE:
224 if (is_ctrl)
225 *is_ctrl = hdr;
226 return 0;
227 default:
228 ERR("IuUP with invalid type: %u\n", hdr->pdu_type);
229 }
230#undef ERR
231}
232
233/* Return true if this RTP packet contains an IuUP Initialization header (detect IuUP peer). */
234bool osmo_iuup_is_init(struct msgb *pdu)
235{
236 struct osmo_iuup_hdr_ctrl *is_ctrl;
237 osmo_iuup_classify(false, NULL, pdu, &is_ctrl, NULL);
238 return is_ctrl
239 && is_ctrl->procedure == OSMO_IUUP_PROC_INITIALIZATION
240 && is_ctrl->ack_nack == OSMO_IUUP_ACKNACK_PROCEDURE;
241}
242
243/* Append an IuUP Initialization ACK message */
244void osmo_iuup_make_init_ack(struct msgb *ack)
245{
246 /* Send Initialization Ack PDU back to the sender */
247 struct osmo_iuup_hdr_ctrl *hdr;
248 OSMO_ASSERT(ack);
249
250 hdr = (void*)msgb_put(ack, sizeof(*hdr));
251
252 *hdr = (struct osmo_iuup_hdr_ctrl){
253 .pdu_type = OSMO_IUUP_PDU_CONTROL_PROCEDURE,
254 .ack_nack = OSMO_IUUP_ACKNACK_ACK,
255 .procedure = OSMO_IUUP_PROC_INITIALIZATION,
256 };
257
258 osmo_iuup_set_checksums((uint8_t*)hdr, sizeof(*hdr));
259}
260
261const struct value_string osmo_iuup_error_cause_names[] = {
262 { 0, "CRC error of frame header" },
263 { 1, "CRC error of frame payload" },
264 { 2, "Unexpected frame number" },
265 { 3, "Frame loss" },
266 { 4, "PDU type unknown" },
267 { 5, "Unknown procedure" },
268 { 6, "Unknown reserved value" },
269 { 7, "Unknown field" },
270 { 8, "Frame too short" },
271 { 9, "Missing fields" },
272 { 16, "Unexpected PDU type" },
273 { 17, "spare" },
274 { 18, "Unexpected procedure" },
275 { 19, "Unexpected RFCI" },
276 { 20, "Unexpected value" },
277 { 42, "Initialisation failure" },
278 { 43, "Initialisation failure (network error, timer expiry)" },
279 { 44, "Initialisation failure (Iu UP function error, repeated NACK)" },
280 { 45, "Rate control failure" },
281 { 46, "Error event failure" },
282 { 47, "Time Alignment not supported" },
283 { 48, "Requested Time Alignment not possible" },
284 { 49, "Iu UP Mode version not supported" },
285 {}
286};