blob: 458ac2744df254bde865bbd247546104cf008d51 [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"
30#include "cmdline.h"
31
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{
40 int i;
41 uint64_t teid;
42
43 if (!debug)
44 return;
45
46 printf("version %u\n", pdp->version);
47 if (pdp->version == 0) {
48 teid = pdp_gettid(pdp->imsi, pdp->nsapi);
49 printf("flowid %u\n", pdp->flru);
50 } else {
51 teid = pdp->teid_gn; /* GTPIE_TEI_DI */
52 }
53
54 printf("teid %llx\n", teid);
55 printf("address (%u)\n", pdp->eua.l);
56
57 /* Byte 0: 0xf1 == IETF */
58 /* Byte 1: 0x21 == IPv4 */
59 /* Byte 2-6: IPv4 address */
60
61 for (i = 0; i < 6; i++)
62 printf("%x ", pdp->eua.v[i] & 0xff); /* GTPIE_EUA */
63
64 printf("\n");
65 printf("sgsn-addr (%u)\n", pdp->gsnrc.l);
66
67 for (i = 0; i < 4; i++)
68 printf("%x ", pdp->gsnrc.v[i] & 0xff); /* GTPIE_GSN_ADDR */
69
70 printf("\n");
71}
72
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +010073static struct {
74 int genl_id;
75 struct mnl_socket *nl;
76 bool enabled;
77} gtp_nl;
78
79/* Always forces the kernel to allocate gtp0. If it exists it hits EEXIST */
80#define GTP_DEVNAME "gtp0"
81
82int gtp_kernel_init(struct gsn_t *gsn, struct in_addr *net,
Harald Welted12eab92017-08-02 19:49:47 +020083 size_t prefixlen,
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +010084 struct gengetopt_args_info *args_info)
85{
Pablo Neira Ayusod9d7be32016-05-10 18:43:12 +020086 if (!args_info->gtp_linux_given)
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +010087 return 0;
88
Pablo Neira Ayuso7b319872016-05-10 18:38:30 +020089 if (gtp_dev_create(-1, GTP_DEVNAME, gsn->fd0, gsn->fd1u) < 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 create GTP tunnel device: %s\n",
92 strerror(errno));
93 return -1;
94 }
95 gtp_nl.enabled = true;
96
97 gtp_nl.nl = genl_socket_open();
98 if (gtp_nl.nl == NULL) {
Andreas Schultzc5fbf9b2015-11-17 12:22:43 +010099 SYS_ERR(DGGSN, LOGL_ERROR, 0,
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100100 "cannot create genetlink socket\n");
101 return -1;
102 }
103 gtp_nl.genl_id = genl_lookup_family(gtp_nl.nl, "gtp");
104 if (gtp_nl.genl_id < 0) {
Andreas Schultzc5fbf9b2015-11-17 12:22:43 +0100105 SYS_ERR(DGGSN, LOGL_ERROR, 0,
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100106 "cannot lookup GTP genetlink ID\n");
107 return -1;
108 }
109 if (debug) {
Andreas Schultzc5fbf9b2015-11-17 12:22:43 +0100110 SYS_ERR(DGGSN, LOGL_NOTICE, 0,
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100111 "Using the GTP kernel mode (genl ID is %d)\n",
112 gtp_nl.genl_id);
113 }
114
Andreas Schultzc5fbf9b2015-11-17 12:22:43 +0100115 DEBUGP(DGGSN, "Setting route to reach %s via %s\n",
116 args_info->net_arg, GTP_DEVNAME);
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100117
Harald Welted12eab92017-08-02 19:49:47 +0200118 if (gtp_dev_config(GTP_DEVNAME, net, prefixlen) < 0) {
Andreas Schultzc5fbf9b2015-11-17 12:22:43 +0100119 SYS_ERR(DGGSN, LOGL_ERROR, 0,
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100120 "Cannot add route to reach network %s\n",
121 args_info->net_arg);
122 }
123
124 /* launch script if it is set to bring up the route to reach
125 * the MS, eg. ip ro add 10.0.0.0/8 dev gtp0. Better add this
126 * using native rtnetlink interface given that we know the
127 * MS network mask, later.
128 */
129 if (ipup) {
130 char cmd[1024];
131 int err;
132
133 /* eg. /home/ggsn/ipup gtp0 10.0.0.0/8 */
134 snprintf(cmd, sizeof(cmd), "%s %s %s",
135 ipup, GTP_DEVNAME, args_info->net_arg);
136 cmd[sizeof(cmd)-1] = '\0';
137
138 err = system(cmd);
139 if (err < 0) {
Andreas Schultzc5fbf9b2015-11-17 12:22:43 +0100140 SYS_ERR(DGGSN, LOGL_ERROR, 0,
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100141 "Failed to launch script `%s'", ipup);
142 return -1;
143 }
144 }
Andreas Schultzc5fbf9b2015-11-17 12:22:43 +0100145 SYS_ERR(DGGSN, LOGL_NOTICE, 0, "GTP kernel configured\n");
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100146
147 return 0;
148}
149
150void gtp_kernel_stop(void)
151{
152 if (!gtp_nl.enabled)
153 return;
154
155 gtp_dev_destroy(GTP_DEVNAME);
156}
157
158int gtp_kernel_tunnel_add(struct pdp_t *pdp)
159{
160 struct in_addr ms, sgsn;
161 struct gtp_tunnel *t;
162 int ret;
163
164 if (!gtp_nl.enabled)
165 return 0;
166
167 pdp_debug(pdp);
168
169 t = gtp_tunnel_alloc();
170 if (t == NULL)
171 return -1;
172
173 memcpy(&ms, &pdp->eua.v[2], sizeof(struct in_addr));
174 memcpy(&sgsn, &pdp->gsnrc.v[0], sizeof(struct in_addr));
175
176 gtp_tunnel_set_ifidx(t, if_nametoindex(GTP_DEVNAME));
177 gtp_tunnel_set_version(t, pdp->version);
178 gtp_tunnel_set_ms_ip4(t, &ms);
179 gtp_tunnel_set_sgsn_ip4(t, &sgsn);
180 if (pdp->version == 0) {
181 gtp_tunnel_set_tid(t, pdp_gettid(pdp->imsi, pdp->nsapi));
182 gtp_tunnel_set_flowid(t, pdp->flru);
183 } else {
Harald Welte875e4dc2017-02-23 20:26:19 +0100184 gtp_tunnel_set_i_tei(t, pdp->teid_own);
185 /* use the TEI advertised by SGSN when sending packets
186 * towards the SGSN */
187 gtp_tunnel_set_o_tei(t, pdp->teid_gn);
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100188 }
189
190 ret = gtp_add_tunnel(gtp_nl.genl_id, gtp_nl.nl, t);
191 gtp_tunnel_free(t);
192
193 return ret;
194}
195
196int gtp_kernel_tunnel_del(struct pdp_t *pdp)
197{
198 struct gtp_tunnel *t;
199 int ret;
200
201 if (!gtp_nl.enabled)
202 return 0;
203
204 pdp_debug(pdp);
205
206 t = gtp_tunnel_alloc();
207 if (t == NULL)
208 return -1;
209
210 gtp_tunnel_set_ifidx(t, if_nametoindex(GTP_DEVNAME));
211 gtp_tunnel_set_version(t, pdp->version);
212 if (pdp->version == 0) {
213 gtp_tunnel_set_tid(t, pdp_gettid(pdp->imsi, pdp->nsapi));
214 gtp_tunnel_set_flowid(t, pdp->flru);
215 } else {
Harald Welte875e4dc2017-02-23 20:26:19 +0100216 gtp_tunnel_set_i_tei(t, pdp->teid_own);
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100217 }
218
219 ret = gtp_del_tunnel(gtp_nl.genl_id, gtp_nl.nl, t);
220 gtp_tunnel_free(t);
221
222 return ret;
223}
224
225int gtp_kernel_enabled(void)
226{
227 return gtp_nl.enabled;
228}