165 lines
3.7 KiB
C
165 lines
3.7 KiB
C
#ifdef __linux__
|
|
#define _GNU_SOURCE 1 /* strdup() prototype, broken arpa/inet.h */
|
|
#endif
|
|
|
|
#include "../config.h"
|
|
|
|
#ifdef HAVE_STDINT_H
|
|
#include <stdint.h>
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <inttypes.h>
|
|
#include <sys/types.h>
|
|
#include <arpa/inet.h>
|
|
#include <net/if.h>
|
|
|
|
#include <libgtpnl/gtp.h>
|
|
#include <libgtpnl/gtpnl.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include "../lib/tun.h"
|
|
#include "../lib/syserr.h"
|
|
#include "../lib/util.h"
|
|
#include "../lib/ippool.h"
|
|
#include "../gtp/pdp.h"
|
|
#include "../gtp/gtp.h"
|
|
|
|
#include "gtp-kernel.h"
|
|
|
|
static void pdp_debug(const char *prefix, const char *devname, struct pdp_t *pdp)
|
|
{
|
|
char buf4[INET_ADDRSTRLEN], buf6[INET6_ADDRSTRLEN];
|
|
struct ippoolm_t *peer;
|
|
struct in_addr ia;
|
|
|
|
buf4[0] = '\0';
|
|
if ((peer = pdp_get_peer_ipv(pdp, false)))
|
|
in46a_ntop(&peer->addr, buf4, sizeof(buf4));
|
|
buf6[0] = '\0';
|
|
if ((peer = pdp_get_peer_ipv(pdp, true)))
|
|
in46a_ntop(&peer->addr, buf6, sizeof(buf6));
|
|
|
|
gsna2in_addr(&ia, &pdp->gsnrc);
|
|
|
|
LOGPDPX(DGGSN, LOGL_DEBUG, pdp, "%s %s v%u TEID %"PRIx64" EUA=(%s,%s) SGSN=%s\n", prefix,
|
|
devname, pdp->version,
|
|
pdp->version == 0 ? pdp_gettid(pdp->imsi, pdp->nsapi) : pdp->teid_gn,
|
|
buf4, buf6, inet_ntoa(ia));
|
|
}
|
|
|
|
static struct {
|
|
int genl_id;
|
|
struct mnl_socket *nl;
|
|
} gtp_nl;
|
|
|
|
static int gtp_kernel_init_once(void)
|
|
{
|
|
/* only initialize once */
|
|
if (gtp_nl.nl)
|
|
return 0;
|
|
|
|
gtp_nl.nl = genl_socket_open();
|
|
if (gtp_nl.nl == NULL) {
|
|
LOGP(DGGSN, LOGL_ERROR, "cannot create genetlink socket\n");
|
|
return -1;
|
|
}
|
|
gtp_nl.genl_id = genl_lookup_family(gtp_nl.nl, "gtp");
|
|
if (gtp_nl.genl_id < 0) {
|
|
LOGP(DGGSN, LOGL_ERROR, "cannot lookup GTP genetlink ID\n");
|
|
genl_socket_close(gtp_nl.nl);
|
|
gtp_nl.nl = NULL;
|
|
return -1;
|
|
}
|
|
LOGP(DGGSN, LOGL_NOTICE, "Initialized GTP kernel mode (genl ID is %d)\n", gtp_nl.genl_id);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int gtp_kernel_create(int dest_ns, const char *devname, int fd0, int fd1u)
|
|
{
|
|
if (gtp_kernel_init_once() < 0)
|
|
return -1;
|
|
|
|
return gtp_dev_create(dest_ns, devname, fd0, fd1u);
|
|
}
|
|
|
|
int gtp_kernel_create_sgsn(int dest_ns, const char *devname, int fd0, int fd1u)
|
|
{
|
|
if (gtp_kernel_init_once() < 0)
|
|
return -1;
|
|
|
|
return gtp_dev_create_sgsn(dest_ns, devname, fd0, fd1u);
|
|
}
|
|
|
|
void gtp_kernel_stop(const char *devname)
|
|
{
|
|
gtp_dev_destroy(devname);
|
|
}
|
|
|
|
int gtp_kernel_tunnel_add(struct pdp_t *pdp, const char *devname)
|
|
{
|
|
struct in_addr ms, sgsn;
|
|
struct gtp_tunnel *t;
|
|
int ret;
|
|
|
|
pdp_debug(__func__, devname, pdp);
|
|
|
|
t = gtp_tunnel_alloc();
|
|
if (t == NULL)
|
|
return -1;
|
|
|
|
memcpy(&ms, &pdp->eua.v[2], sizeof(struct in_addr));
|
|
memcpy(&sgsn, &pdp->gsnrc.v[0], sizeof(struct in_addr));
|
|
|
|
gtp_tunnel_set_ifidx(t, if_nametoindex(devname));
|
|
gtp_tunnel_set_version(t, pdp->version);
|
|
gtp_tunnel_set_ms_ip4(t, &ms);
|
|
gtp_tunnel_set_sgsn_ip4(t, &sgsn);
|
|
if (pdp->version == 0) {
|
|
gtp_tunnel_set_tid(t, pdp_gettid(pdp->imsi, pdp->nsapi));
|
|
gtp_tunnel_set_flowid(t, pdp->flru);
|
|
} else {
|
|
gtp_tunnel_set_i_tei(t, pdp->teid_own);
|
|
/* use the TEI advertised by SGSN when sending packets
|
|
* towards the SGSN */
|
|
gtp_tunnel_set_o_tei(t, pdp->teid_gn);
|
|
}
|
|
|
|
ret = gtp_add_tunnel(gtp_nl.genl_id, gtp_nl.nl, t);
|
|
gtp_tunnel_free(t);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int gtp_kernel_tunnel_del(struct pdp_t *pdp, const char *devname)
|
|
{
|
|
struct gtp_tunnel *t;
|
|
int ret;
|
|
|
|
pdp_debug(__func__, devname, pdp);
|
|
|
|
t = gtp_tunnel_alloc();
|
|
if (t == NULL)
|
|
return -1;
|
|
|
|
gtp_tunnel_set_ifidx(t, if_nametoindex(devname));
|
|
gtp_tunnel_set_version(t, pdp->version);
|
|
if (pdp->version == 0) {
|
|
gtp_tunnel_set_tid(t, pdp_gettid(pdp->imsi, pdp->nsapi));
|
|
gtp_tunnel_set_flowid(t, pdp->flru);
|
|
} else {
|
|
gtp_tunnel_set_i_tei(t, pdp->teid_own);
|
|
}
|
|
|
|
ret = gtp_del_tunnel(gtp_nl.genl_id, gtp_nl.nl, t);
|
|
gtp_tunnel_free(t);
|
|
|
|
return ret;
|
|
}
|