osmo-hnodeb/src/osmo-hnodeb/nas.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;
}
}