aboutsummaryrefslogtreecommitdiffstats
path: root/lib/gtp-kernel.c
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2018-04-25 19:02:31 +0200
committerHarald Welte <laforge@gnumonks.org>2018-04-25 21:44:46 +0200
commitf2286395e9dd5c1a9d2c57bafbe68c1e3a1b273f (patch)
tree409f91f428ca9a17d89208f37f7c8b730c227393 /lib/gtp-kernel.c
parent9eebe15cd1309011d43a3490de7fbc973966f120 (diff)
Move kernel GTP support from ggsn/ to lib/
This way, the IP address / route handling between TUN devices and kernel GTP can be shared, which will provide not only a unified codebase but also a more consistent behavior. This also paves the road for to use kernel GTP from sgsnemu in the future. Related: OS#3214 Change-Id: Ic53a971136edd0d8871fbd6746d7b0090ce3a188
Diffstat (limited to 'lib/gtp-kernel.c')
-rw-r--r--lib/gtp-kernel.c160
1 files changed, 160 insertions, 0 deletions
diff --git a/lib/gtp-kernel.c b/lib/gtp-kernel.c
new file mode 100644
index 0000000..48811bc
--- /dev/null
+++ b/lib/gtp-kernel.c
@@ -0,0 +1,160 @@
+#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 <libmnl/libmnl.h>
+
+#include <errno.h>
+
+#include <time.h>
+
+#include "../lib/tun.h"
+#include "../lib/syserr.h"
+#include "../gtp/pdp.h"
+#include "../gtp/gtp.h"
+
+#include <libgtpnl/gtp.h>
+#include <libgtpnl/gtpnl.h>
+#include <libmnl/libmnl.h>
+
+#include "gtp-kernel.h"
+
+static void pdp_debug(const char *prefix, const char *devname, struct pdp_t *pdp)
+{
+ struct in46_addr ia46;
+ struct in_addr ia;
+
+ in46a_from_eua(&pdp->eua, &ia46);
+ gsna2in_addr(&ia, &pdp->gsnrc);
+
+ LOGPDPX(DGGSN, LOGL_DEBUG, pdp, "%s %s v%u TEID %"PRIx64" EUA=%s SGSN=%s\n", prefix,
+ devname, pdp->version,
+ pdp->version == 0 ? pdp_gettid(pdp->imsi, pdp->nsapi) : pdp->teid_gn,
+ in46a_ntoa(&ia46), 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;
+}