#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 #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) { struct in46_addr ia46; struct in_addr ia; in46a_from_eua(&pdp->eua, &ia46); gsna2in_addr(&ia, &pdp->gsnrc); LOGPDPX(DGGSN, LOGL_DEBUG, pdp, "v%u TEID %"PRIu64"x EUA=%s SGSN=%s\n", 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; int gtp_kernel_init(struct gsn_t *gsn, const char *devname, struct in46_prefix *prefix, const char *ipup) { struct in_addr net; const char *net_arg; if (prefix->addr.len != 4) { SYS_ERR(DGGSN, LOGL_ERROR, 0, "we only support IPv4 in this path :/"); return -1; } net = prefix->addr.v4; if (gtp_dev_create(-1, 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.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; } SYS_ERR(DGGSN, LOGL_DEBUG, 0, "Using the GTP kernel mode (genl ID is %d)\n", gtp_nl.genl_id); net_arg = in46p_ntoa(prefix); DEBUGP(DGGSN, "Setting route to reach %s via %s\n", net_arg, devname); if (gtp_dev_config(devname, &net, prefix->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, 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(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(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(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; }