269 lines
6.9 KiB
C
269 lines
6.9 KiB
C
/* (C) 2015 by Daniel Willmann <dwillmann@sysmocom.de>
|
|
* (C) 2021 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
|
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
|
* All Rights Reserved
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/lienses/>.
|
|
*
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
|
|
#include <osmocom/core/msgb.h>
|
|
#include <osmocom/gsm/tlv.h>
|
|
#include <osmocom/gsm/protocol/gsm_04_08.h>
|
|
#include <osmocom/gsm/gsm48.h>
|
|
#include <osmocom/crypt/auth.h>
|
|
|
|
#include <osmocom/ranap/ranap_msg_factory.h>
|
|
|
|
#include <osmocom/hnodeb/rua.h>
|
|
#include <osmocom/hnodeb/ranap.h>
|
|
#include <osmocom/hnodeb/nas.h>
|
|
#include <osmocom/hnodeb/hnodeb.h>
|
|
|
|
static struct msgb *gen_nas_id_resp()
|
|
{
|
|
uint8_t id_resp[] = {
|
|
GSM48_PDISC_MM,
|
|
GSM48_MT_MM_ID_RESP,
|
|
/* IMEISV */
|
|
0x09, /* len */
|
|
0x03, /* first digit (0000) + even (0) + id IMEISV (011) */
|
|
0x31, 0x91, 0x06, 0x00, 0x28, 0x47, 0x11, /* digits */
|
|
0xf2, /* filler (1111) + last digit (0010) */
|
|
};
|
|
|
|
return ranap_new_msg_dt(0, id_resp, sizeof(id_resp));
|
|
}
|
|
|
|
static struct msgb *gen_nas_tmsi_realloc_compl()
|
|
{
|
|
uint8_t id_resp[] = {
|
|
GSM48_PDISC_MM,
|
|
GSM48_MT_MM_TMSI_REALL_COMPL,
|
|
};
|
|
|
|
return ranap_new_msg_dt(0, id_resp, sizeof(id_resp));
|
|
}
|
|
|
|
static struct msgb *gen_nas_auth_resp(uint8_t *sres)
|
|
{
|
|
uint8_t id_resp[] = {
|
|
GSM48_PDISC_MM,
|
|
0x80 | GSM48_MT_MM_AUTH_RESP, /* simulate sequence nr 2 */
|
|
0x61, 0xb5, 0x69, 0xf5 /* hardcoded SRES */
|
|
};
|
|
|
|
memcpy(id_resp + 2, sres, 4);
|
|
|
|
return ranap_new_msg_dt(0, id_resp, sizeof(id_resp));
|
|
}
|
|
|
|
static struct tlv_parsed *parse_mm(struct gsm48_hdr *gh, int len)
|
|
{
|
|
static struct tlv_parsed tp;
|
|
int parse_res;
|
|
|
|
len -= (const char *)&gh->data[0] - (const char *)gh;
|
|
|
|
OSMO_ASSERT(gsm48_hdr_pdisc(gh) == GSM48_PDISC_MM);
|
|
|
|
parse_res = tlv_parse(&tp, &gsm48_mm_att_tlvdef, &gh->data[0], len, 0, 0);
|
|
if (parse_res <= 0) {
|
|
uint8_t msg_type = gsm48_hdr_msg_type(gh);
|
|
LOGP(DNAS, LOGL_ERROR, "Error parsing MM message 0x%hhx: %d\n", msg_type, parse_res);
|
|
return NULL;
|
|
}
|
|
|
|
return &tp;
|
|
}
|
|
|
|
int hnb_nas_rx_lu_accept(struct gsm48_hdr *gh, int len, int *sent_tmsi)
|
|
{
|
|
LOGP(DNAS, LOGL_INFO, " :D Location Update Accept :D\n");
|
|
struct gsm48_loc_area_id *lai;
|
|
|
|
lai = (struct gsm48_loc_area_id *)&gh->data[0];
|
|
|
|
struct osmo_location_area_id laid;
|
|
gsm48_decode_lai2(lai, &laid);
|
|
LOGP(DNAS, LOGL_INFO, "LU: mcc %s mnc %s lac %hd\n",
|
|
osmo_mcc_name(laid.plmn.mcc), osmo_mnc_name(laid.plmn.mnc, laid.plmn.mnc_3_digits),
|
|
laid.lac);
|
|
|
|
struct tlv_parsed tp;
|
|
int parse_res;
|
|
|
|
len -= (const char *)&gh->data[0] - (const char *)gh;
|
|
parse_res = tlv_parse(&tp, &gsm48_mm_att_tlvdef, &gh->data[0], len, 0, 0);
|
|
if (parse_res <= 0) {
|
|
LOGP(DNAS, LOGL_ERROR, "Error parsing Location Update Accept message: %d\n", parse_res);
|
|
return -1;
|
|
}
|
|
|
|
if (TLVP_PRESENT(&tp, GSM48_IE_MOBILE_ID)) {
|
|
uint8_t type = TLVP_VAL(&tp, GSM48_IE_NAME_SHORT)[0] & 0x0f;
|
|
if (type == GSM_MI_TYPE_TMSI)
|
|
*sent_tmsi = 1;
|
|
else *sent_tmsi = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void hnb_nas_rx_mm_info(struct gsm48_hdr *gh, int len)
|
|
{
|
|
LOGP(DNAS, LOGL_INFO, " :) MM Info :)\n");
|
|
struct tlv_parsed *tp = parse_mm(gh, len);
|
|
if (!tp)
|
|
return;
|
|
|
|
if (TLVP_PRESENT(tp, GSM48_IE_NAME_SHORT)) {
|
|
char name[128] = {0};
|
|
gsm_7bit_decode_n(name, 127,
|
|
TLVP_VAL(tp, GSM48_IE_NAME_SHORT)+1,
|
|
(TLVP_LEN(tp, GSM48_IE_NAME_SHORT)-1)*8/7);
|
|
LOGP(DNAS, LOGL_INFO, "Info: Short Network Name: %s\n", name);
|
|
}
|
|
|
|
if (TLVP_PRESENT(tp, GSM48_IE_NAME_LONG)) {
|
|
char name[128] = {0};
|
|
gsm_7bit_decode_n(name, 127,
|
|
TLVP_VAL(tp, GSM48_IE_NAME_LONG)+1,
|
|
(TLVP_LEN(tp, GSM48_IE_NAME_LONG)-1)*8/7);
|
|
LOGP(DNAS, LOGL_INFO, "Info: Long Network Name: %s\n", name);
|
|
}
|
|
}
|
|
|
|
static int hnb_nas_rx_auth_req(struct hnb *hnb, struct gsm48_hdr *gh,
|
|
int len)
|
|
{
|
|
struct gsm48_auth_req *ar;
|
|
|
|
len -= (const char *)&gh->data[0] - (const char *)gh;
|
|
|
|
if (len < sizeof(*ar)) {
|
|
LOGP(DNAS, LOGL_ERROR, "GSM48 Auth Req does not fit.\n");
|
|
return -1;
|
|
}
|
|
|
|
LOGP(DNAS, LOGL_INFO, " :) Authentication Request :)\n");
|
|
|
|
ar = (struct gsm48_auth_req*) &gh->data[0];
|
|
int seq = ar->key_seq;
|
|
|
|
/* Generate SRES from *HARDCODED* Ki for Iuh testing */
|
|
struct osmo_auth_vector vec;
|
|
/* Ki 000102030405060708090a0b0c0d0e0f */
|
|
struct osmo_sub_auth_data auth = {
|
|
.type = OSMO_AUTH_TYPE_GSM,
|
|
.algo = OSMO_AUTH_ALG_COMP128v1,
|
|
.u.gsm.ki = {
|
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
|
|
0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
|
|
0x0e, 0x0f
|
|
},
|
|
};
|
|
|
|
memset(&vec, 0, sizeof(vec));
|
|
osmo_auth_gen_vec(&vec, &auth, ar->rand);
|
|
|
|
LOGP(DNAS, LOGL_DEBUG, "seq %d rand %s",
|
|
seq, osmo_hexdump(ar->rand, sizeof(ar->rand)));
|
|
LOGP(DNAS, LOGL_DEBUG, " --> sres %s\n",
|
|
osmo_hexdump(vec.sres, 4));
|
|
|
|
return hnb_tx_dt(hnb, gen_nas_auth_resp(vec.sres));
|
|
}
|
|
|
|
static int hnb_nas_rx_mm(struct hnb *hnb, struct gsm48_hdr *gh, int len)
|
|
{
|
|
struct hnb_chan *chan;
|
|
|
|
chan = hnb->cs.chan;
|
|
if (!chan) {
|
|
LOGP(DNAS, LOGL_ERROR, "hnb_nas_rx_mm(): No CS channel established yet.\n");
|
|
return -1;
|
|
}
|
|
|
|
OSMO_ASSERT(!chan->is_ps);
|
|
|
|
uint8_t msg_type = gsm48_hdr_msg_type(gh);
|
|
int sent_tmsi;
|
|
|
|
switch (msg_type) {
|
|
case GSM48_MT_MM_ID_REQ:
|
|
return hnb_tx_dt(hnb, gen_nas_id_resp());
|
|
|
|
case GSM48_MT_MM_LOC_UPD_ACCEPT:
|
|
if (hnb_nas_rx_lu_accept(gh, len, &sent_tmsi))
|
|
return -1;
|
|
if (sent_tmsi)
|
|
return hnb_tx_dt(hnb, gen_nas_tmsi_realloc_compl());
|
|
else
|
|
return 0;
|
|
|
|
case GSM48_MT_MM_LOC_UPD_REJECT:
|
|
LOGP(DNAS, LOGL_INFO, "Received Location Update Reject\n");
|
|
return 0;
|
|
|
|
case GSM48_MT_MM_INFO:
|
|
hnb_nas_rx_mm_info(gh, len);
|
|
hnb_tx_iu_release_req(hnb);
|
|
return 0;
|
|
|
|
case GSM48_MT_MM_AUTH_REQ:
|
|
return hnb_nas_rx_auth_req(hnb, gh, len);
|
|
|
|
default:
|
|
LOGP(DNAS, LOGL_INFO, "04.08 message type not handled by hnb-test: 0x%x\n",
|
|
msg_type);
|
|
return 0;
|
|
}
|
|
|
|
}
|
|
|
|
void hnb_nas_rx_dtap(struct hnb *hnb, void *data, int len)
|
|
{
|
|
int rc;
|
|
LOGP(DNAS, LOGL_INFO, "got %d bytes: %s\n", len, osmo_hexdump(data, len));
|
|
|
|
// nas_pdu == '05 08 12' ==> IMEI Identity request
|
|
// '05 04 0d' ==> LU reject
|
|
|
|
struct gsm48_hdr *gh = data;
|
|
if (len < sizeof(*gh)) {
|
|
LOGP(DNAS, LOGL_ERROR, "hnb_nas_rx_dtap(): NAS PDU is too short: %d. Ignoring.\n",
|
|
len);
|
|
return;
|
|
}
|
|
uint8_t pdisc = gsm48_hdr_pdisc(gh);
|
|
|
|
switch (pdisc) {
|
|
case GSM48_PDISC_MM:
|
|
rc = hnb_nas_rx_mm(hnb, gh, len);
|
|
if (rc != 0)
|
|
LOGP(DNAS, LOGL_ERROR, "Error receiving MM message: %d\n", rc);
|
|
return;
|
|
default:
|
|
LOGP(DNAS, LOGL_NOTICE, "04.08 discriminator not handled by hnb-test: %d\n",
|
|
pdisc);
|
|
return;
|
|
}
|
|
}
|