blob: 0f180029d6661b2b9f9c4e05bf19926b8a208822 [file] [log] [blame]
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +01001#ifdef __linux__
2#define _GNU_SOURCE 1 /* strdup() prototype, broken arpa/inet.h */
3#endif
4
5#include "../config.h"
6
7#ifdef HAVE_STDINT_H
8#include <stdint.h>
9#endif
10
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +010011#include <stdio.h>
12#include <string.h>
13#include <stdlib.h>
Harald Welte51127ea2017-11-06 02:42:22 +090014#include <inttypes.h>
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +010015#include <sys/types.h>
16#include <arpa/inet.h>
17#include <net/if.h>
18
19#include <libgtpnl/gtp.h>
20#include <libgtpnl/gtpnl.h>
21#include <libmnl/libmnl.h>
22
23#include <errno.h>
24
25#include <time.h>
26
27#include "../lib/tun.h"
28#include "../lib/syserr.h"
29#include "../gtp/pdp.h"
30#include "../gtp/gtp.h"
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +010031
32#include <libgtpnl/gtp.h>
33#include <libgtpnl/gtpnl.h>
34#include <libmnl/libmnl.h>
35
36#include "gtp-kernel.h"
37
38static void pdp_debug(struct pdp_t *pdp)
39{
Harald Welte51127ea2017-11-06 02:42:22 +090040 struct in46_addr ia46;
41 struct in_addr ia;
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +010042
Harald Welte51127ea2017-11-06 02:42:22 +090043 in46a_from_eua(&pdp->eua, &ia46);
44 gsna2in_addr(&ia, &pdp->gsnrc);
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +010045
Harald Welte51127ea2017-11-06 02:42:22 +090046 LOGPDPX(DGGSN, LOGL_DEBUG, pdp, "v%u TEID %"PRIu64"x EUA=%s SGSN=%s\n", pdp->version,
47 pdp->version == 0 ? pdp_gettid(pdp->imsi, pdp->nsapi) : pdp->teid_gn,
48 in46a_ntoa(&ia46), inet_ntoa(ia));
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +010049}
50
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +010051static struct {
52 int genl_id;
53 struct mnl_socket *nl;
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +010054} gtp_nl;
55
Harald Welte22e15732017-11-08 16:07:12 +090056static int gtp_kernel_init_once(void)
57{
58 /* only initialize once */
59 if (gtp_nl.nl)
60 return 0;
61
62 gtp_nl.nl = genl_socket_open();
63 if (gtp_nl.nl == NULL) {
Harald Welte3dad9512017-11-08 16:53:47 +090064 LOGP(DGGSN, LOGL_ERROR, "cannot create genetlink socket\n");
Harald Welte22e15732017-11-08 16:07:12 +090065 return -1;
66 }
67 gtp_nl.genl_id = genl_lookup_family(gtp_nl.nl, "gtp");
68 if (gtp_nl.genl_id < 0) {
Harald Welte3dad9512017-11-08 16:53:47 +090069 LOGP(DGGSN, LOGL_ERROR, "cannot lookup GTP genetlink ID\n");
Harald Welte31879562017-11-08 16:08:20 +090070 genl_socket_close(gtp_nl.nl);
71 gtp_nl.nl = NULL;
Harald Welte22e15732017-11-08 16:07:12 +090072 return -1;
73 }
Harald Welte3dad9512017-11-08 16:53:47 +090074 LOGP(DGGSN, LOGL_NOTICE, "Initialized GTP kernel mode (genl ID is %d)\n", gtp_nl.genl_id);
Harald Welte22e15732017-11-08 16:07:12 +090075
76 return 0;
77}
78
Harald Welte698a2332017-11-08 15:09:58 +090079int gtp_kernel_init(struct gsn_t *gsn, const char *devname, struct in46_prefix *prefix, const char *ipup)
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +010080{
Harald Weltee3c59182017-11-08 14:08:24 +090081 struct in_addr net;
82 const char *net_arg;
83
Harald Welte22e15732017-11-08 16:07:12 +090084 if (!gtp_nl.nl)
85 gtp_kernel_init_once();
86
Harald Weltee3c59182017-11-08 14:08:24 +090087 if (prefix->addr.len != 4) {
Harald Welte3dad9512017-11-08 16:53:47 +090088 LOGP(DGGSN, LOGL_ERROR, "we only support IPv4 in this path :/");
Harald Weltee3c59182017-11-08 14:08:24 +090089 return -1;
90 }
91 net = prefix->addr.v4;
92
Harald Welte698a2332017-11-08 15:09:58 +090093 if (gtp_dev_create(-1, devname, gsn->fd0, gsn->fd1u) < 0) {
Harald Welte3dad9512017-11-08 16:53:47 +090094 LOGP(DGGSN, LOGL_ERROR, "cannot create GTP tunnel device: %s\n",
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +010095 strerror(errno));
96 return -1;
97 }
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +010098
Harald Weltee3c59182017-11-08 14:08:24 +090099 net_arg = in46p_ntoa(prefix);
100
Harald Welte698a2332017-11-08 15:09:58 +0900101 DEBUGP(DGGSN, "Setting route to reach %s via %s\n", net_arg, devname);
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100102
Harald Welte698a2332017-11-08 15:09:58 +0900103 if (gtp_dev_config(devname, &net, prefix->prefixlen) < 0) {
Harald Welte3dad9512017-11-08 16:53:47 +0900104 LOGP(DGGSN, LOGL_ERROR, "Cannot add route to reach network %s\n", net_arg);
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100105 }
106
107 /* launch script if it is set to bring up the route to reach
108 * the MS, eg. ip ro add 10.0.0.0/8 dev gtp0. Better add this
109 * using native rtnetlink interface given that we know the
110 * MS network mask, later.
111 */
112 if (ipup) {
113 char cmd[1024];
114 int err;
115
116 /* eg. /home/ggsn/ipup gtp0 10.0.0.0/8 */
Harald Welte698a2332017-11-08 15:09:58 +0900117 snprintf(cmd, sizeof(cmd), "%s %s %s", ipup, devname, net_arg);
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100118 cmd[sizeof(cmd)-1] = '\0';
119
120 err = system(cmd);
121 if (err < 0) {
Harald Welte3dad9512017-11-08 16:53:47 +0900122 LOGP(DGGSN, LOGL_ERROR, "Failed to launch script `%s'\n", ipup);
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100123 return -1;
124 }
125 }
Harald Welte3dad9512017-11-08 16:53:47 +0900126 LOGP(DGGSN, LOGL_NOTICE, "GTP kernel configured\n");
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100127
128 return 0;
129}
130
Harald Welte698a2332017-11-08 15:09:58 +0900131void gtp_kernel_stop(const char *devname)
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100132{
Harald Welte698a2332017-11-08 15:09:58 +0900133 gtp_dev_destroy(devname);
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100134}
135
Harald Welte698a2332017-11-08 15:09:58 +0900136int gtp_kernel_tunnel_add(struct pdp_t *pdp, const char *devname)
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100137{
138 struct in_addr ms, sgsn;
139 struct gtp_tunnel *t;
140 int ret;
141
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100142 pdp_debug(pdp);
143
144 t = gtp_tunnel_alloc();
145 if (t == NULL)
146 return -1;
147
148 memcpy(&ms, &pdp->eua.v[2], sizeof(struct in_addr));
149 memcpy(&sgsn, &pdp->gsnrc.v[0], sizeof(struct in_addr));
150
Harald Welte698a2332017-11-08 15:09:58 +0900151 gtp_tunnel_set_ifidx(t, if_nametoindex(devname));
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100152 gtp_tunnel_set_version(t, pdp->version);
153 gtp_tunnel_set_ms_ip4(t, &ms);
154 gtp_tunnel_set_sgsn_ip4(t, &sgsn);
155 if (pdp->version == 0) {
156 gtp_tunnel_set_tid(t, pdp_gettid(pdp->imsi, pdp->nsapi));
157 gtp_tunnel_set_flowid(t, pdp->flru);
158 } else {
Harald Welte875e4dc2017-02-23 20:26:19 +0100159 gtp_tunnel_set_i_tei(t, pdp->teid_own);
160 /* use the TEI advertised by SGSN when sending packets
161 * towards the SGSN */
162 gtp_tunnel_set_o_tei(t, pdp->teid_gn);
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100163 }
164
165 ret = gtp_add_tunnel(gtp_nl.genl_id, gtp_nl.nl, t);
166 gtp_tunnel_free(t);
167
168 return ret;
169}
170
Harald Welte698a2332017-11-08 15:09:58 +0900171int gtp_kernel_tunnel_del(struct pdp_t *pdp, const char *devname)
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100172{
173 struct gtp_tunnel *t;
174 int ret;
175
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100176 pdp_debug(pdp);
177
178 t = gtp_tunnel_alloc();
179 if (t == NULL)
180 return -1;
181
Harald Welte698a2332017-11-08 15:09:58 +0900182 gtp_tunnel_set_ifidx(t, if_nametoindex(devname));
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100183 gtp_tunnel_set_version(t, pdp->version);
184 if (pdp->version == 0) {
185 gtp_tunnel_set_tid(t, pdp_gettid(pdp->imsi, pdp->nsapi));
186 gtp_tunnel_set_flowid(t, pdp->flru);
187 } else {
Harald Welte875e4dc2017-02-23 20:26:19 +0100188 gtp_tunnel_set_i_tei(t, pdp->teid_own);
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100189 }
190
191 ret = gtp_del_tunnel(gtp_nl.genl_id, gtp_nl.nl, t);
192 gtp_tunnel_free(t);
193
194 return ret;
195}