blob: 16e51ac513587a2954e77813e73eea7522575136 [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
11#include <syslog.h>
12#include <stdio.h>
13#include <string.h>
14#include <stdlib.h>
15#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"
31#include "cmdline.h"
32
33#include <libgtpnl/gtp.h>
34#include <libgtpnl/gtpnl.h>
35#include <libmnl/libmnl.h>
36
37#include "gtp-kernel.h"
38
39static void pdp_debug(struct pdp_t *pdp)
40{
41 int i;
42 uint64_t teid;
43
44 if (!debug)
45 return;
46
47 printf("version %u\n", pdp->version);
48 if (pdp->version == 0) {
49 teid = pdp_gettid(pdp->imsi, pdp->nsapi);
50 printf("flowid %u\n", pdp->flru);
51 } else {
52 teid = pdp->teid_gn; /* GTPIE_TEI_DI */
53 }
54
55 printf("teid %llx\n", teid);
56 printf("address (%u)\n", pdp->eua.l);
57
58 /* Byte 0: 0xf1 == IETF */
59 /* Byte 1: 0x21 == IPv4 */
60 /* Byte 2-6: IPv4 address */
61
62 for (i = 0; i < 6; i++)
63 printf("%x ", pdp->eua.v[i] & 0xff); /* GTPIE_EUA */
64
65 printf("\n");
66 printf("sgsn-addr (%u)\n", pdp->gsnrc.l);
67
68 for (i = 0; i < 4; i++)
69 printf("%x ", pdp->gsnrc.v[i] & 0xff); /* GTPIE_GSN_ADDR */
70
71 printf("\n");
72}
73
74static int mask2prefix(struct in_addr *mask)
75{
76 uint32_t tmp = ntohl(mask->s_addr);
77 int k;
78
79 for (k=0; tmp > 0; k++)
80 tmp = (tmp << 1);
81
82 return k;
83}
84
85static struct {
86 int genl_id;
87 struct mnl_socket *nl;
88 bool enabled;
89} gtp_nl;
90
91/* Always forces the kernel to allocate gtp0. If it exists it hits EEXIST */
92#define GTP_DEVNAME "gtp0"
93
94int gtp_kernel_init(struct gsn_t *gsn, struct in_addr *net,
95 struct in_addr *mask,
96 struct gengetopt_args_info *args_info)
97{
98 if (!args_info->gtpnl_given)
99 return 0;
100
101 if (gtp_dev_create(GTP_DEVNAME, args_info->gtpnl_orig,
102 gsn->fd0, gsn->fd1u) < 0) {
103 sys_err(LOG_ERR, __FILE__, __LINE__, 0,
104 "cannot create GTP tunnel device: %s\n",
105 strerror(errno));
106 return -1;
107 }
108 gtp_nl.enabled = true;
109
110 gtp_nl.nl = genl_socket_open();
111 if (gtp_nl.nl == NULL) {
112 sys_err(LOG_ERR, __FILE__, __LINE__, 0,
113 "cannot create genetlink socket\n");
114 return -1;
115 }
116 gtp_nl.genl_id = genl_lookup_family(gtp_nl.nl, "gtp");
117 if (gtp_nl.genl_id < 0) {
118 sys_err(LOG_ERR, __FILE__, __LINE__, 0,
119 "cannot lookup GTP genetlink ID\n");
120 return -1;
121 }
122 if (debug) {
123 sys_err(LOG_NOTICE, __FILE__, __LINE__, 0,
124 "Using the GTP kernel mode (genl ID is %d)\n",
125 gtp_nl.genl_id);
126 }
127
128 if (debug) {
129 printf("Setting route to reach %s via %s\n",
130 args_info->net_arg, GTP_DEVNAME);
131 }
132
133 if (gtp_dev_config(GTP_DEVNAME, net, mask2prefix(mask)) < 0) {
134 sys_err(LOG_ERR, __FILE__, __LINE__, 0,
135 "Cannot add route to reach network %s\n",
136 args_info->net_arg);
137 }
138
139 /* launch script if it is set to bring up the route to reach
140 * the MS, eg. ip ro add 10.0.0.0/8 dev gtp0. Better add this
141 * using native rtnetlink interface given that we know the
142 * MS network mask, later.
143 */
144 if (ipup) {
145 char cmd[1024];
146 int err;
147
148 /* eg. /home/ggsn/ipup gtp0 10.0.0.0/8 */
149 snprintf(cmd, sizeof(cmd), "%s %s %s",
150 ipup, GTP_DEVNAME, args_info->net_arg);
151 cmd[sizeof(cmd)-1] = '\0';
152
153 err = system(cmd);
154 if (err < 0) {
155 sys_err(LOG_ERR, __FILE__, __LINE__, 0,
156 "Failed to launch script `%s'", ipup);
157 return -1;
158 }
159 }
160 sys_err(LOG_NOTICE, __FILE__, __LINE__, 0, "GTP kernel configured\n");
161
162 return 0;
163}
164
165void gtp_kernel_stop(void)
166{
167 if (!gtp_nl.enabled)
168 return;
169
170 gtp_dev_destroy(GTP_DEVNAME);
171}
172
173int gtp_kernel_tunnel_add(struct pdp_t *pdp)
174{
175 struct in_addr ms, sgsn;
176 struct gtp_tunnel *t;
177 int ret;
178
179 if (!gtp_nl.enabled)
180 return 0;
181
182 pdp_debug(pdp);
183
184 t = gtp_tunnel_alloc();
185 if (t == NULL)
186 return -1;
187
188 memcpy(&ms, &pdp->eua.v[2], sizeof(struct in_addr));
189 memcpy(&sgsn, &pdp->gsnrc.v[0], sizeof(struct in_addr));
190
191 gtp_tunnel_set_ifidx(t, if_nametoindex(GTP_DEVNAME));
192 gtp_tunnel_set_version(t, pdp->version);
193 gtp_tunnel_set_ms_ip4(t, &ms);
194 gtp_tunnel_set_sgsn_ip4(t, &sgsn);
195 if (pdp->version == 0) {
196 gtp_tunnel_set_tid(t, pdp_gettid(pdp->imsi, pdp->nsapi));
197 gtp_tunnel_set_flowid(t, pdp->flru);
198 } else {
199 gtp_tunnel_set_tid(t, pdp->teid_gn); /* GTPIE_TEI_DI */
200 }
201
202 ret = gtp_add_tunnel(gtp_nl.genl_id, gtp_nl.nl, t);
203 gtp_tunnel_free(t);
204
205 return ret;
206}
207
208int gtp_kernel_tunnel_del(struct pdp_t *pdp)
209{
210 struct gtp_tunnel *t;
211 int ret;
212
213 if (!gtp_nl.enabled)
214 return 0;
215
216 pdp_debug(pdp);
217
218 t = gtp_tunnel_alloc();
219 if (t == NULL)
220 return -1;
221
222 gtp_tunnel_set_ifidx(t, if_nametoindex(GTP_DEVNAME));
223 gtp_tunnel_set_version(t, pdp->version);
224 if (pdp->version == 0) {
225 gtp_tunnel_set_tid(t, pdp_gettid(pdp->imsi, pdp->nsapi));
226 gtp_tunnel_set_flowid(t, pdp->flru);
227 } else {
228 gtp_tunnel_set_tid(t, pdp->teid_gn); /* GTPIE_TEI_DI */
229 }
230
231 ret = gtp_del_tunnel(gtp_nl.genl_id, gtp_nl.nl, t);
232 gtp_tunnel_free(t);
233
234 return ret;
235}
236
237int gtp_kernel_enabled(void)
238{
239 return gtp_nl.enabled;
240}