layer23: Initial integration of tun device

Use the new tundev API from libosmocore to create tun devices for each
configured (and enbled) APN.
No address nor routes are yet set on the tun devices because we still
lack GMM/SNDCP/LLC/RLCMAC layers to negotiate them.

A follow up patch will add some code to interact with the SNDCP layer.

Depends: libosmocore.git Change-Id I3463271666df1e85746fb7b06ec45a17024b8c53
Change-Id: I86cac406843157aa2e51727cf8ccac9804d7961d
This commit is contained in:
Pau Espin 2023-01-17 11:01:37 +01:00
parent 6327f40be6
commit 3da09d1f7e
7 changed files with 124 additions and 1 deletions

View File

@ -19,6 +19,7 @@
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/select.h>
#include <osmocom/core/tun.h>
struct osmocom_ms;
@ -48,9 +49,16 @@ struct osmobb_apn {
/* transmit G-PDU sequence numbers (true) or not (false) */
bool tx_gpdu_seq;
} cfg;
struct osmo_tundev *tun;
};
struct osmobb_apn *apn_alloc(struct osmocom_ms *ms, const char *name);
void apn_free(struct osmobb_apn *apn);
int apn_start(struct osmobb_apn *apn);
int apn_stop(struct osmobb_apn *apn);
#define LOGPAPN(level, apn, fmt, args...) \
LOGP(DTUN, level, "APN(%s): " fmt, (apn)->cfg.name, ## args)
#define LOGTUN(level, tun, fmt, args...) \
LOGP(DTUN, level, "TUN(%s): " fmt, osmo_tundev_get_name(tun), ## args)

View File

@ -1,6 +1,8 @@
#ifndef _L23_APP_H
#define _L23_APP_H
#include <osmocom/core/tun.h>
struct option;
struct vty_app_info;
@ -37,6 +39,7 @@ struct l23_app_info {
int (*cfg_getopt_opt)(struct option **options);
int (*cfg_handle_opt)(int c,const char *optarg);
int (*vty_init)(void);
osmo_tundev_data_ind_cb_t tun_data_ind_cb;
};
extern struct l23_app_info *l23_app_info();

View File

@ -26,6 +26,7 @@ enum {
DPRIM,
DLUA,
DGAPK,
DTUN,
};
extern const struct log_info log_info;

View File

@ -18,13 +18,15 @@
#include <stdint.h>
#include <errno.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <talloc.h>
#include <osmocom/bb/common/logging.h>
#include <osmocom/bb/common/apn.h>
#include <osmocom/bb/common/ms.h>
#include <osmocom/bb/common/l23_app.h>
struct osmobb_apn *apn_alloc(struct osmocom_ms *ms, const char *name)
{
@ -38,6 +40,13 @@ struct osmobb_apn *apn_alloc(struct osmocom_ms *ms, const char *name)
apn->cfg.shutdown = true;
apn->cfg.tx_gpdu_seq = true;
apn->tun = osmo_tundev_alloc(apn, name);
if (!apn->tun) {
talloc_free(apn);
return NULL;
}
osmo_tundev_set_priv_data(apn->tun, apn);
apn->ms = ms;
llist_add_tail(&apn->list, &ms->gprs.apn_list);
return apn;
@ -46,15 +55,53 @@ struct osmobb_apn *apn_alloc(struct osmocom_ms *ms, const char *name)
void apn_free(struct osmobb_apn *apn)
{
llist_del(&apn->list);
osmo_tundev_free(apn->tun);
talloc_free(apn);
}
int apn_start(struct osmobb_apn *apn)
{
struct l23_app_info *app_info = l23_app_info();
int rc;
if (apn->started)
return 0;
LOGPAPN(LOGL_INFO, apn, "Opening TUN device %s\n", apn->cfg.dev_name);
/* Set TUN library callback. Must have been configured by the app: */
OSMO_ASSERT(app_info && app_info->tun_data_ind_cb);
osmo_tundev_set_data_ind_cb(apn->tun, app_info->tun_data_ind_cb);
osmo_tundev_set_dev_name(apn->tun, apn->cfg.dev_name);
osmo_tundev_set_netns_name(apn->tun, apn->cfg.dev_netns_name);
rc = osmo_tundev_open(apn->tun);
if (rc < 0) {
LOGPAPN(LOGL_ERROR, apn, "Failed to configure tun device\n");
return -1;
}
LOGPAPN(LOGL_INFO, apn, "Opened TUN device %s\n", osmo_tundev_get_dev_name(apn->tun));
/* TODO: set IP addresses on the tun device once we receive them from GGSN. See
osmo-ggsn.git's apn_start() */
LOGPAPN(LOGL_NOTICE, apn, "Successfully started\n");
apn->started = true;
return 0;
}
int apn_stop(struct osmobb_apn *apn)
{
LOGPAPN(LOGL_NOTICE, apn, "Stopping\n");
/* shutdown whatever old state might be left */
if (apn->tun) {
/* release tun device */
LOGPAPN(LOGL_INFO, apn, "Closing TUN device %s\n",
osmo_tundev_get_dev_name(apn->tun));
osmo_tundev_close(apn->tun);
}
apn->started = false;
return 0;
}

View File

@ -147,6 +147,12 @@ static const struct log_info_cat default_categories[] = {
.color = "\033[0;36m",
.enabled = 1, .loglevel = LOGL_DEBUG,
},
[DTUN] = {
.name = "DTUN",
.description = "Tunnel interface",
.color = "\033[0;37m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
};
const struct log_info log_info = {

View File

@ -508,6 +508,11 @@ int l23_app_init(const char *config_file)
return 0;
}
struct l23_app_info *l23_app_info(void)
{
return NULL; /* TODO: implement mobile as a full l23_app. */
}
void mobile_set_started(struct osmocom_ms *ms, bool state)
{
ms->started = state;

View File

@ -23,9 +23,14 @@
#include <errno.h>
#include <stdio.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/signal.h>
#include <osmocom/core/application.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/tun.h>
#include <osmocom/vty/vty.h>
#include <osmocom/gsm/rsl.h>
@ -42,6 +47,7 @@
#include <osmocom/bb/common/l23_app.h>
#include <osmocom/bb/common/l1l2_interface.h>
#include <osmocom/bb/common/sysinfo.h>
#include <osmocom/bb/common/apn.h>
#include <osmocom/bb/modem/vty.h>
#include <l1ctl_proto.h>
@ -60,6 +66,52 @@ static struct {
} chan_req;
} app_data;
/* Local network-originated IP packet, needs to be sent via SNDCP/LLC (GPRS) towards GSM network */
static int modem_tun_data_ind_cb(struct osmo_tundev *tun, struct msgb *msg)
{
struct osmobb_apn *apn = (struct osmobb_apn *)osmo_tundev_get_priv_data(tun);
struct osmo_sockaddr dst;
struct iphdr *iph = (struct iphdr *)msgb_data(msg);
struct ip6_hdr *ip6h = (struct ip6_hdr *)msgb_data(msg);
size_t pkt_len = msgb_length(msg);
uint8_t pref_offset;
char addrstr[INET6_ADDRSTRLEN];
int rc = 0;
switch (iph->version) {
case 4:
if (pkt_len < sizeof(*iph) || pkt_len < 4*iph->ihl)
return -1;
dst.u.sin.sin_family = AF_INET;
dst.u.sin.sin_addr.s_addr = iph->daddr;
break;
case 6:
/* Due to the fact that 3GPP requires an allocation of a
* /64 prefix to each MS, we must instruct
* ippool_getip() below to match only the leading /64
* prefix, i.e. the first 8 bytes of the address. If the ll addr
* is used, then the match should be done on the trailing 64
* bits. */
dst.u.sin6.sin6_family = AF_INET6;
pref_offset = IN6_IS_ADDR_LINKLOCAL(&ip6h->ip6_dst) ? 8 : 0;
memcpy(&dst.u.sin6.sin6_addr, ((uint8_t *)&ip6h->ip6_dst) + pref_offset, 8);
break;
default:
LOGTUN(LOGL_NOTICE, tun, "non-IPv%u packet received\n", iph->version);
rc = -1;
goto free_ret;
}
LOGPAPN(LOGL_DEBUG, apn, "system wants to transmit IPv%c pkt to %s (%zu bytes)\n",
iph->version == 4 ? '4' : '6', osmo_sockaddr_ntop(&dst.u.sa, addrstr), pkt_len);
/* TODO: prepare & transmit SNDCP UNITDATA.req */
free_ret:
msgb_free(msg);
return rc;
}
/* Generate a 8-bit CHANNEL REQUEST message as per 3GPP TS 44.018, 9.1.8 */
static uint8_t gen_chan_req(bool single_block)
{
@ -517,6 +569,7 @@ static struct l23_app_info info = {
.cfg_supported = &l23_cfg_supported,
.vty_info = &_modem_vty_info,
.vty_init = modem_vty_init,
.tun_data_ind_cb = modem_tun_data_ind_cb,
};
struct l23_app_info *l23_app_info(void)