blob: f98586d2a43c4389595ad0798f66f3ce89f37b24 [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>
14#include <sys/types.h>
15#include <arpa/inet.h>
16#include <net/if.h>
17
18#include <libgtpnl/gtp.h>
19#include <libgtpnl/gtpnl.h>
20#include <libmnl/libmnl.h>
21
22#include <errno.h>
23
24#include <time.h>
25
26#include "../lib/tun.h"
27#include "../lib/syserr.h"
28#include "../gtp/pdp.h"
29#include "../gtp/gtp.h"
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +010030
31#include <libgtpnl/gtp.h>
32#include <libgtpnl/gtpnl.h>
33#include <libmnl/libmnl.h>
34
35#include "gtp-kernel.h"
36
37static void pdp_debug(struct pdp_t *pdp)
38{
39 int i;
40 uint64_t teid;
41
42 if (!debug)
43 return;
44
45 printf("version %u\n", pdp->version);
46 if (pdp->version == 0) {
47 teid = pdp_gettid(pdp->imsi, pdp->nsapi);
48 printf("flowid %u\n", pdp->flru);
49 } else {
50 teid = pdp->teid_gn; /* GTPIE_TEI_DI */
51 }
52
53 printf("teid %llx\n", teid);
54 printf("address (%u)\n", pdp->eua.l);
55
56 /* Byte 0: 0xf1 == IETF */
57 /* Byte 1: 0x21 == IPv4 */
58 /* Byte 2-6: IPv4 address */
59
60 for (i = 0; i < 6; i++)
61 printf("%x ", pdp->eua.v[i] & 0xff); /* GTPIE_EUA */
62
63 printf("\n");
64 printf("sgsn-addr (%u)\n", pdp->gsnrc.l);
65
66 for (i = 0; i < 4; i++)
67 printf("%x ", pdp->gsnrc.v[i] & 0xff); /* GTPIE_GSN_ADDR */
68
69 printf("\n");
70}
71
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +010072static struct {
73 int genl_id;
74 struct mnl_socket *nl;
75 bool enabled;
76} gtp_nl;
77
78/* Always forces the kernel to allocate gtp0. If it exists it hits EEXIST */
79#define GTP_DEVNAME "gtp0"
80
81int gtp_kernel_init(struct gsn_t *gsn, struct in_addr *net,
Harald Welte8ffd7fc2017-08-12 14:52:15 +020082 size_t prefixlen, const char *net_arg)
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +010083{
Pablo Neira Ayuso7b319872016-05-10 18:38:30 +020084 if (gtp_dev_create(-1, GTP_DEVNAME, gsn->fd0, gsn->fd1u) < 0) {
Andreas Schultzc5fbf9b2015-11-17 12:22:43 +010085 SYS_ERR(DGGSN, LOGL_ERROR, 0,
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +010086 "cannot create GTP tunnel device: %s\n",
87 strerror(errno));
88 return -1;
89 }
90 gtp_nl.enabled = true;
91
92 gtp_nl.nl = genl_socket_open();
93 if (gtp_nl.nl == NULL) {
Andreas Schultzc5fbf9b2015-11-17 12:22:43 +010094 SYS_ERR(DGGSN, LOGL_ERROR, 0,
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +010095 "cannot create genetlink socket\n");
96 return -1;
97 }
98 gtp_nl.genl_id = genl_lookup_family(gtp_nl.nl, "gtp");
99 if (gtp_nl.genl_id < 0) {
Andreas Schultzc5fbf9b2015-11-17 12:22:43 +0100100 SYS_ERR(DGGSN, LOGL_ERROR, 0,
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100101 "cannot lookup GTP genetlink ID\n");
102 return -1;
103 }
104 if (debug) {
Andreas Schultzc5fbf9b2015-11-17 12:22:43 +0100105 SYS_ERR(DGGSN, LOGL_NOTICE, 0,
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100106 "Using the GTP kernel mode (genl ID is %d)\n",
107 gtp_nl.genl_id);
108 }
109
Andreas Schultzc5fbf9b2015-11-17 12:22:43 +0100110 DEBUGP(DGGSN, "Setting route to reach %s via %s\n",
Harald Welte8ffd7fc2017-08-12 14:52:15 +0200111 net_arg, GTP_DEVNAME);
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100112
Harald Welted12eab92017-08-02 19:49:47 +0200113 if (gtp_dev_config(GTP_DEVNAME, net, prefixlen) < 0) {
Andreas Schultzc5fbf9b2015-11-17 12:22:43 +0100114 SYS_ERR(DGGSN, LOGL_ERROR, 0,
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100115 "Cannot add route to reach network %s\n",
Harald Welte8ffd7fc2017-08-12 14:52:15 +0200116 net_arg);
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100117 }
118
119 /* launch script if it is set to bring up the route to reach
120 * the MS, eg. ip ro add 10.0.0.0/8 dev gtp0. Better add this
121 * using native rtnetlink interface given that we know the
122 * MS network mask, later.
123 */
124 if (ipup) {
125 char cmd[1024];
126 int err;
127
128 /* eg. /home/ggsn/ipup gtp0 10.0.0.0/8 */
129 snprintf(cmd, sizeof(cmd), "%s %s %s",
Harald Welte8ffd7fc2017-08-12 14:52:15 +0200130 ipup, GTP_DEVNAME, net_arg);
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100131 cmd[sizeof(cmd)-1] = '\0';
132
133 err = system(cmd);
134 if (err < 0) {
Andreas Schultzc5fbf9b2015-11-17 12:22:43 +0100135 SYS_ERR(DGGSN, LOGL_ERROR, 0,
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100136 "Failed to launch script `%s'", ipup);
137 return -1;
138 }
139 }
Andreas Schultzc5fbf9b2015-11-17 12:22:43 +0100140 SYS_ERR(DGGSN, LOGL_NOTICE, 0, "GTP kernel configured\n");
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100141
142 return 0;
143}
144
145void gtp_kernel_stop(void)
146{
147 if (!gtp_nl.enabled)
148 return;
149
150 gtp_dev_destroy(GTP_DEVNAME);
151}
152
153int gtp_kernel_tunnel_add(struct pdp_t *pdp)
154{
155 struct in_addr ms, sgsn;
156 struct gtp_tunnel *t;
157 int ret;
158
159 if (!gtp_nl.enabled)
160 return 0;
161
162 pdp_debug(pdp);
163
164 t = gtp_tunnel_alloc();
165 if (t == NULL)
166 return -1;
167
168 memcpy(&ms, &pdp->eua.v[2], sizeof(struct in_addr));
169 memcpy(&sgsn, &pdp->gsnrc.v[0], sizeof(struct in_addr));
170
171 gtp_tunnel_set_ifidx(t, if_nametoindex(GTP_DEVNAME));
172 gtp_tunnel_set_version(t, pdp->version);
173 gtp_tunnel_set_ms_ip4(t, &ms);
174 gtp_tunnel_set_sgsn_ip4(t, &sgsn);
175 if (pdp->version == 0) {
176 gtp_tunnel_set_tid(t, pdp_gettid(pdp->imsi, pdp->nsapi));
177 gtp_tunnel_set_flowid(t, pdp->flru);
178 } else {
Harald Welte875e4dc2017-02-23 20:26:19 +0100179 gtp_tunnel_set_i_tei(t, pdp->teid_own);
180 /* use the TEI advertised by SGSN when sending packets
181 * towards the SGSN */
182 gtp_tunnel_set_o_tei(t, pdp->teid_gn);
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100183 }
184
185 ret = gtp_add_tunnel(gtp_nl.genl_id, gtp_nl.nl, t);
186 gtp_tunnel_free(t);
187
188 return ret;
189}
190
191int gtp_kernel_tunnel_del(struct pdp_t *pdp)
192{
193 struct gtp_tunnel *t;
194 int ret;
195
196 if (!gtp_nl.enabled)
197 return 0;
198
199 pdp_debug(pdp);
200
201 t = gtp_tunnel_alloc();
202 if (t == NULL)
203 return -1;
204
205 gtp_tunnel_set_ifidx(t, if_nametoindex(GTP_DEVNAME));
206 gtp_tunnel_set_version(t, pdp->version);
207 if (pdp->version == 0) {
208 gtp_tunnel_set_tid(t, pdp_gettid(pdp->imsi, pdp->nsapi));
209 gtp_tunnel_set_flowid(t, pdp->flru);
210 } else {
Harald Welte875e4dc2017-02-23 20:26:19 +0100211 gtp_tunnel_set_i_tei(t, pdp->teid_own);
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100212 }
213
214 ret = gtp_del_tunnel(gtp_nl.genl_id, gtp_nl.nl, t);
215 gtp_tunnel_free(t);
216
217 return ret;
218}
219
220int gtp_kernel_enabled(void)
221{
222 return gtp_nl.enabled;
223}