Skip to content

Commit b455c9b

Browse files
committed
switch: Add support to wildcard APN matching & dynamic Realm
access-point-name can define nameserver and force realm and service selection. This wildcard matching can be used to define default behaviour. For example in the following configuration : ---< snip >--- ! access-point-name internet nameserver 1.2.3.4 resolv-max-retry 5 resolv-cache-update 3600 session-lifetime 4500 realm apn.epc.mncXXX.mccYYY.3gppnetwork.org. service-selection x-3gpp-pgw:x-s5-gtp:x-gn prio 20 service-selection x-3gpp-pgw:x-s5-gtp+nc-nr prio 10 ! access-point-name * nameserver 1.2.3.4 resolv-max-retry 1 session-lifetime 15000 realm-dynamic service-selection x-3gpp-pgw:x-s8-gtp:x-gp prio 10 ! ---< snip >--- When Create-Session-Request is received for 'internet' APN, matching will select 'access-point-name internet' and will then use defined realm to perform remote pGW selection. When Create-Session-Request is received for 'op' for example, then matching will select '*' as default/last-change selection. 'realm-dynamic' in wildcard APN refer to automatic NAPTR resolution built from GTP-C 'Access Point Name' IE to extract PLMN and then build following request : op.apn.epc.mncXXX.mccYYY.3gppnetwork.org.
1 parent a9a6498 commit b455c9b

File tree

6 files changed

+124
-28
lines changed

6 files changed

+124
-28
lines changed

src/gtp_apn.c

+9-2
Original file line numberDiff line numberDiff line change
@@ -145,13 +145,20 @@ apn_resolv_cache_realloc(gtp_apn_t *apn)
145145
int err;
146146

147147
/* Context init */
148-
ctx = gtp_resolv_ctx_alloc(apn, apn->name);
148+
ctx = gtp_resolv_ctx_alloc(apn);
149149
if (!ctx)
150150
return -1;
151151

152+
if (!ctx->realm) {
153+
log_message(LOG_INFO, "%s(): no realm available to resolv naptr... keeping previous..."
154+
, __FUNCTION__);
155+
gtp_resolv_ctx_destroy(ctx);
156+
return -1;
157+
}
158+
152159
/* Create temp resolv */
153160
INIT_LIST_HEAD(&l);
154-
err = gtp_resolv_naptr(ctx, &l);
161+
err = gtp_resolv_naptr(ctx, &l, "%s.%s", apn->name, ctx->realm);
155162
if (err) {
156163
log_message(LOG_INFO, "%s(): Unable to update resolv cache while resolving naptr... keeping previous..."
157164
, __FUNCTION__);

src/gtp_resolv.c

+41-11
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <sys/prctl.h>
2727
#include <sys/types.h>
2828
#include <sys/socket.h>
29+
#include <stdarg.h>
2930
#include <ctype.h>
3031
#include <netdb.h>
3132
#include <errno.h>
@@ -341,12 +342,16 @@ gtp_naptr_alloc(list_head_t *l, const u_char *rdata, size_t rdlen)
341342
}
342343

