9
0
Fork 0
This repository has been archived on 2022-06-17. You can view files and clone it, but cannot push or open issues or pull requests.
openggsn/ggsn/gtp-kernel.c

224 lines
4.6 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 <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(struct pdp_t *pdp)
{
int i;
uint64_t teid;
if (!debug)
return;
printf("version %u\n", pdp->version);
if (pdp->version == 0) {
teid = pdp_gettid(pdp->imsi, pdp->nsapi);
printf("flowid %u\n", pdp->flru);
} else {
teid = pdp->teid_gn; /* GTPIE_TEI_DI */
}
printf("teid %llx\n", teid);
printf("address (%u)\n", pdp->eua.l);
/* Byte 0: 0xf1 == IETF */
/* Byte 1: 0x21 == IPv4 */
/* Byte 2-6: IPv4 address */
for (i = 0; i < 6; i++)
printf("%x ", pdp->eua.v[i] & 0xff); /* GTPIE_EUA */
printf("\n");
printf("sgsn-addr (%u)\n", pdp->gsnrc.l);
for (i = 0; i < 4; i++)
printf("%x ", pdp->gsnrc.v[i] & 0xff); /* GTPIE_GSN_ADDR */
printf("\n");
}
static struct {
int genl_id;
struct mnl_socket *nl;
bool enabled;
} gtp_nl;
/* Always forces the kernel to allocate gtp0. If it exists it hits EEXIST */
#define GTP_DEVNAME "gtp0"
int gtp_kernel_init(struct gsn_t *gsn, struct in_addr *net,
size_t prefixlen, const char *net_arg)
{
if (gtp_dev_create(-1, GTP_DEVNAME, gsn->fd0, gsn->fd1u) < 0) {
SYS_ERR(DGGSN, LOGL_ERROR, 0,
"cannot create GTP tunnel device: %s\n",
strerror(errno));
return -1;
}
gtp_nl.enabled = true;
gtp_nl.nl = genl_socket_open();
if (gtp_nl.nl == NULL) {
SYS_ERR(DGGSN, LOGL_ERROR, 0,
"cannot create genetlink socket\n");
return -1;
}
gtp_nl.genl_id = genl_lookup_family(gtp_nl.nl, "gtp");
if (gtp_nl.genl_id < 0) {
SYS_ERR(DGGSN, LOGL_ERROR, 0,
"cannot lookup GTP genetlink ID\n");
return -1;
}
if (debug) {
SYS_ERR(DGGSN, LOGL_NOTICE, 0,
"Using the GTP kernel mode (genl ID is %d)\n",
gtp_nl.genl_id);
}
DEBUGP(DGGSN, "Setting route to reach %s via %s\n",
net_arg, GTP_DEVNAME);
if (gtp_dev_config(GTP_DEVNAME, net, prefixlen) < 0) {
SYS_ERR(DGGSN, LOGL_ERROR, 0,
"Cannot add route to reach network %s\n",
net_arg);
}
/* launch script if it is set to bring up the route to reach
* the MS, eg. ip ro add 10.0.0.0/8 dev gtp0. Better add this
* using native rtnetlink interface given that we know the
* MS network mask, later.
*/
if (ipup) {
char cmd[1024];
int err;
/* eg. /home/ggsn/ipup gtp0 10.0.0.0/8 */
snprintf(cmd, sizeof(cmd), "%s %s %s",
ipup, GTP_DEVNAME, net_arg);
cmd[sizeof(cmd)-1] = '\0';
err = system(cmd);
if (err < 0) {
SYS_ERR(DGGSN, LOGL_ERROR, 0,
"Failed to launch script `%s'", ipup);
return -1;
}
}
SYS_ERR(DGGSN, LOGL_NOTICE, 0, "GTP kernel configured\n");
return 0;
}
void gtp_kernel_stop(void)
{
if (!gtp_nl.enabled)
return;
gtp_dev_destroy(GTP_DEVNAME);
}
int gtp_kernel_tunnel_add(struct pdp_t *pdp)
{
struct in_addr ms, sgsn;
struct gtp_tunnel *t;
int ret;
if (!gtp_nl.enabled)
return 0;
pdp_debug(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(GTP_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)
{
struct gtp_tunnel *t;
int ret;
if (!gtp_nl.enabled)
return 0;
pdp_debug(pdp);
t = gtp_tunnel_alloc();
if (t == NULL)
return -1;
gtp_tunnel_set_ifidx(t, if_nametoindex(GTP_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;
}
int gtp_kernel_enabled(void)
{
return gtp_nl.enabled;
}