aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ggsn/ggsn.c94
-rw-r--r--ggsn/ggsn.h16
-rw-r--r--ggsn/ggsn_vty.c58
3 files changed, 167 insertions, 1 deletions
diff --git a/ggsn/ggsn.c b/ggsn/ggsn.c
index a0479a1..91dfadd 100644
--- a/ggsn/ggsn.c
+++ b/ggsn/ggsn.c
@@ -328,6 +328,74 @@ int apn_start(struct apn_ctx *apn)
return 0;
}
+static struct imsi_map_entry *apn_imsi_map_lookup_by_imsi(const char *imsi, const struct apn_ctx_ip *ctx)
+{
+ struct imsi_map_entry *map;
+ llist_for_each_entry(map, &ctx->imsi_ip_map, list) {
+ if (!strcmp(imsi, map->imsi))
+ return map;
+ }
+ return NULL;
+}
+
+static struct imsi_map_entry *apn_imsi_map_lookup_by_ip(const struct in46_addr *addr, const struct apn_ctx_ip *ctx)
+{
+ struct imsi_map_entry *map;
+ if (!addr)
+ return NULL;
+ llist_for_each_entry(map, &ctx->imsi_ip_map, list) {
+ if (in46a_equal(addr, &map->addr))
+ return map;
+ }
+ return NULL;
+}
+
+int apn_imsi_ip_map_add(const char *imsi, const char *ip, struct apn_ctx_ip *ctx)
+{
+ struct imsi_map_entry *map;
+ struct in46_addr addr = { 0 };
+ size_t t;
+
+ if (ippool_aton(&addr, &t, ip, 0))
+ return -EINVAL;
+ if (apn_imsi_map_lookup_by_imsi(imsi, ctx) || apn_imsi_map_lookup_by_ip(&addr, ctx))
+ return -EEXIST;
+
+ map = talloc_zero(NULL, struct imsi_map_entry);
+ if (!map)
+ return -ENOMEM;
+ if (ippool_aton(&map->addr, &t, ip, 0)) {
+ talloc_free(map);
+ return -EINVAL;
+ }
+
+ osmo_strlcpy(map->imsi, imsi, sizeof(map->imsi));
+ llist_add(&map->list, &ctx->imsi_ip_map);
+
+ return 0;
+}
+
+int apn_imsi_ip_map_del(const char *imsi, const char *ip, struct apn_ctx_ip *ctx)
+{
+ struct imsi_map_entry *map;
+
+ map = apn_imsi_map_lookup_by_imsi(imsi, ctx);
+ if (!map)
+ return -ENODEV;
+
+ llist_del(&map->list);
+ talloc_free(map);
+
+ return 0;
+}
+
+static struct imsi_map_entry *imsi_has_reserved_ip(const char *imsi, struct apn_ctx_ip *ctx)
+{
+ if (llist_empty(&ctx->imsi_ip_map))
+ return NULL;
+ return apn_imsi_map_lookup_by_imsi(imsi, ctx);
+}
+
static bool send_trap(const struct gsn_t *gsn, const struct pdp_t *pdp, const struct ippoolm_t *member, const char *var)
{
char addrbuf[256];
@@ -385,7 +453,7 @@ static int delete_context(struct pdp_t *pdp)
return 0;
}
-static bool apn_supports_ipv4(const struct apn_ctx *apn)
+bool apn_supports_ipv4(const struct apn_ctx *apn)
{
if (apn->v4.cfg.static_prefix.addr.len || apn->v4.cfg.dynamic_prefix.addr.len)
return true;
@@ -442,6 +510,7 @@ int create_context_ind(struct pdp_t *pdp)
char *apn_name;
struct sgsn_peer *sgsn;
struct pdp_priv_t *pdp_priv;
+ struct imsi_map_entry *imsi_map;
apn_name = osmo_apn_to_str(name_buf, pdp->apn_req.v, pdp->apn_req.l);
LOGPPDP(LOGL_DEBUG, pdp, "Processing create PDP context request for APN '%s'\n",
@@ -495,6 +564,17 @@ int create_context_ind(struct pdp_t *pdp)
LOGPPDP(LOGL_ERROR, pdp, "Failed to store APN '%s'\n", apn->cfg.name);
pdp->apn_use.l = rc;
+ /* Check if we have a entry in the reserved IP map for this IMSI */
+ imsi_map = imsi_has_reserved_ip(imsi_gtp2str(&pdp->imsi), &apn->v4);
+ if (imsi_map) {
+ /* Override (prefill) any requested (dynamic|static) IP
+ * in the EUA with the one from the configuration map. */
+ addr[0].len = 4;
+ memcpy(&addr[0].v4.s_addr, &imsi_map->addr.v4, 4);
+ LOGPPDP(LOGL_INFO, pdp, "IMSI[%s] has an entry[%s] in reserved IP map\n",
+ imsi_gtp2str(&pdp->imsi), in46a_ntoa(&imsi_map->addr));
+ }
+
/* Allocate dynamic addresses from the pool */
for (i = 0; i < num_addr; i++) {
if (in46a_is_v4(&addr[i])) {
@@ -503,6 +583,18 @@ int create_context_ind(struct pdp_t *pdp)
goto err_wrong_af;
rc = ippool_newip(apn->v4.pool, &member, &addr[i], 0);
+
+ if (!imsi_map) {
+ /* This IMSI does not have a reserved IP, check that we did not assign one */
+ while (apn_imsi_map_lookup_by_ip(&member->addr, &apn->v4)) {
+ LOGPPDP(LOGL_INFO, pdp, "Returned IP[%s] is reserved, trying again.\n",
+ in46a_ntoa(&member->addr));
+ ippool_freeip(member->pool, member);
+ rc = ippool_newip(apn->v4.pool, &member, &addr[i], 0);
+ }
+
+ }
+ LOGPPDP(LOGL_INFO, pdp, "Got IP[%s] from the pool.\n", in46a_ntoa(&member->addr));
if (rc < 0)
goto err_pool_full;
/* copy back */
diff --git a/ggsn/ggsn.h b/ggsn/ggsn.h
index 1abbc9a..44fd3cc 100644
--- a/ggsn/ggsn.h
+++ b/ggsn/ggsn.h
@@ -8,6 +8,7 @@
#include <osmocom/core/timer.h>
#include <osmocom/core/tdef.h>
#include <osmocom/ctrl/control_if.h>
+#include <osmocom/gsm/protocol/gsm_23_003.h>
#include "../lib/tun.h"
#include "../lib/ippool.h"
@@ -35,6 +36,9 @@ struct apn_ctx_ip {
/* v4 address pool */
struct ippool_t *pool;
+ /* Static IMSI to IPv4 reserved address mappings. */
+ struct llist_head imsi_ip_map;
+
};
struct apn_name {
@@ -47,6 +51,15 @@ enum apn_gtpu_mode {
APN_GTPU_MODE_KERNEL_GTP,
};
+/*
+ * IMSI to static IP map handling
+ */
+struct imsi_map_entry {
+ struct llist_head list;
+ char imsi[OSMO_IMSI_BUF_SIZE];
+ struct in46_addr addr;
+};
+
struct apn_ctx {
/* list of APNs inside GGSN */
struct llist_head list;
@@ -161,6 +174,9 @@ extern int ggsn_stop(struct ggsn_ctx *ggsn);
extern int apn_start(struct apn_ctx *apn);
extern int apn_stop(struct apn_ctx *apn);
void ggsn_close_one_pdp(struct pdp_t *pdp);
+bool apn_supports_ipv4(const struct apn_ctx *apn);
+int apn_imsi_ip_map_add(const char *imsi, const char *ip, struct apn_ctx_ip *ctx);
+int apn_imsi_ip_map_del(const char *imsi, const char *ip, struct apn_ctx_ip *ctx);
#define LOGPAPN(level, apn, fmt, args...) \
LOGP(DGGSN, level, "APN(%s): " fmt, (apn)->cfg.name, ## args)
diff --git a/ggsn/ggsn_vty.c b/ggsn/ggsn_vty.c
index 2d49a94..e8b9124 100644
--- a/ggsn/ggsn_vty.c
+++ b/ggsn/ggsn_vty.c
@@ -112,6 +112,7 @@ struct apn_ctx *ggsn_find_or_create_apn(struct ggsn_ctx *ggsn, const char *name)
apn->cfg.shutdown = true;
apn->cfg.tx_gpdu_seq = true;
INIT_LLIST_HEAD(&apn->cfg.name_list);
+ INIT_LLIST_HEAD(&apn->v4.imsi_ip_map);
llist_add_tail(&apn->list, &ggsn->apn_list);
return apn;
@@ -562,6 +563,58 @@ DEFUN(cfg_apn_no_ip_ifconfig, cfg_apn_no_ip_ifconfig_cmd,
return CMD_SUCCESS;
}
+DEFUN(cfg_apn_imsi_ip_map, cfg_apn_imsi_ip_map_cmd,
+ "imsi-ip-map (add|del) IMSI [A.B.C.D]",
+ "List of IMSI to RESERVED ip4 mappings\n"
+ "Add IMSI/IP pair to mappings\n"
+ "Remove IMSI/IP pair from mappings\n"
+ "IMSI of the subscriber\n"
+ "IP for the subscriber\n")
+{
+ char imsi_sanitized[GSM23003_IMSI_MAX_DIGITS + 1];
+ const char *op = argv[0];
+ const char *imsi = imsi_sanitized;
+ size_t len = strnlen(argv[1], GSM23003_IMSI_MAX_DIGITS + 1);
+ const char *ip = argv[2];
+ int rc;
+
+ struct apn_ctx *apn = (struct apn_ctx *) vty->index;
+
+ if (!apn_supports_ipv4(apn)) {
+ vty_out(vty, "%% APN does not support ipv4 addresses%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ memset(imsi_sanitized, '0', GSM23003_IMSI_MAX_DIGITS);
+ imsi_sanitized[GSM23003_IMSI_MAX_DIGITS] = '\0';
+
+ /* Sanitize IMSI */
+ if (len > GSM23003_IMSI_MAX_DIGITS) {
+ vty_out(vty, "%% IMSI (%s) too long (max %u digits) -- ignored!%s",
+ argv[1], GSM23003_IMSI_MAX_DIGITS, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ osmo_strlcpy(imsi_sanitized + GSM23003_IMSI_MAX_DIGITS - len, argv[1],
+ sizeof(imsi_sanitized) - (GSM23003_IMSI_MAX_DIGITS - len));
+
+ if (!strcmp(op, "add")) {
+ if (!ip) {
+ vty_out(vty, "%% unable to %s IMSI without IP address%s", op, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ rc = apn_imsi_ip_map_add(imsi, ip, &apn->v4);
+ } else {
+ rc = apn_imsi_ip_map_del(imsi, ip, &apn->v4);
+ }
+ if (rc < 0) {
+ vty_out(vty, "%% unable to %s IMSI%s", op, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
DEFUN(cfg_apn_ipv6_prefix, cfg_apn_ipv6_prefix_cmd,
"ipv6 prefix (static|dynamic) X:X::X:X/M",
IP6_STR PREFIX_STR "IPv6 Address/Prefix-Length\n")
@@ -726,6 +779,7 @@ static void vty_dump_prefix(struct vty *vty, const char *pre, const struct in46_
static void config_write_apn(struct vty *vty, struct apn_ctx *apn)
{
unsigned int i;
+ struct imsi_map_entry *map;
vty_out(vty, " apn %s%s", apn->cfg.name, VTY_NEWLINE);
if (apn->cfg.description)
@@ -762,6 +816,9 @@ static void config_write_apn(struct vty *vty, struct apn_ctx *apn)
if (apn->v4.cfg.ifconfig_prefix.addr.len)
vty_dump_prefix(vty, " ip ifconfig", &apn->v4.cfg.ifconfig_prefix);
+ llist_for_each_entry(map, &apn->v4.imsi_ip_map, list)
+ vty_out(vty, " imsi-ip-map add %s %s%s", map->imsi, in46a_ntoa(&map->addr), VTY_NEWLINE);
+
/* IPv6 prefixes + DNS */
if (apn->v6.cfg.static_prefix.addr.len)
vty_dump_prefix(vty, " ipv6 prefix static", &apn->v6.cfg.static_prefix);
@@ -1131,6 +1188,7 @@ int ggsn_vty_init(void)
install_element(APN_NODE, &cfg_apn_ipdown_script_cmd);
install_element(APN_NODE, &cfg_apn_no_ipdown_script_cmd);
install_element(APN_NODE, &cfg_apn_ip_prefix_cmd);
+ install_element(APN_NODE, &cfg_apn_imsi_ip_map_cmd);
install_element(APN_NODE, &cfg_apn_ipv6_prefix_cmd);
install_element(APN_NODE, &cfg_apn_ip_dns_cmd);
install_element(APN_NODE, &cfg_apn_ipv6_dns_cmd);