blob: 916b92f49b0b894c449d31bccb8f0f6e940ec932 [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;
54 bool enabled;
55} gtp_nl;
56
57/* Always forces the kernel to allocate gtp0. If it exists it hits EEXIST */
58#define GTP_DEVNAME "gtp0"
59
60int gtp_kernel_init(struct gsn_t *gsn, struct in_addr *net,
Harald Welte8ffd7fc2017-08-12 14:52:15 +020061 size_t prefixlen, const char *net_arg)
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +010062{
Pablo Neira Ayuso7b319872016-05-10 18:38:30 +020063 if (gtp_dev_create(-1, GTP_DEVNAME, gsn->fd0, gsn->fd1u) < 0) {
Andreas Schultzc5fbf9b2015-11-17 12:22:43 +010064 SYS_ERR(DGGSN, LOGL_ERROR, 0,
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +010065 "cannot create GTP tunnel device: %s\n",
66 strerror(errno));
67 return -1;
68 }
69 gtp_nl.enabled = true;
70
71 gtp_nl.nl = genl_socket_open();
72 if (gtp_nl.nl == NULL) {
Andreas Schultzc5fbf9b2015-11-17 12:22:43 +010073 SYS_ERR(DGGSN, LOGL_ERROR, 0,
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +010074 "cannot create genetlink socket\n");
75 return -1;
76 }
77 gtp_nl.genl_id = genl_lookup_family(gtp_nl.nl, "gtp");
78 if (gtp_nl.genl_id < 0) {
Andreas Schultzc5fbf9b2015-11-17 12:22:43 +010079 SYS_ERR(DGGSN, LOGL_ERROR, 0,
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +010080 "cannot lookup GTP genetlink ID\n");
81 return -1;
82 }
Harald Welte51127ea2017-11-06 02:42:22 +090083 SYS_ERR(DGGSN, LOGL_DEBUG, 0,
84 "Using the GTP kernel mode (genl ID is %d)\n", gtp_nl.genl_id);
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +010085
Andreas Schultzc5fbf9b2015-11-17 12:22:43 +010086 DEBUGP(DGGSN, "Setting route to reach %s via %s\n",
Harald Welte8ffd7fc2017-08-12 14:52:15 +020087 net_arg, GTP_DEVNAME);
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +010088
Harald Welted12eab92017-08-02 19:49:47 +020089 if (gtp_dev_config(GTP_DEVNAME, net, prefixlen) < 0) {
Andreas Schultzc5fbf9b2015-11-17 12:22:43 +010090 SYS_ERR(DGGSN, LOGL_ERROR, 0,
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +010091 "Cannot add route to reach network %s\n",
Harald Welte8ffd7fc2017-08-12 14:52:15 +020092 net_arg);
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +010093 }
94
95 /* launch script if it is set to bring up the route to reach
96 * the MS, eg. ip ro add 10.0.0.0/8 dev gtp0. Better add this
97 * using native rtnetlink interface given that we know the
98 * MS network mask, later.
99 */
100 if (ipup) {
101 char cmd[1024];
102 int err;
103
104 /* eg. /home/ggsn/ipup gtp0 10.0.0.0/8 */
105 snprintf(cmd, sizeof(cmd), "%s %s %s",
Harald Welte8ffd7fc2017-08-12 14:52:15 +0200106 ipup, GTP_DEVNAME, net_arg);
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100107 cmd[sizeof(cmd)-1] = '\0';
108
109 err = system(cmd);
110 if (err < 0) {
Andreas Schultzc5fbf9b2015-11-17 12:22:43 +0100111 SYS_ERR(DGGSN, LOGL_ERROR, 0,
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100112 "Failed to launch script `%s'", ipup);
113 return -1;
114 }
115 }
Andreas Schultzc5fbf9b2015-11-17 12:22:43 +0100116 SYS_ERR(DGGSN, LOGL_NOTICE, 0, "GTP kernel configured\n");
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100117
118 return 0;
119}
120
121void gtp_kernel_stop(void)
122{
123 if (!gtp_nl.enabled)
124 return;
125
126 gtp_dev_destroy(GTP_DEVNAME);
127}
128
129int gtp_kernel_tunnel_add(struct pdp_t *pdp)
130{
131 struct in_addr ms, sgsn;
132 struct gtp_tunnel *t;
133 int ret;
134
135 if (!gtp_nl.enabled)
136 return 0;
137
138 pdp_debug(pdp);
139
140 t = gtp_tunnel_alloc();
141 if (t == NULL)
142 return -1;
143
144 memcpy(&ms, &pdp->eua.v[2], sizeof(struct in_addr));
145 memcpy(&sgsn, &pdp->gsnrc.v[0], sizeof(struct in_addr));
146
147 gtp_tunnel_set_ifidx(t, if_nametoindex(GTP_DEVNAME));
148 gtp_tunnel_set_version(t, pdp->version);
149 gtp_tunnel_set_ms_ip4(t, &ms);
150 gtp_tunnel_set_sgsn_ip4(t, &sgsn);
151 if (pdp->version == 0) {
152 gtp_tunnel_set_tid(t, pdp_gettid(pdp->imsi, pdp->nsapi));
153 gtp_tunnel_set_flowid(t, pdp->flru);
154 } else {
Harald Welte875e4dc2017-02-23 20:26:19 +0100155 gtp_tunnel_set_i_tei(t, pdp->teid_own);
156 /* use the TEI advertised by SGSN when sending packets
157 * towards the SGSN */
158 gtp_tunnel_set_o_tei(t, pdp->teid_gn);
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100159 }
160
161 ret = gtp_add_tunnel(gtp_nl.genl_id, gtp_nl.nl, t);
162 gtp_tunnel_free(t);
163
164 return ret;
165}
166
167int gtp_kernel_tunnel_del(struct pdp_t *pdp)
168{
169 struct gtp_tunnel *t;
170 int ret;
171
172 if (!gtp_nl.enabled)
173 return 0;
174
175 pdp_debug(pdp);
176
177 t = gtp_tunnel_alloc();
178 if (t == NULL)
179 return -1;
180
181 gtp_tunnel_set_ifidx(t, if_nametoindex(GTP_DEVNAME));
182 gtp_tunnel_set_version(t, pdp->version);
183 if (pdp->version == 0) {
184 gtp_tunnel_set_tid(t, pdp_gettid(pdp->imsi, pdp->nsapi));
185 gtp_tunnel_set_flowid(t, pdp->flru);
186 } else {
Harald Welte875e4dc2017-02-23 20:26:19 +0100187 gtp_tunnel_set_i_tei(t, pdp->teid_own);
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100188 }
189
190 ret = gtp_del_tunnel(gtp_nl.genl_id, gtp_nl.nl, t);
191 gtp_tunnel_free(t);
192
193 return ret;
194}
195
196int gtp_kernel_enabled(void)
197{
198 return gtp_nl.enabled;
199}