| #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; |
| } |