mirror of https://gerrit.osmocom.org/libosmocore
import various generic IPA related functions from libosmo-abis
libosmo-abis is about forming A-bis interfaces/lines by means of E1 or the IPA multiplex (or possibly other link layers). The IPA multiplex is used in other contexts, such as the Control interface, or the A interface. In that context, it makes sense to have generic IPA related functions in libosmocore.laforge/c-ares
parent
fe3e42bdcb
commit
28aa991c2f
|
@ -58,6 +58,7 @@ nobase_include_HEADERS = \
|
|||
osmocom/gsm/gsm48.h \
|
||||
osmocom/gsm/gsm48_ie.h \
|
||||
osmocom/gsm/gsm_utils.h \
|
||||
osmocom/gsm/ipaccess.h \
|
||||
osmocom/gsm/lapd_core.h \
|
||||
osmocom/gsm/lapdm.h \
|
||||
osmocom/gsm/meas_rep.h \
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/gsm/tlv.h>
|
||||
|
||||
/* internal (host-only) data structure */
|
||||
struct ipaccess_unit {
|
||||
uint16_t site_id;
|
||||
uint16_t bts_id;
|
||||
uint16_t trx_id;
|
||||
char *unit_name;
|
||||
char *equipvers;
|
||||
char *swversion;
|
||||
uint8_t mac_addr[6];
|
||||
char *location1;
|
||||
char *location2;
|
||||
char *serno;
|
||||
};
|
||||
|
||||
/* obtain the human-readable name of an IPA CCM ID TAG */
|
||||
const char *ipaccess_idtag_name(uint8_t tag);
|
||||
|
||||
/* parse a buffer of ID tags into a osmocom TLV style representation */
|
||||
int ipaccess_idtag_parse(struct tlv_parsed *dec, unsigned char *buf, int len);
|
||||
|
||||
/* parse an Unit ID in string format into the 'ipaccess_unit' data structure */
|
||||
int ipaccess_parse_unitid(const char *str, struct ipaccess_unit *unit_data);
|
||||
|
||||
/* fill a 'struct ipaccess_unit' based on a parsed IDTAG TLV */
|
||||
int ipaccess_tlv_to_unitdata(struct ipaccess_unit *ud,
|
||||
const struct tlv_parsed *tp);
|
||||
|
||||
/* Send an IPA message to the given FD */
|
||||
int ipaccess_send(int fd, const void *msg, size_t msglen);
|
||||
|
||||
/* Send an IPA CCM PONG via the given FD */
|
||||
int ipaccess_send_pong(int fd);
|
||||
|
||||
/* Send an IPA CCM ID_ACK via the given FD */
|
||||
int ipaccess_send_id_ack(int fd);
|
||||
|
||||
/* Send an IPA CCM ID_REQ via the given FD */
|
||||
int ipaccess_send_id_req(int fd);
|
||||
|
||||
/* Common handling of IPA CCM, BSC side */
|
||||
int ipaccess_rcvmsg_base(struct msgb *msg, struct osmo_fd *bfd);
|
||||
|
||||
/* Common handling of IPA CCM, BTS side */
|
||||
int ipaccess_rcvmsg_bts_base(struct msgb *msg, struct osmo_fd *bfd);
|
||||
|
||||
/* prepend (push) an ipaccess_head_ext to the msgb */
|
||||
void ipaccess_prepend_header_ext(struct msgb *msg, int proto);
|
||||
|
||||
/* prepend (push) an ipaccess_head to the msgb */
|
||||
void ipaccess_prepend_header(struct msgb *msg, int proto);
|
||||
|
||||
struct msgb *ipa_msg_alloc(int headroom);
|
||||
|
||||
int ipa_msg_recv(int fd, struct msgb **rmsg);
|
||||
int ipa_msg_recv_buffered(int fd, struct msgb **rmsg, struct msgb **tmp_msg);
|
|
@ -19,7 +19,7 @@ libosmogsm_la_SOURCES = a5.c rxlev_stat.c tlv_parser.c comp128.c comp128v23.c \
|
|||
auth_core.c auth_comp128v1.c auth_comp128v23.c \
|
||||
auth_milenage.c milenage/aes-encblock.c \
|
||||
milenage/aes-internal.c milenage/aes-internal-enc.c \
|
||||
milenage/milenage.c gan.c
|
||||
milenage/milenage.c gan.c ipaccess.c
|
||||
|
||||
libosmogsm_la_LDFLAGS = $(LTLDFLAGS_OSMOGSM) -version-info $(LIBVERSION) -no-undefined
|
||||
libosmogsm_la_LIBADD = $(top_builddir)/src/libosmocore.la
|
||||
|
|
|
@ -0,0 +1,447 @@
|
|||
/* OpenBSC Abis input driver for ip.access */
|
||||
|
||||
/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2010 by Holger Hans Peter Freyther
|
||||
* (C) 2010 by On-Waves
|
||||
*
|
||||
* 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/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include <osmocom/core/macaddr.h>
|
||||
#include <osmocom/core/select.h>
|
||||
|
||||
#include <osmocom/gsm/tlv.h>
|
||||
#include <osmocom/gsm/protocol/ipaccess.h>
|
||||
#include <osmocom/gsm/ipaccess.h>
|
||||
|
||||
#define IPA_ALLOC_SIZE 1200
|
||||
|
||||
/*
|
||||
* Common propietary IPA messages:
|
||||
* - PONG: in reply to PING.
|
||||
* - ID_REQUEST: first messages once OML has been established.
|
||||
* - ID_ACK: in reply to ID_ACK.
|
||||
*/
|
||||
static const uint8_t ipa_pong_msg[] = {
|
||||
0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_PONG
|
||||
};
|
||||
|
||||
static const uint8_t ipa_id_ack_msg[] = {
|
||||
0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_ACK
|
||||
};
|
||||
|
||||
static const uint8_t ipa_id_req_msg[] = {
|
||||
0, 17, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_GET,
|
||||
0x01, IPAC_IDTAG_UNIT,
|
||||
0x01, IPAC_IDTAG_MACADDR,
|
||||
0x01, IPAC_IDTAG_LOCATION1,
|
||||
0x01, IPAC_IDTAG_LOCATION2,
|
||||
0x01, IPAC_IDTAG_EQUIPVERS,
|
||||
0x01, IPAC_IDTAG_SWVERSION,
|
||||
0x01, IPAC_IDTAG_UNITNAME,
|
||||
0x01, IPAC_IDTAG_SERNR,
|
||||
};
|
||||
|
||||
|
||||
static const char *idtag_names[] = {
|
||||
[IPAC_IDTAG_SERNR] = "Serial_Number",
|
||||
[IPAC_IDTAG_UNITNAME] = "Unit_Name",
|
||||
[IPAC_IDTAG_LOCATION1] = "Location_1",
|
||||
[IPAC_IDTAG_LOCATION2] = "Location_2",
|
||||
[IPAC_IDTAG_EQUIPVERS] = "Equipment_Version",
|
||||
[IPAC_IDTAG_SWVERSION] = "Software_Version",
|
||||
[IPAC_IDTAG_IPADDR] = "IP_Address",
|
||||
[IPAC_IDTAG_MACADDR] = "MAC_Address",
|
||||
[IPAC_IDTAG_UNIT] = "Unit_ID",
|
||||
};
|
||||
|
||||
const char *ipaccess_idtag_name(uint8_t tag)
|
||||
{
|
||||
if (tag >= ARRAY_SIZE(idtag_names))
|
||||
return "unknown";
|
||||
|
||||
return idtag_names[tag];
|
||||
}
|
||||
|
||||
int ipaccess_idtag_parse(struct tlv_parsed *dec, unsigned char *buf, int len)
|
||||
{
|
||||
uint8_t t_len;
|
||||
uint8_t t_tag;
|
||||
uint8_t *cur = buf;
|
||||
|
||||
memset(dec, 0, sizeof(*dec));
|
||||
|
||||
while (len >= 2) {
|
||||
len -= 2;
|
||||
t_len = *cur++;
|
||||
t_tag = *cur++;
|
||||
|
||||
if (t_len > len + 1) {
|
||||
LOGP(DLMI, LOGL_ERROR, "The tag does not fit: %d\n", t_len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
DEBUGPC(DLMI, "%s='%s' ", ipaccess_idtag_name(t_tag), cur);
|
||||
|
||||
dec->lv[t_tag].len = t_len;
|
||||
dec->lv[t_tag].val = cur;
|
||||
|
||||
cur += t_len;
|
||||
len -= t_len;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ipaccess_parse_unitid(const char *str, struct ipaccess_unit *unit_data)
|
||||
{
|
||||
unsigned long ul;
|
||||
char *endptr;
|
||||
const char *nptr;
|
||||
|
||||
nptr = str;
|
||||
ul = strtoul(nptr, &endptr, 10);
|
||||
if (endptr <= nptr)
|
||||
return -EINVAL;
|
||||
unit_data->site_id = ul & 0xffff;
|
||||
|
||||
if (*endptr++ != '/')
|
||||
return -EINVAL;
|
||||
|
||||
nptr = endptr;
|
||||
ul = strtoul(nptr, &endptr, 10);
|
||||
if (endptr <= nptr)
|
||||
return -EINVAL;
|
||||
unit_data->bts_id = ul & 0xffff;
|
||||
|
||||
if (*endptr++ != '/')
|
||||
return -EINVAL;
|
||||
|
||||
nptr = endptr;
|
||||
ul = strtoul(nptr, &endptr, 10);
|
||||
if (endptr <= nptr)
|
||||
return -EINVAL;
|
||||
unit_data->trx_id = ul & 0xffff;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ipaccess_tlv_to_unitdata(struct ipaccess_unit *ud,
|
||||
const struct tlv_parsed *tp)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (TLVP_PRES_LEN(tp, IPAC_IDTAG_SERNR, 1))
|
||||
ud->serno = talloc_strdup(ud, (char *)
|
||||
TLVP_VAL(tp, IPAC_IDTAG_SERNR));
|
||||
|
||||
if (TLVP_PRES_LEN(tp, IPAC_IDTAG_UNITNAME, 1))
|
||||
ud->unit_name = talloc_strdup(ud, (char *)
|
||||
TLVP_VAL(tp, IPAC_IDTAG_UNITNAME));
|
||||
|
||||
if (TLVP_PRES_LEN(tp, IPAC_IDTAG_LOCATION1, 1))
|
||||
ud->location1 = talloc_strdup(ud, (char *)
|
||||
TLVP_VAL(tp, IPAC_IDTAG_LOCATION1));
|
||||
|
||||
if (TLVP_PRES_LEN(tp, IPAC_IDTAG_LOCATION2, 1))
|
||||
ud->location2 = talloc_strdup(ud, (char *)
|
||||
TLVP_VAL(tp, IPAC_IDTAG_LOCATION2));
|
||||
|
||||
if (TLVP_PRES_LEN(tp, IPAC_IDTAG_EQUIPVERS, 1))
|
||||
ud->equipvers = talloc_strdup(ud, (char *)
|
||||
TLVP_VAL(tp, IPAC_IDTAG_EQUIPVERS));
|
||||
|
||||
if (TLVP_PRES_LEN(tp, IPAC_IDTAG_SWVERSION, 1))
|
||||
ud->swversion = talloc_strdup(ud, (char *)
|
||||
TLVP_VAL(tp, IPAC_IDTAG_SWVERSION));
|
||||
|
||||
if (TLVP_PRES_LEN(tp, IPAC_IDTAG_MACADDR, 17)) {
|
||||
rc = osmo_macaddr_parse(ud->mac_addr, (char *)
|
||||
TLVP_VAL(tp, IPAC_IDTAG_MACADDR));
|
||||
if (rc < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (TLVP_PRES_LEN(tp, IPAC_IDTAG_UNIT, 1))
|
||||
rc = ipaccess_parse_unitid((char *)
|
||||
TLVP_VAL(tp, IPAC_IDTAG_UNIT), ud);
|
||||
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
int ipaccess_send(int fd, const void *msg, size_t msglen)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = write(fd, msg, msglen);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret < msglen) {
|
||||
LOGP(DLINP, LOGL_ERROR, "ipaccess_send: short write\n");
|
||||
return -EIO;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ipaccess_send_pong(int fd)
|
||||
{
|
||||
return ipaccess_send(fd, ipa_pong_msg, sizeof(ipa_pong_msg));
|
||||
}
|
||||
|
||||
int ipaccess_send_id_ack(int fd)
|
||||
{
|
||||
return ipaccess_send(fd, ipa_id_ack_msg, sizeof(ipa_id_ack_msg));
|
||||
}
|
||||
|
||||
int ipaccess_send_id_req(int fd)
|
||||
{
|
||||
return ipaccess_send(fd, ipa_id_req_msg, sizeof(ipa_id_req_msg));
|
||||
}
|
||||
|
||||
/* base handling of the ip.access protocol */
|
||||
int ipaccess_rcvmsg_base(struct msgb *msg, struct osmo_fd *bfd)
|
||||
{
|
||||
uint8_t msg_type = *(msg->l2h);
|
||||
int ret;
|
||||
|
||||
switch (msg_type) {
|
||||
case IPAC_MSGT_PING:
|
||||
ret = ipaccess_send_pong(bfd->fd);
|
||||
if (ret < 0) {
|
||||
LOGP(DLINP, LOGL_ERROR, "Cannot send PING "
|
||||
"message. Reason: %s\n", strerror(errno));
|
||||
break;
|
||||
}
|
||||
ret = 1;
|
||||
break;
|
||||
case IPAC_MSGT_PONG:
|
||||
DEBUGP(DLMI, "PONG!\n");
|
||||
ret = 1;
|
||||
break;
|
||||
case IPAC_MSGT_ID_ACK:
|
||||
DEBUGP(DLMI, "ID_ACK? -> ACK!\n");
|
||||
ret = ipaccess_send_id_ack(bfd->fd);
|
||||
if (ret < 0) {
|
||||
LOGP(DLINP, LOGL_ERROR, "Cannot send ID_ACK "
|
||||
"message. Reason: %s\n", strerror(errno));
|
||||
break;
|
||||
}
|
||||
ret = 1;
|
||||
break;
|
||||
default:
|
||||
/* This is not an IPA PING, PONG or ID_ACK message */
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* base handling of the ip.access protocol */
|
||||
int ipaccess_rcvmsg_bts_base(struct msgb *msg,
|
||||
struct osmo_fd *bfd)
|
||||
{
|
||||
uint8_t msg_type = *(msg->l2h);
|
||||
int ret = 0;
|
||||
|
||||
switch (msg_type) {
|
||||
case IPAC_MSGT_PING:
|
||||
ret = ipaccess_send_pong(bfd->fd);
|
||||
if (ret < 0) {
|
||||
LOGP(DLINP, LOGL_ERROR, "Cannot send PONG "
|
||||
"message. Reason: %s\n", strerror(errno));
|
||||
}
|
||||
break;
|
||||
case IPAC_MSGT_PONG:
|
||||
DEBUGP(DLMI, "PONG!\n");
|
||||
break;
|
||||
case IPAC_MSGT_ID_ACK:
|
||||
DEBUGP(DLMI, "ID_ACK\n");
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void ipaccess_prepend_header_ext(struct msgb *msg, int proto)
|
||||
{
|
||||
struct ipaccess_head_ext *hh_ext;
|
||||
|
||||
/* prepend the osmo ip.access header extension */
|
||||
hh_ext = (struct ipaccess_head_ext *) msgb_push(msg, sizeof(*hh_ext));
|
||||
hh_ext->proto = proto;
|
||||
}
|
||||
|
||||
void ipaccess_prepend_header(struct msgb *msg, int proto)
|
||||
{
|
||||
struct ipaccess_head *hh;
|
||||
|
||||
/* prepend the ip.access header */
|
||||
hh = (struct ipaccess_head *) msgb_push(msg, sizeof(*hh));
|
||||
hh->len = htons(msg->len - sizeof(*hh));
|
||||
hh->proto = proto;
|
||||
}
|
||||
|
||||
int ipa_msg_recv(int fd, struct msgb **rmsg)
|
||||
{
|
||||
int rc = ipa_msg_recv_buffered(fd, rmsg, NULL);
|
||||
if (rc < 0) {
|
||||
errno = -rc;
|
||||
rc = -1;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int ipa_msg_recv_buffered(int fd, struct msgb **rmsg, struct msgb **tmp_msg)
|
||||
{
|
||||
struct msgb *msg = tmp_msg ? *tmp_msg : NULL;
|
||||
struct ipaccess_head *hh;
|
||||
int len, ret;
|
||||
int needed;
|
||||
|
||||
if (msg == NULL) {
|
||||
msg = ipa_msg_alloc(0);
|
||||
if (msg == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto discard_msg;
|
||||
}
|
||||
msg->l1h = msg->tail;
|
||||
}
|
||||
|
||||
if (msg->l2h == NULL) {
|
||||
/* first read our 3-byte header */
|
||||
needed = sizeof(*hh) - msg->len;
|
||||
ret = recv(fd, msg->tail, needed, 0);
|
||||
if (ret == 0)
|
||||
goto discard_msg;
|
||||
|
||||
if (ret < 0) {
|
||||
if (errno == EAGAIN || errno == EINTR)
|
||||
ret = 0;
|
||||
else {
|
||||
ret = -errno;
|
||||
goto discard_msg;
|
||||
}
|
||||
}
|
||||
|
||||
msgb_put(msg, ret);
|
||||
|
||||
if (ret < needed) {
|
||||
if (msg->len == 0) {
|
||||
ret = -EAGAIN;
|
||||
goto discard_msg;
|
||||
}
|
||||
|
||||
LOGP(DLINP, LOGL_INFO,
|
||||
"Received part of IPA message header (%d/%d)\n",
|
||||
msg->len, sizeof(*hh));
|
||||
if (!tmp_msg) {
|
||||
ret = -EIO;
|
||||
goto discard_msg;
|
||||
}
|
||||
*tmp_msg = msg;
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
msg->l2h = msg->tail;
|
||||
}
|
||||
|
||||
hh = (struct ipaccess_head *) msg->data;
|
||||
|
||||
/* then read the length as specified in header */
|
||||
len = ntohs(hh->len);
|
||||
|
||||
if (len < 0 || IPA_ALLOC_SIZE < len + sizeof(*hh)) {
|
||||
LOGP(DLINP, LOGL_ERROR, "bad message length of %d bytes, "
|
||||
"received %d bytes\n", len, msg->len);
|
||||
ret = -EIO;
|
||||
goto discard_msg;
|
||||
}
|
||||
|
||||
needed = len - msgb_l2len(msg);
|
||||
|
||||
if (needed > 0) {
|
||||
ret = recv(fd, msg->tail, needed, 0);
|
||||
|
||||
if (ret == 0)
|
||||
goto discard_msg;
|
||||
|
||||
if (ret < 0) {
|
||||
if (errno == EAGAIN || errno == EINTR)
|
||||
ret = 0;
|
||||
else {
|
||||
ret = -errno;
|
||||
goto discard_msg;
|
||||
}
|
||||
}
|
||||
|
||||
msgb_put(msg, ret);
|
||||
|
||||
if (ret < needed) {
|
||||
LOGP(DLINP, LOGL_INFO,
|
||||
"Received part of IPA message L2 data (%d/%d)\n",
|
||||
msgb_l2len(msg), len);
|
||||
if (!tmp_msg) {
|
||||
ret = -EIO;
|
||||
goto discard_msg;
|
||||
}
|
||||
*tmp_msg = msg;
|
||||
return -EAGAIN;
|
||||
}
|
||||
}
|
||||
|
||||
ret = msgb_l2len(msg);
|
||||
|
||||
if (ret == 0) {
|
||||
LOGP(DLINP, LOGL_INFO,
|
||||
"Discarding IPA message without payload\n");
|
||||
ret = -EAGAIN;
|
||||
goto discard_msg;
|
||||
}
|
||||
|
||||
if (tmp_msg)
|
||||
*tmp_msg = NULL;
|
||||
*rmsg = msg;
|
||||
return ret;
|
||||
|
||||
discard_msg:
|
||||
if (tmp_msg)
|
||||
*tmp_msg = NULL;
|
||||
msgb_free(msg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct msgb *ipa_msg_alloc(int headroom)
|
||||
{
|
||||
struct msgb *nmsg;
|
||||
|
||||
headroom += sizeof(struct ipaccess_head);
|
||||
|
||||
nmsg = msgb_alloc_headroom(1200 + headroom, headroom, "Abis/IP");
|
||||
if (!nmsg)
|
||||
return NULL;
|
||||
return nmsg;
|
||||
}
|
Loading…
Reference in New Issue