343344
int
344-
gtp_resolv_naptr(gtp_resolv_ctx_t *ctx, list_head_t *l)
345+
gtp_resolv_naptr(gtp_resolv_ctx_t *ctx, list_head_t *l, const char *format, ...)
345346
{
347+
va_list args;
346348
int ret, i, err;
347349

348350
/* Perform Query */
349-
snprintf(ctx->nsdisp, GTP_DISPLAY_BUFFER_LEN - 1, "%s.%s", ctx->apn_ni, ctx->realm);
351+
va_start(args, format);
352+
vsnprintf(ctx->nsdisp, GTP_DISPLAY_BUFFER_LEN, format, args);
353+
va_end(args);
354+
350355
ret = ns_res_nquery_retry(ctx, ns_c_in, ns_t_naptr);
351356
if (ret < 0) {
352357
res_nclose(&ctx->ns_rs);
@@ -371,7 +376,7 @@ gtp_resolv_naptr(gtp_resolv_ctx_t *ctx, list_head_t *l)
371376
}
372377

373378
gtp_resolv_ctx_t *
374-
gtp_resolv_ctx_alloc(gtp_apn_t *apn, const char *apn_name)
379+
gtp_resolv_ctx_alloc(gtp_apn_t *apn)
375380
{
376381
gtp_resolv_ctx_t *ctx;
377382
struct sockaddr_storage *addr;
@@ -390,19 +395,11 @@ gtp_resolv_ctx_alloc(gtp_apn_t *apn, const char *apn_name)
390395
}
391396

392397
ctx->realm = (strlen(apn->realm)) ? apn->realm : daemon_data->realm;
393-
if (!strlen(ctx->realm)) {
394-
log_message(LOG_INFO, "%s(): No Realm configured... Ignoring..."
395-
, __FUNCTION__);
396-
FREE(ctx);
397-
return NULL;
398-
}
399398

400399
res_ninit(&ctx->ns_rs);
401400
ctx->ns_rs.nsaddr_list[0] = *((struct sockaddr_in *) addr);
402401
ctx->ns_rs.nscount = 1;
403402

404-
strlcpy(ctx->apn_ni, apn_name, GTP_APN_MAX_LEN);
405-
406403
return ctx;
407404
}
408405

@@ -448,6 +445,23 @@ gtp_pgw_show(vty_t *vty, list_head_t *l)
448445
return 0;
449446
}
450447

448+
static int
449+
gtp_pgw_dump(list_head_t *l)
450+
{
451+
gtp_pgw_t *pgw;
452+
453+
list_for_each_entry(pgw, l, next) {
454+
printf(" %s\t\t[%s]:%d\tPrio:%d Weight:%d\n",
455+
pgw->srv_name,
456+
inet_sockaddrtos(&pgw->addr),
457+
ntohs(inet_sockaddrport(&pgw->addr)),
458+
pgw->priority,
459+
pgw->weight);
460+
}
461+
462+
return 0;
463+
}
464+
451465
int
452466
gtp_naptr_show(vty_t *vty, gtp_apn_t *apn)
453467
{
@@ -470,6 +484,22 @@ gtp_naptr_show(vty_t *vty, gtp_apn_t *apn)
470484
return 0;
471485
}
472486

487+
int
488+
gtp_naptr_dump(list_head_t *l)
489+
{
490+
gtp_naptr_t *naptr;
491+
492+
list_for_each_entry(naptr, l, next) {
493+
printf("%s\t(%s, %s, Order:%d, Pref:%d)\n",
494+
naptr->server, (naptr->server_type == ns_t_srv) ? "SRV" : "A",
495+
naptr->service,
496+
naptr->order,
497+
naptr->preference);
498+
gtp_pgw_dump(&naptr->pgw);
499+
}
500+
501+
return 0;
502+
}
473503

474504
int
475505
gtp_naptr_destroy(list_head_t *l)

src/gtp_sched.c

+46-7
Original file line numberDiff line numberDiff line change
@@ -80,18 +80,18 @@ gtp_sched_pgw_wlc(gtp_naptr_t *naptr, struct sockaddr_in *addr_skip)
8080
}
8181

