layer23: Introduce APN VTY node

This commit adds an initial set of VTY commands to manage APN
configuration and set up, which is used by the modem app.

The sample modem.cfg file is updated to showcase how to configure APNs.
The app doesn't do anything with them yet. A follow up patch will add
code to create tun devices for each configured APN.

Change-Id: I7b4eaa0de428b418bb1d89bd544694e89beb3e6e
This commit is contained in:
Pau Espin 2023-01-16 12:55:11 +01:00
parent c1bddf20b5
commit 6327f40be6
12 changed files with 381 additions and 3 deletions

View File

@ -6,4 +6,9 @@ line vty
no login
!
ms 1
apn internet
tun-device modem4
! tun-netns netns_modem4
type-support v4
no shutdown
no shutdown

View File

@ -1,4 +1,5 @@
noinst_HEADERS = \
apn.h \
l1ctl.h \
l1l2_interface.h \
l23_app.h \

View File

@ -0,0 +1,56 @@
/* APN Context
* (C) 2023 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
*/
#pragma once
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/select.h>
struct osmocom_ms;
#define APN_TYPE_IPv4 0x01 /* v4-only */
#define APN_TYPE_IPv6 0x02 /* v6-only */
#define APN_TYPE_IPv4v6 0x04 /* v4v6 dual-stack */
struct osmobb_apn {
/* list of APNs inside MS */
struct llist_head list;
/* back-pointer to MS */
struct osmocom_ms *ms;
bool started;
struct {
/* Primary name */
char *name;
/* name of the network device */
char *dev_name;
/* netns name of the network device, NULL = default netns */
char *dev_netns_name;
/* types supported address types on this APN */
uint32_t apn_type_mask;
/* administratively shutdown (true) or not (false) */
bool shutdown;
/* transmit G-PDU sequence numbers (true) or not (false) */
bool tx_gpdu_seq;
} cfg;
};
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);

View File

