diff --git a/src/host/layer23/include/osmocom/bb/common/apn.h b/src/host/layer23/include/osmocom/bb/common/apn.h index 4a92a624c..94784ef71 100644 --- a/src/host/layer23/include/osmocom/bb/common/apn.h +++ b/src/host/layer23/include/osmocom/bb/common/apn.h @@ -19,6 +19,7 @@ #include #include +#include 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) diff --git a/src/host/layer23/include/osmocom/bb/common/l23_app.h b/src/host/layer23/include/osmocom/bb/common/l23_app.h index 46c3f44bf..d442e7e2b 100644 --- a/src/host/layer23/include/osmocom/bb/common/l23_app.h +++ b/src/host/layer23/include/osmocom/bb/common/l23_app.h @@ -1,6 +1,8 @@ #ifndef _L23_APP_H #define _L23_APP_H +#include + 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(); diff --git a/src/host/layer23/include/osmocom/bb/common/logging.h b/src/host/layer23/include/osmocom/bb/common/logging.h index e96852847..5259f35e0 100644 --- a/src/host/layer23/include/osmocom/bb/common/logging.h +++ b/src/host/layer23/include/osmocom/bb/common/logging.h @@ -26,6 +26,7 @@ enum { DPRIM, DLUA, DGAPK, + DTUN, }; extern const struct log_info log_info; diff --git a/src/host/layer23/src/common/apn.c b/src/host/layer23/src/common/apn.c index f60053d9e..169e30e9a 100644 --- a/src/host/layer23/src/common/apn.c +++ b/src/host/layer23/src/common/apn.c @@ -18,13 +18,15 @@ #include #include #include -#include +#include +#include #include #include #include #include +#include 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; } diff --git a/src/host/layer23/src/common/logging.c b/src/host/layer23/src/common/logging.c index 56f032248..34c171f63 100644 --- a/src/host/layer23/src/common/logging.c +++ b/src/host/layer23/src/common/logging.c @@ -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 = { diff --git a/src/host/layer23/src/mobile/app_mobile.c b/src/host/layer23/src/mobile/app_mobile.c index 5bb19209f..285672d7b 100644 --- a/src/host/layer23/src/mobile/app_mobile.c +++ b/src/host/layer23/src/mobile/app_mobile.c @@ -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; diff --git a/src/host/layer23/src/modem/app_modem.c b/src/host/layer23/src/modem/app_modem.c index d7c9a29db..6b8466d77 100644 --- a/src/host/layer23/src/modem/app_modem.c +++ b/src/host/layer23/src/modem/app_modem.c @@ -23,9 +23,14 @@ #include #include +#include +#include + #include #include #include +#include +#include #include #include @@ -42,6 +47,7 @@ #include #include #include +#include #include #include @@ -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)