diff options
Diffstat (limited to 'ggsn/ggsn.c')
-rw-r--r-- | ggsn/ggsn.c | 94 |
1 files changed, 93 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 */ |