8282
static gtp_pgw_t *
83-
gtp_sched_naptr(gtp_apn_t *apn, const char *service, struct sockaddr_in *addr_skip)
83+
gtp_sched_naptr(list_head_t *l, const char *service, struct sockaddr_in *addr_skip)
8484
{
8585
gtp_naptr_t *naptr, *least = NULL;
8686
gtp_pgw_t *pgw = NULL;
8787

8888
/* First stage: Reset previous scheduling flags */
89-
list_for_each_entry(naptr, &apn->naptr, next)
89+
list_for_each_entry(naptr, l, next)
9090
naptr->fl = 0;
9191

9292
/* Second stage : Schedule by order until pgw election */
9393
shoot_again:
94-
list_for_each_entry(naptr, &apn->naptr, next) {
94+
list_for_each_entry(naptr, l, next) {
9595
if (!strstr(naptr->service, service) ||
9696
__test_bit(GTP_SCHEDULE_FL_SKIP, &naptr->fl))
9797
continue;
@@ -114,24 +114,63 @@ gtp_sched_naptr(gtp_apn_t *apn, const char *service, struct sockaddr_in *addr_sk
114114
return pgw;
115115
}
116116

117-
int
118-
gtp_sched(gtp_apn_t *apn, struct sockaddr_in *addr, struct sockaddr_in *addr_skip)
117+
static int
118+
gtp_sched_generic(gtp_apn_t *apn, list_head_t *l, struct sockaddr_in *addr, struct sockaddr_in *addr_skip)
119119
{
120120
gtp_service_t *service;
121121
gtp_pgw_t *pgw = NULL;
122122

123123
/* Service selection list is already sorted by prio */
124124
pthread_mutex_lock(&apn->mutex);
125125
list_for_each_entry(service, &apn->service_selection, next) {
126-
pgw = gtp_sched_naptr(apn, service->str, addr_skip);
126+
pgw = gtp_sched_naptr(l, service->str, addr_skip);
127127
if (pgw) {
128128
*addr = *(struct sockaddr_in *) &pgw->addr;
129129
pthread_mutex_unlock(&apn->mutex);
130130
return 0;
131131
}
132-
133132
}
134133
pthread_mutex_unlock(&apn->mutex);
135134

136135
return -1;
137136
}
137+
138+
int
139+
gtp_sched(gtp_apn_t *apn, struct sockaddr_in *addr, struct sockaddr_in *addr_skip)
140+
{
141+
return gtp_sched_generic(apn, &apn->naptr, addr, addr_skip);
142+
}
143+
144+
int
145+
gtp_sched_dynamic(gtp_apn_t *apn, const char *apn_name, const char *plmn, struct sockaddr_in *addr, struct sockaddr_in *addr_skip)
146+
{
147+
gtp_resolv_ctx_t *ctx;
148+
list_head_t l;
149+
int err = 0;
150+
151+
ctx = gtp_resolv_ctx_alloc(apn);
152+
if (!ctx)
153+
return -1;
154+
155+
INIT_LIST_HEAD(&l);
156+
err = gtp_resolv_naptr(ctx, &l, "%s.apn.epc.%s.3gppnetwork.org.", apn_name, plmn);
157+
if (err) {
158+
log_message(LOG_INFO, "%s(): Unable to resolv apn:'%s.apn.epc.%s.3gppnetwork.org.'"
159+
, __FUNCTION__, apn_name, plmn);
160+
goto end;
161+
}
162+
163+
err = gtp_resolv_pgw(ctx, &l);
164+
if (err) {
165+
log_message(LOG_INFO, "%s(): Unable resolv pgw for apn:'%s.apn.epc.%s.3gppnetwork.org.'"
166+
, __FUNCTION__, apn_name, plmn);
167+
goto end;
168+
}
169+
170+
err = gtp_sched_generic(apn, &l, addr, addr_skip);
171+
172+
end:
173+
gtp_resolv_ctx_destroy(ctx);
174+
gtp_naptr_destroy(&l);
175+
return err;
176+
}

src/gtp_switch_hdl_v2.c

+24-5
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,8 @@ gtpc_create_session_request_hdl(gtp_server_worker_t *w, struct sockaddr_storage
226226
uint64_t imsi;
227227
uint8_t *cp;
228228
char apn_str[64];
229-
int ret;
229+
char apn_plmn[64];
230+
int err;
230231

231232
/* Retransmission detection: Operating in a tranparent
232233
* way in order to preserve transitivity of messages, so
@@ -258,8 +259,8 @@ gtpc_create_session_request_hdl(gtp_server_worker_t *w, struct sockaddr_storage
258259

259260
ie_apn = (gtp_ie_apn_t *) cp;
260261
memset(apn_str, 0, 64);
261-
ret = gtp_ie_apn_extract_ni(ie_apn, apn_str, 63);
262-
if (ret < 0) {
262+
err = gtp_ie_apn_extract_ni(ie_apn, apn_str, 63);
263+
if (err) {
263264
log_message(LOG_INFO, "%s(): Error parsing Access-Point-Name IE. ignoring..."
264265
, __FUNCTION__);
265266
return NULL;
@@ -333,8 +334,26 @@ gtpc_create_session_request_hdl(gtp_server_worker_t *w, struct sockaddr_storage
333334
goto end;
334335
}
335336

336-
ret = gtp_sched(apn, &teid->pgw_addr, &teid->sgw_addr);
337-
if (ret < 0) {
337+
if (__test_bit(GTP_APN_FL_REALM_DYNAMIC, &apn->flags)) {
338+
err = gtp_ie_apn_extract_plmn(ie_apn, apn_plmn, 63);
339+
if (err)
340+
goto end;
341+
342+
err = gtp_sched_dynamic(apn, apn_str, apn_plmn, &teid->pgw_addr, &teid->sgw_addr);
343+
if (err) {
344+
log_message(LOG_INFO, "%s(): Unable to schedule pGW for apn:'%s.apn.epc.%s.3gppnetwork.org.'"
345+
, __FUNCTION__
346+
, apn_str, apn_plmn);
347+
gtp_teid_put(teid);
348+
gtp_session_destroy(s);
349+
teid = NULL;
350+
}
351+
352+
goto end;
353+
}
354+
355+
err = gtp_sched(apn, &teid->pgw_addr, &teid->sgw_addr);
356+
if (err) {
338357
log_message(LOG_INFO, "%s(): Unable to schedule pGW for apn:%s"
339358
, __FUNCTION__
340359
, apn->name);

src/include/gtp_resolv.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ typedef struct _gtp_service {
7171
} gtp_service_t;
7272

7373
typedef struct _gtp_resolv_ctx {
74-
char apn_ni[GTP_APN_MAX_LEN];
7574
char *realm;
7675
struct __res_state ns_rs;
7776
ns_msg msg;
@@ -85,10 +84,11 @@ typedef struct _gtp_resolv_ctx {
8584
/* Prototypes */
8685
extern int gtp_naptr_destroy(list_head_t *);
8786
extern int gtp_naptr_show(vty_t *vty, gtp_apn_t *);
87+
extern int gtp_naptr_dump(list_head_t *);
8888
extern gtp_naptr_t *gtp_naptr_get(gtp_apn_t *, const char *);
8989
extern int gtp_resolv_pgw(gtp_resolv_ctx_t *, list_head_t *);
90-
extern int gtp_resolv_naptr(gtp_resolv_ctx_t *, list_head_t *);
91-
extern gtp_resolv_ctx_t *gtp_resolv_ctx_alloc(gtp_apn_t *, const char *);
90+
extern int gtp_resolv_naptr(gtp_resolv_ctx_t *, list_head_t *, const char *, ...);
91+
extern gtp_resolv_ctx_t *gtp_resolv_ctx_alloc(gtp_apn_t *);
9292
extern int gtp_resolv_ctx_destroy(gtp_resolv_ctx_t *);
9393
extern int gtp_resolv_init(void);
9494
extern int gtp_resolv_destroy(void);

src/include/gtp_sched.h

+1
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,6 @@
2424

2525
/* Prototypes */
2626
extern int gtp_sched(gtp_apn_t *, struct sockaddr_in *, struct sockaddr_in *);
27+
extern int gtp_sched_dynamic(gtp_apn_t *, const char *, const char *, struct sockaddr_in *, struct sockaddr_in *);
2728

2829
#endif

0 commit comments

Comments
 (0)