blob: 2985cc51bb199ee463af6881b9c25819eabdfbde [file] [log] [blame]
Holger Hans Peter Freyther3485feb2010-11-09 23:28:33 +01001/* GSM 08.08 BSSMAP handling */
2/* (C) 2009-2010 by Holger Hans Peter Freyther <zecke@selfish.org>
3 * (C) 2009-2010 by On-Waves
4 * All Rights Reserved
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 */
21
22#include <openbsc/osmo_bsc.h>
Holger Hans Peter Freyther890dfc52010-11-10 09:24:37 +010023#include <openbsc/osmo_bsc_grace.h>
Holger Hans Peter Freyther50c579b2010-11-10 10:07:30 +010024#include <openbsc/osmo_msc_data.h>
Holger Hans Peter Freyther3485feb2010-11-09 23:28:33 +010025#include <openbsc/debug.h>
Holger Hans Peter Freyther9c838ae2010-11-15 09:16:09 +010026#include <openbsc/gsm_subscriber.h>
Holger Hans Peter Freyther50c579b2010-11-10 10:07:30 +010027#include <openbsc/mgcp.h>
Holger Hans Peter Freyther9c838ae2010-11-15 09:16:09 +010028#include <openbsc/paging.h>
Holger Hans Peter Freyther3485feb2010-11-09 23:28:33 +010029
Holger Hans Peter Freyther890dfc52010-11-10 09:24:37 +010030#include <osmocore/gsm0808.h>
Holger Hans Peter Freyther3485feb2010-11-09 23:28:33 +010031#include <osmocore/protocol/gsm_08_08.h>
32
Holger Hans Peter Freyther890dfc52010-11-10 09:24:37 +010033#include <arpa/inet.h>
34
35static uint16_t read_data16(const uint8_t *data)
36{
37 uint16_t res;
38
39 memcpy(&res, data, sizeof(res));
40 return res;
41}
42
Holger Hans Peter Freyther50c579b2010-11-10 10:07:30 +010043/*
44 * helpers for the assignment command
45 */
46enum gsm0808_permitted_speech audio_support_to_gsm88(struct gsm_audio_support *audio)
47{
48 if (audio->hr) {
49 switch (audio->ver) {
50 case 1:
51 return GSM0808_PERM_HR1;
52 break;
53 case 2:
54 return GSM0808_PERM_HR2;
55 break;
56 case 3:
57 return GSM0808_PERM_HR3;
58 break;
59 default:
60 LOGP(DMSC, LOGL_ERROR, "Wrong speech mode: %d\n", audio->ver);
61 return GSM0808_PERM_FR1;
62 }
63 } else {
64 switch (audio->ver) {
65 case 1:
66 return GSM0808_PERM_FR1;
67 break;
68 case 2:
69 return GSM0808_PERM_FR2;
70 break;
71 case 3:
72 return GSM0808_PERM_FR3;
73 break;
74 default:
75 LOGP(DMSC, LOGL_ERROR, "Wrong speech mode: %d\n", audio->ver);
76 return GSM0808_PERM_HR1;
77 }
78 }
79}
80
81enum gsm48_chan_mode gsm88_to_chan_mode(enum gsm0808_permitted_speech speech)
82{
83 switch (speech) {
84 case GSM0808_PERM_HR1:
85 case GSM0808_PERM_FR1:
86 return GSM48_CMODE_SPEECH_V1;
87 break;
88 case GSM0808_PERM_HR2:
89 case GSM0808_PERM_FR2:
90 return GSM48_CMODE_SPEECH_EFR;
91 break;
92 case GSM0808_PERM_HR3:
93 case GSM0808_PERM_FR3:
94 return GSM48_CMODE_SPEECH_AMR;
95 break;
96 }
97
98 LOGP(DMSC, LOGL_FATAL, "Should not be reached.\n");
99 return GSM48_CMODE_SPEECH_AMR;
100}
101
Holger Hans Peter Freyther890dfc52010-11-10 09:24:37 +0100102static int bssmap_handle_reset_ack(struct gsm_network *net,
103 struct msgb *msg, unsigned int length)
104{
105 LOGP(DMSC, LOGL_NOTICE, "Reset ACK from MSC\n");
106 return 0;
107}
108
109/* GSM 08.08 ยง 3.2.1.19 */
110static int bssmap_handle_paging(struct gsm_network *net,
111 struct msgb *msg, unsigned int payload_length)
112{
Holger Hans Peter Freyther9c838ae2010-11-15 09:16:09 +0100113 struct gsm_subscriber *subscr;
Holger Hans Peter Freyther890dfc52010-11-10 09:24:37 +0100114 struct tlv_parsed tp;
115 char mi_string[GSM48_MI_SIZE];
116 uint32_t tmsi = GSM_RESERVED_TMSI;
117 unsigned int lac = GSM_LAC_RESERVED_ALL_BTS;
118 uint8_t data_length;
119 const uint8_t *data;
120 uint8_t chan_needed = RSL_CHANNEED_ANY;
121
122 tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, payload_length - 1, 0, 0);
123
124 if (!TLVP_PRESENT(&tp, GSM0808_IE_IMSI)) {
125 LOGP(DMSC, LOGL_ERROR, "Mandantory IMSI not present.\n");
126 return -1;
127 } else if ((TLVP_VAL(&tp, GSM0808_IE_IMSI)[0] & GSM_MI_TYPE_MASK) != GSM_MI_TYPE_IMSI) {
128 LOGP(DMSC, LOGL_ERROR, "Wrong content in the IMSI\n");
129 return -1;
130 }
131
132 if (!TLVP_PRESENT(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST)) {
133 LOGP(DMSC, LOGL_ERROR, "Mandantory CELL IDENTIFIER LIST not present.\n");
134 return -1;
135 }
136
137 if (TLVP_PRESENT(&tp, GSM0808_IE_TMSI)) {
138 gsm48_mi_to_string(mi_string, sizeof(mi_string),
139 TLVP_VAL(&tp, GSM0808_IE_TMSI), TLVP_LEN(&tp, GSM0808_IE_TMSI));
140 tmsi = strtoul(mi_string, NULL, 10);
141 }
142
143
144 /*
145 * parse the IMSI
146 */
147 gsm48_mi_to_string(mi_string, sizeof(mi_string),
148 TLVP_VAL(&tp, GSM0808_IE_IMSI), TLVP_LEN(&tp, GSM0808_IE_IMSI));
149
150 /*
151 * parse the cell identifier list
152 */
153 data_length = TLVP_LEN(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST);
154 data = TLVP_VAL(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST);
155
156 /*
157 * Support paging to all network or one BTS at one LAC
158 */
159 if (data_length == 3 && data[0] == CELL_IDENT_LAC) {
160 lac = ntohs(read_data16(&data[1]));
161 } else if (data_length > 1 || (data[0] & 0x0f) != CELL_IDENT_BSS) {
162 LOGP(DMSC, LOGL_ERROR, "Unsupported Cell Identifier List: %s\n", hexdump(data, data_length));
163 return -1;
164 }
165
166 if (TLVP_PRESENT(&tp, GSM0808_IE_CHANNEL_NEEDED) && TLVP_LEN(&tp, GSM0808_IE_CHANNEL_NEEDED) == 1)
167 chan_needed = TLVP_VAL(&tp, GSM0808_IE_CHANNEL_NEEDED)[0] & 0x03;
168
169 if (TLVP_PRESENT(&tp, GSM0808_IE_EMLPP_PRIORITY)) {
170 LOGP(DMSC, LOGL_ERROR, "eMLPP is not handled\n");
171 }
172
Holger Hans Peter Freyther9c838ae2010-11-15 09:16:09 +0100173 subscr = subscr_get_or_create(net, mi_string);
174 if (!subscr) {
175 LOGP(DMSC, LOGL_ERROR, "Failed to allocate a subscriber for %s\n", mi_string);
176 return -1;
177 }
178
179 subscr->lac = lac;
180 subscr->tmsi = tmsi;
181
Holger Hans Peter Freyther890dfc52010-11-10 09:24:37 +0100182 LOGP(DMSC, LOGL_DEBUG, "Paging request from MSC IMSI: '%s' TMSI: '0x%x/%u' LAC: 0x%x\n", mi_string, tmsi, tmsi, lac);
Holger Hans Peter Freyther9c838ae2010-11-15 09:16:09 +0100183 paging_request(net, subscr, chan_needed, NULL, NULL);
184 return 0;
Holger Hans Peter Freyther890dfc52010-11-10 09:24:37 +0100185}
186
Holger Hans Peter Freytherf1f57a82010-11-10 09:34:47 +0100187/*
188 * GSM 08.08 ยง 3.1.9.1 and 3.2.1.21...
189 * release our gsm_subscriber_connection and send message
190 */
191static int bssmap_handle_clear_command(struct osmo_bsc_sccp_con *conn,
192 struct msgb *msg, unsigned int payload_length)
193{
194 struct msgb *resp;
195
196 /* TODO: handle the cause of this package */
197
198 if (conn->conn) {
199 LOGP(DMSC, LOGL_DEBUG, "Releasing all transactions on %p\n", conn);
200 gsm0808_clear(conn->conn);
201 subscr_con_free(conn->conn);
202 conn->conn = NULL;
203 }
204
205 /* send the clear complete message */
206 resp = gsm0808_create_clear_complete();
207 if (!resp) {
208 LOGP(DMSC, LOGL_ERROR, "Sending clear complete failed.\n");
209 return -1;
210 }
211
212 bsc_queue_for_msc(conn, resp);
213 return 0;
214}
215
Holger Hans Peter Freytherfae3c652010-11-10 09:44:34 +0100216/*
217 * GSM 08.08 ยง 3.4.7 cipher mode handling. We will have to pick
218 * the cipher to be used for this. In case we are already using
219 * a cipher we will have to send cipher mode reject to the MSC,
220 * otherwise we will have to pick something that we and the MS
221 * is supporting. Currently we are doing it in a rather static
222 * way by picking one ecnryption or no encrytpion.
223 */
224static int bssmap_handle_cipher_mode(struct osmo_bsc_sccp_con *conn,
225 struct msgb *msg, unsigned int payload_length)
226{
227 uint16_t len;
228 struct gsm_network *network = NULL;
229 const uint8_t *data;
230 struct tlv_parsed tp;
231 struct msgb *resp;
232 int reject_cause = -1;
233 int include_imeisv = 1;
234
235 if (!conn->conn) {
236 LOGP(DMSC, LOGL_ERROR, "No lchan/msc_data in cipher mode command.\n");
237 goto reject;
238 }
239
240 if (conn->ciphering_handled) {
241 LOGP(DMSC, LOGL_ERROR, "Already seen ciphering command. Protocol Error.\n");
242 goto reject;
243 }
244
245 conn->ciphering_handled = 1;
246
247 tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, payload_length - 1, 0, 0);
248 if (!TLVP_PRESENT(&tp, GSM0808_IE_ENCRYPTION_INFORMATION)) {
249 LOGP(DMSC, LOGL_ERROR, "IE Encryption Information missing.\n");
250 goto reject;
251 }
252
253 /*
254 * check if our global setting is allowed
255 * - Currently we check for A5/0 and A5/1
256 * - Copy the key if that is necessary
257 * - Otherwise reject
258 */
259 len = TLVP_LEN(&tp, GSM0808_IE_ENCRYPTION_INFORMATION);
260 if (len < 1) {
261 LOGP(DMSC, LOGL_ERROR, "IE Encryption Information is too short.\n");
262 goto reject;
263 }
264
265 network = conn->conn->bts->network;
266 data = TLVP_VAL(&tp, GSM0808_IE_ENCRYPTION_INFORMATION);
267
268 if (TLVP_PRESENT(&tp, GSM0808_IE_CIPHER_RESPONSE_MODE))
269 include_imeisv = TLVP_VAL(&tp, GSM0808_IE_CIPHER_RESPONSE_MODE)[0] & 0x1;
270
271 if (network->a5_encryption == 0 && (data[0] & 0x1) == 0x1) {
272 gsm0808_cipher_mode(conn->conn, 0, NULL, 0, include_imeisv);
273 } else if (network->a5_encryption != 0 && (data[0] & 0x2) == 0x2) {
274 gsm0808_cipher_mode(conn->conn, 1, &data[1], len - 1, include_imeisv);
275 } else {
276 LOGP(DMSC, LOGL_ERROR, "Can not select encryption...\n");
277 goto reject;
278 }
279
280reject:
281 resp = gsm0808_create_cipher_reject(reject_cause);
282 if (!resp) {
283 LOGP(DMSC, LOGL_ERROR, "Sending the cipher reject failed.\n");
284 return -1;
285 }
286
287 bsc_queue_for_msc(conn, resp);
288 return -1;
289}
290
Holger Hans Peter Freyther50c579b2010-11-10 10:07:30 +0100291/*
292 * Handle the assignment request message.
293 *
294 * See ยง3.2.1.1 for the message type
295 */
296static int bssmap_handle_assignm_req(struct osmo_bsc_sccp_con *conn,
297 struct msgb *msg, unsigned int length)
298{
299 struct msgb *resp;
300 struct gsm_network *network;
301 struct tlv_parsed tp;
302 uint8_t *data;
303 uint16_t cic;
304 uint8_t timeslot;
305 uint8_t multiplex;
306 enum gsm48_chan_mode chan_mode = GSM48_CMODE_SIGN;
307 int i, supported, port, full_rate = -1;
308
309 if (!conn->conn) {
310 LOGP(DMSC, LOGL_ERROR, "No lchan/msc_data in cipher mode command.\n");
311 return -1;
312 }
313
314 network = conn->conn->bts->network;
315 tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, length - 1, 0, 0);
316
317 if (!TLVP_PRESENT(&tp, GSM0808_IE_CHANNEL_TYPE)) {
318 LOGP(DMSC, LOGL_ERROR, "Mandantory channel type not present.\n");
319 goto reject;
320 }
321
322 if (!TLVP_PRESENT(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)) {
323 LOGP(DMSC, LOGL_ERROR, "Identity code missing. Audio routing will not work.\n");
324 goto reject;
325 }
326
327 cic = ntohs(read_data16(TLVP_VAL(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)));
328 timeslot = cic & 0x1f;
329 multiplex = (cic & ~0x1f) >> 5;
330
331 /*
332 * Currently we only support a limited subset of all
333 * possible channel types. The limitation ends by not using
334 * multi-slot, limiting the channel coding, speech...
335 */
336 if (TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE) < 3) {
337 LOGP(DMSC, LOGL_ERROR, "ChannelType len !=3 not supported: %d\n",
338 TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE));
339 goto reject;
340 }
341
342 /*
343 * Try to figure out if we support the proposed speech codecs. For
344 * now we will always pick the full rate codecs.
345 */
346
347 data = (uint8_t *) TLVP_VAL(&tp, GSM0808_IE_CHANNEL_TYPE);
348 if ((data[0] & 0xf) != 0x1) {
349 LOGP(DMSC, LOGL_ERROR, "ChannelType != speech: %d\n", data[0]);
350 goto reject;
351 }
352
353 if (data[1] != GSM0808_SPEECH_FULL_PREF && data[1] != GSM0808_SPEECH_HALF_PREF) {
354 LOGP(DMSC, LOGL_ERROR, "ChannelType full not allowed: %d\n", data[1]);
355 goto reject;
356 }
357
358 /*
359 * go through the list of preferred codecs of our gsm network
360 * and try to find it among the permitted codecs. If we found
361 * it we will send chan_mode to the right mode and break the
362 * inner loop. The outer loop will exit due chan_mode having
363 * the correct value.
364 */
365 full_rate = 0;
366 for (supported = 0;
367 chan_mode == GSM48_CMODE_SIGN && supported < network->msc_data->audio_length;
368 ++supported) {
369
370 int perm_val = audio_support_to_gsm88(network->msc_data->audio_support[supported]);
371 for (i = 2; i < TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE); ++i) {
372 if ((data[i] & 0x7f) == perm_val) {
373 chan_mode = gsm88_to_chan_mode(perm_val);
374 full_rate = (data[i] & 0x4) == 0;
375 break;
376 } else if ((data[i] & 0x80) == 0x00) {
377 break;
378 }
379 }
380 }
381
382 if (chan_mode == GSM48_CMODE_SIGN) {
383 LOGP(DMSC, LOGL_ERROR, "No supported audio type found.\n");
384 goto reject;
385 }
386
387 /* map it to a MGCP Endpoint and a RTP port */
388 port = mgcp_timeslot_to_endpoint(multiplex, timeslot);
389 conn->rtp_port = rtp_calculate_port(port,
390 network->msc_data->rtp_base);
391
392 return gsm0808_assign_req(conn->conn, chan_mode, full_rate);
393
394reject:
395 resp = gsm0808_create_assignment_failure(GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, NULL);
396 if (!resp) {
397 LOGP(DMSC, LOGL_ERROR, "Channel allocation failure.\n");
398 return -1;
399 }
400
401 bsc_queue_for_msc(conn, resp);
402 return -1;
403}
404
Holger Hans Peter Freyther890dfc52010-11-10 09:24:37 +0100405static int bssmap_rcvmsg_udt(struct gsm_network *net,
406 struct msgb *msg, unsigned int length)
407{
408 int ret = 0;
409
410 if (length < 1) {
411 LOGP(DMSC, LOGL_ERROR, "Not enough room: %d\n", length);
412 return -1;
413 }
414
415 switch (msg->l4h[0]) {
416 case BSS_MAP_MSG_RESET_ACKNOWLEDGE:
417 ret = bssmap_handle_reset_ack(net, msg, length);
418 break;
419 case BSS_MAP_MSG_PAGING:
420 if (bsc_grace_allow_new_connection(net))
421 ret = bssmap_handle_paging(net, msg, length);
422 break;
423 }
424
425 return ret;
426}
427
428static int bssmap_rcvmsg_dt1(struct osmo_bsc_sccp_con *conn,
429 struct msgb *msg, unsigned int length)
430{
Holger Hans Peter Freytherf1f57a82010-11-10 09:34:47 +0100431 int ret = 0;
432
433 if (length < 1) {
434 LOGP(DMSC, LOGL_ERROR, "Not enough room: %d\n", length);
435 return -1;
436 }
437
438 switch (msg->l4h[0]) {
439 case BSS_MAP_MSG_CLEAR_CMD:
440 ret = bssmap_handle_clear_command(conn, msg, length);
441 break;
Holger Hans Peter Freytherfae3c652010-11-10 09:44:34 +0100442 case BSS_MAP_MSG_CIPHER_MODE_CMD:
443 ret = bssmap_handle_cipher_mode(conn, msg, length);
444 break;
Holger Hans Peter Freyther50c579b2010-11-10 10:07:30 +0100445 case BSS_MAP_MSG_ASSIGMENT_RQST:
446 ret = bssmap_handle_assignm_req(conn, msg, length);
447 break;
Holger Hans Peter Freytherf1f57a82010-11-10 09:34:47 +0100448 default:
449 LOGP(DMSC, LOGL_DEBUG, "Unimplemented msg type: %d\n", msg->l4h[0]);
450 break;
451 }
452
453 return ret;
Holger Hans Peter Freyther890dfc52010-11-10 09:24:37 +0100454}
455
456static int dtap_rcvmsg(struct osmo_bsc_sccp_con *conn,
457 struct msgb *msg, unsigned int length)
458{
Holger Hans Peter Freytherdbc698a2010-11-10 10:17:05 +0100459 struct dtap_header *header;
460 struct msgb *gsm48;
461 uint8_t *data;
462
463 if (!conn->conn) {
464 LOGP(DMSC, LOGL_ERROR, "No subscriber connection available\n");
465 return -1;
466 }
467
468 header = (struct dtap_header *) msg->l3h;
469 if (sizeof(*header) >= length) {
470 LOGP(DMSC, LOGL_ERROR, "The DTAP header does not fit. Wanted: %u got: %u\n", sizeof(*header), length);
471 LOGP(DMSC, LOGL_ERROR, "hex: %s\n", hexdump(msg->l3h, length));
472 return -1;
473 }
474
475 if (header->length > length - sizeof(*header)) {
476 LOGP(DMSC, LOGL_ERROR, "The DTAP l4 information does not fit: header: %u length: %u\n", header->length, length);
477 LOGP(DMSC, LOGL_ERROR, "hex: %s\n", hexdump(msg->l3h, length));
478 return -1;
479 }
480
481 LOGP(DMSC, LOGL_DEBUG, "DTAP message: SAPI: %u CHAN: %u\n", header->link_id & 0x07, header->link_id & 0xC0);
482
483 /* forward the data */
484 gsm48 = gsm48_msgb_alloc();
485 if (!gsm48) {
486 LOGP(DMSC, LOGL_ERROR, "Allocation of the message failed.\n");
487 return -1;
488 }
489
490 gsm48->l3h = gsm48->data;
491 data = msgb_put(gsm48, length - sizeof(*header));
492 memcpy(data, msg->l3h + sizeof(*header), length - sizeof(*header));
493
494 /* pass it to the filter for extra actions */
495 bsc_scan_msc_msg(conn->conn, gsm48);
496 return gsm0808_submit_dtap(conn->conn, gsm48, header->link_id, 1);
Holger Hans Peter Freyther890dfc52010-11-10 09:24:37 +0100497}
498
Holger Hans Peter Freyther3485feb2010-11-09 23:28:33 +0100499int bsc_handle_udt(struct gsm_network *network,
500 struct bsc_msc_connection *conn,
501 struct msgb *msgb, unsigned int length)
502{
503 struct bssmap_header *bs;
504
505 LOGP(DMSC, LOGL_DEBUG, "Incoming SCCP message ftom MSC: %s\n",
506 hexdump(msgb->l3h, length));
507
508 if (length < sizeof(*bs)) {
509 LOGP(DMSC, LOGL_ERROR, "The header is too short.\n");
510 return -1;
511 }
512
513 bs = (struct bssmap_header *) msgb->l3h;
514 if (bs->length < length - sizeof(*bs))
515 return -1;
516
517 switch (bs->type) {
518 case BSSAP_MSG_BSS_MANAGEMENT:
Holger Hans Peter Freyther890dfc52010-11-10 09:24:37 +0100519 msgb->l4h = &msgb->l3h[sizeof(*bs)];
520 bssmap_rcvmsg_udt(network, msgb, length - sizeof(*bs));
Holger Hans Peter Freyther3485feb2010-11-09 23:28:33 +0100521 break;
522 default:
523 LOGP(DMSC, LOGL_ERROR, "Unimplemented msg type: %d\n", bs->type);
524 }
525
526 return 0;
527}
528
529int bsc_handle_dt1(struct osmo_bsc_sccp_con *conn,
530 struct msgb *msg, unsigned int len)
531{
Holger Hans Peter Freyther890dfc52010-11-10 09:24:37 +0100532 if (len < sizeof(struct bssmap_header)) {
533 LOGP(DMSC, LOGL_ERROR, "The header is too short.\n");
534 }
535
536 switch (msg->l3h[0]) {
537 case BSSAP_MSG_BSS_MANAGEMENT:
538 msg->l4h = &msg->l3h[sizeof(struct bssmap_header)];
539 bssmap_rcvmsg_dt1(conn, msg, len - sizeof(struct bssmap_header));
540 break;
541 case BSSAP_MSG_DTAP:
542 dtap_rcvmsg(conn, msg, len);
543 break;
544 default:
545 LOGP(DMSC, LOGL_DEBUG, "Unimplemented msg type: %d\n", msg->l3h[0]);
546 }
547
Holger Hans Peter Freyther3485feb2010-11-09 23:28:33 +0100548 return -1;
549}