#ifdef __linux__ #define _GNU_SOURCE 1 /* strdup() prototype, broken arpa/inet.h */ #endif #include "../config.h" #ifdef HAVE_STDINT_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include "../lib/tun.h" #include "../lib/syserr.h" #include "../gtp/pdp.h" #include "../gtp/gtp.h" #include #include #include #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; }