@ -90,6 +90,9 @@ struct osmocom_ms {
struct osmomncc_entity mncc_entity;
struct llist_head trans_list;
/* GPRS */
struct gprs_settings gprs;
/* Audio I/O */
struct gapk_io_state *gapk_io;

View File

@ -5,6 +5,7 @@
#include <osmocom/core/linuxlist.h>
struct osmocom_ms;
struct osmobb_apn;
#define MOB_C7_DEFLT_ANY_TIMEOUT 30
@ -180,6 +181,14 @@ int gsm_settings_exit(struct osmocom_ms *ms);
char *gsm_check_imei(const char *imei, const char *sv);
int gsm_random_imei(struct gsm_settings *set);
struct gprs_settings {
struct llist_head apn_list;
};
int gprs_settings_init(struct osmocom_ms *ms);
int gprs_settings_fi(struct osmocom_ms *ms);
struct osmobb_apn *ms_find_apn_by_name(struct osmocom_ms *ms, const char *apn_name);
extern char *layer2_socket_path;
#endif /* _settings_h */

View File

@ -1,3 +1,10 @@
#pragma once
#include <osmocom/bb/common/vty.h>
enum modem_vty_node {
APN_NODE = _LAST_L23VTY_NODE + 1,
};
int modem_vty_init(void);
int modem_vty_go_parent(struct vty *vty);

View File

@ -13,6 +13,7 @@ AM_CFLAGS = \
noinst_LIBRARIES = liblayer23.a
liblayer23_a_SOURCES = \
apn.c \
gps.c \
l1ctl.c \
l1l2_interface.c \

View File

@ -0,0 +1,60 @@
/*
* (C) 2023 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
*/
#include <stdint.h>
#include <errno.h>
#include <string.h>
#include <arpa/inet.h>
#include <talloc.h>
#include <osmocom/bb/common/logging.h>
#include <osmocom/bb/common/apn.h>
#include <osmocom/bb/common/ms.h>
struct osmobb_apn *apn_alloc(struct osmocom_ms *ms, const char *name)
{
struct osmobb_apn *apn;
apn = talloc_zero(ms, struct osmobb_apn);
if (!apn)
return NULL;
talloc_set_name(apn, "apn_%s", name);
apn->cfg.name = talloc_strdup(apn, name);
apn->cfg.shutdown = true;
apn->cfg.tx_gpdu_seq = true;
apn->ms = ms;
llist_add_tail(&apn->list, &ms->gprs.apn_list);
return apn;
}
void apn_free(struct osmobb_apn *apn)
{
llist_del(&apn->list);
talloc_free(apn);
}
int apn_start(struct osmobb_apn *apn)
{
return 0;
}
int apn_stop(struct osmobb_apn *apn)
{
return 0;
}

View File

@ -24,6 +24,12 @@ extern struct llist_head ms_list;
/* Default value be configured by cmdline arg: */
uint16_t cfg_test_arfcn = 871;
static int osmocom_ms_talloc_destructor(struct osmocom_ms *ms)
{
gprs_settings_fi(ms);
return 0;
}
struct osmocom_ms *osmocom_ms_alloc(void *ctx, const char *name)
{
struct osmocom_ms *ms;
@ -32,6 +38,7 @@ struct osmocom_ms *osmocom_ms_alloc(void *ctx, const char *name)
if (!ms)
return NULL;
talloc_set_name(ms, "ms_%s", name);
talloc_set_destructor(ms, osmocom_ms_talloc_destructor);
ms->name = talloc_strdup(ms, name);
ms->test_arfcn = cfg_test_arfcn;
@ -50,6 +57,7 @@ struct osmocom_ms *osmocom_ms_alloc(void *ctx, const char *name)
gsm_support_init(ms);
gsm_settings_init(ms);
gprs_settings_init(ms);
return ms;
}

View File

@ -20,13 +20,12 @@
#include <string.h>
#include <osmocom/core/talloc.h>
#include <osmocom/bb/mobile/app_mobile.h>
#include <osmocom/bb/common/settings.h>
#include <osmocom/bb/common/utils.h>
#include <osmocom/bb/common/logging.h>
#include <osmocom/bb/common/osmocom_data.h>
#include <osmocom/bb/common/apn.h>
#include <osmocom/bb/common/ms.h>
#include <osmocom/bb/common/networks.h>
#include <osmocom/bb/common/l1l2_interface.h>
/* Used to set default path globally through cmdline */
@ -220,3 +219,35 @@ const struct value_string audio_io_format_names[] = {
{ AUDIO_IOF_TI, "ti" },
{ 0x00, NULL}
};
int gprs_settings_init(struct osmocom_ms *ms)
{
struct gprs_settings *set = &ms->gprs;
INIT_LLIST_HEAD(&set->apn_list);
return 0;
}
int gprs_settings_fi(struct osmocom_ms *ms)
{
struct gprs_settings *set = &ms->gprs;
struct osmobb_apn *apn;
while ((apn = llist_first_entry_or_null(&set->apn_list, struct osmobb_apn, list))) {
/* free calls llist_del(): */
apn_free(apn);
}
return 0;
}
struct osmobb_apn *ms_find_apn_by_name(struct osmocom_ms *ms, const char *apn_name)
{
struct gprs_settings *set = &ms->gprs;
struct osmobb_apn *apn;
llist_for_each_entry(apn, &set->apn_list, list) {
if (strcmp(apn->cfg.name, apn_name) == 0)
return apn;
}
return NULL;
}

View File

@ -509,6 +509,7 @@ static int l23_cfg_supported(void)
static struct vty_app_info _modem_vty_info = {
.name = "modem",
.version = PACKAGE_VERSION,
.go_parent_cb = modem_vty_go_parent,
};
static struct l23_app_info info = {

View File

@ -25,14 +25,200 @@
#include <osmocom/vty/command.h>
#include <osmocom/bb/common/vty.h>
#include <osmocom/bb/common/apn.h>
#include <osmocom/bb/common/ms.h>
#include <osmocom/bb/modem/vty.h>
static struct cmd_node apn_node = {
APN_NODE,
"%s(apn)# ",
1
};
int modem_vty_go_parent(struct vty *vty)
{
struct osmobb_apn *apn;
switch (vty->node) {
case APN_NODE:
apn = vty->index;
vty->index = apn->ms;
vty->node = MS_NODE;
break;
}
return vty->node;
}
/* per APN config */
DEFUN(cfg_ms_apn, cfg_ms_apn_cmd, "apn APN_NAME",
"Configure an APN\n"
"Name of APN\n")
{
struct osmocom_ms *ms = vty->index;
struct osmobb_apn *apn;
apn = ms_find_apn_by_name(ms, argv[0]);
if (!apn)
apn = apn_alloc(ms, argv[0]);
if (!apn) {
vty_out(vty, "Unable to create APN '%s'%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
vty->index = apn;
vty->node = APN_NODE;
return CMD_SUCCESS;
}
DEFUN(cfg_ms_no_apn, cfg_ms_no_apn_cmd, "no apn APN_NAME",
NO_STR "Configure an APN\n"
"Name of APN\n")
{
struct osmocom_ms *ms = vty->index;
struct osmobb_apn *apn;
apn = ms_find_apn_by_name(ms, argv[0]);
if (!apn) {
vty_out(vty, "Unable to find APN '%s'%s", argv[0], VTY_NEWLINE);
return CMD_WARNING;
}
apn_free(apn);
return CMD_SUCCESS;
}
DEFUN(cfg_apn_tun_dev_name, cfg_apn_tun_dev_name_cmd,
"tun-device NAME",
"Configure tun device name\n"
"TUN device name")
{
struct osmobb_apn *apn = (struct osmobb_apn *) vty->index;
osmo_talloc_replace_string(apn, &apn->cfg.dev_name, argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_apn_tun_netns_name, cfg_apn_tun_netns_name_cmd,
"tun-netns NAME",
"Configure tun device network namespace name\n"
"TUN device network namespace name")
{
struct osmobb_apn *apn = (struct osmobb_apn *) vty->index;
osmo_talloc_replace_string(apn, &apn->cfg.dev_netns_name, argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_apn_no_tun_netns_name, cfg_apn_no_tun_netns_name_cmd,
"no tun-netns",
"Configure tun device to use default network namespace name\n")
{
struct osmobb_apn *apn = (struct osmobb_apn *) vty->index;
TALLOC_FREE(apn->cfg.dev_netns_name);
return CMD_SUCCESS;
}
static const struct value_string pdp_type_names[] = {
{ APN_TYPE_IPv4, "v4" },
{ APN_TYPE_IPv6, "v6" },
{ APN_TYPE_IPv4v6, "v4v6" },
{ 0, NULL }
};
#define V4V6V46_STRING "IPv4(-only) PDP Type\n" \
"IPv6(-only) PDP Type\n" \
"IPv4v6 (dual-stack) PDP Type\n"
DEFUN(cfg_apn_type_support, cfg_apn_type_support_cmd,
"type-support (v4|v6|v4v6)",
"Enable support for PDP Type\n"
V4V6V46_STRING)
{
struct osmobb_apn *apn = (struct osmobb_apn *) vty->index;
uint32_t type = get_string_value(pdp_type_names, argv[0]);
apn->cfg.apn_type_mask |= type;
return CMD_SUCCESS;
}
DEFUN(cfg_apn_shutdown, cfg_apn_shutdown_cmd,
"shutdown",
"Put the APN in administrative shut-down\n")
{
struct osmobb_apn *apn = (struct osmobb_apn *) vty->index;
if (!apn->cfg.shutdown) {
if (apn_stop(apn)) {
vty_out(vty, "%% Failed to Stop APN%s", VTY_NEWLINE);
return CMD_WARNING;
}
apn->cfg.shutdown = true;
}
return CMD_SUCCESS;
}
DEFUN(cfg_apn_no_shutdown, cfg_apn_no_shutdown_cmd,
"no shutdown",
NO_STR "Remove the APN from administrative shut-down\n")
{
struct osmobb_apn *apn = (struct osmobb_apn *) vty->index;
if (apn->cfg.shutdown) {
if (!apn->cfg.dev_name) {
vty_out(vty, "%% Failed to start APN, tun-device is not configured%s", VTY_NEWLINE);
return CMD_WARNING;
}
if (apn_start(apn) < 0) {
vty_out(vty, "%% Failed to start APN, check log for details%s", VTY_NEWLINE);
return CMD_WARNING;
}
apn->cfg.shutdown = false;
}
return CMD_SUCCESS;
}
static void config_write_apn(struct vty *vty, const struct osmobb_apn *apn)
{
unsigned int i;
vty_out(vty, " apn %s%s", apn->cfg.name, VTY_NEWLINE);
if (apn->cfg.dev_name)
vty_out(vty, " tun-device %s%s", apn->cfg.dev_name, VTY_NEWLINE);
if (apn->cfg.dev_netns_name)
vty_out(vty, " tun-netns %s%s", apn->cfg.dev_netns_name, VTY_NEWLINE);
for (i = 0; i < 32; i++) {
if (!(apn->cfg.apn_type_mask & (UINT32_C(1) << i)))
continue;
vty_out(vty, " type-support %s%s", get_value_string(pdp_type_names, (UINT32_C(1) << i)),
VTY_NEWLINE);
}
/* must be last */
vty_out(vty, " %sshutdown%s", apn->cfg.shutdown ? "" : "no ", VTY_NEWLINE);
}
static void config_write_ms(struct vty *vty, const struct osmocom_ms *ms)
{
struct osmobb_apn *apn;
vty_out(vty, "ms %s%s", ms->name, VTY_NEWLINE);
l23_vty_config_write_ms_node_contents(vty, ms, " ");
llist_for_each_entry(apn, &ms->gprs.apn_list, list)
config_write_apn(vty, apn);
l23_vty_config_write_ms_node_contents_final(vty, ms, " ");
}
static int config_write(struct vty *vty)
{
struct osmocom_ms *ms;
llist_for_each_entry(ms, &ms_list, entity)
l23_vty_config_write_ms_node(vty, ms, "");
config_write_ms(vty, ms);
return CMD_SUCCESS;
}
@ -45,5 +231,15 @@ int modem_vty_init(void)
install_element_ve(&l23_show_ms_cmd);
install_element(CONFIG_NODE, &l23_cfg_ms_cmd);
install_element(MS_NODE, &cfg_ms_apn_cmd);
install_element(MS_NODE, &cfg_ms_no_apn_cmd);
install_node(&apn_node, NULL);
install_element(APN_NODE, &cfg_apn_tun_dev_name_cmd);
install_element(APN_NODE, &cfg_apn_tun_netns_name_cmd);
install_element(APN_NODE, &cfg_apn_no_tun_netns_name_cmd);
install_element(APN_NODE, &cfg_apn_type_support_cmd);
install_element(APN_NODE, &cfg_apn_shutdown_cmd);
install_element(APN_NODE, &cfg_apn_no_shutdown_cmd);
return 0;
}