blob: 159362fe6cb3856973075855feff51d412a1f9b2 [file] [log] [blame]
Pau Espin Pedrolfdd732b2017-10-13 14:32:24 +02001/*
Harald Welte632e8432017-09-05 18:12:14 +02002 * OsmoGGSN - Gateway GPRS Support Node
jjako0fe0df02004-09-17 11:30:40 +00003 * Copyright (C) 2002, 2003, 2004 Mondru AB.
Harald Welte7bdc80d2019-04-11 18:47:59 +02004 * Copyright (C) 2017-2019 by Harald Welte <laforge@gnumonks.org>
Pau Espin Pedrolfdd732b2017-10-13 14:32:24 +02005 *
jjakoa7cd2492003-04-11 09:40:12 +00006 * The contents of this file may be used under the terms of the GNU
7 * General Public License Version 2, provided that the above copyright
8 * notice and this permission notice is included in all copies or
9 * substantial portions of the software.
Pau Espin Pedrolfdd732b2017-10-13 14:32:24 +020010 *
jjako52c24142002-12-16 13:33:51 +000011 */
12
13/* ggsn.c
14 *
15 */
16
17#ifdef __linux__
18#define _GNU_SOURCE 1 /* strdup() prototype, broken arpa/inet.h */
19#endif
20
jjako0fe0df02004-09-17 11:30:40 +000021#include "../config.h"
22
23#ifdef HAVE_STDINT_H
24#include <stdint.h>
25#endif
jjako52c24142002-12-16 13:33:51 +000026
Harald Weltedda21ed2017-08-12 15:07:02 +020027#include <getopt.h>
jjako52c24142002-12-16 13:33:51 +000028#include <ctype.h>
jjako52c24142002-12-16 13:33:51 +000029#include <signal.h>
30#include <stdio.h>
31#include <string.h>
32#include <stdlib.h>
Harald Weltedda21ed2017-08-12 15:07:02 +020033#include <unistd.h>
34#include <inttypes.h>
35#include <errno.h>
jjako52c24142002-12-16 13:33:51 +000036#include <sys/types.h>
Harald Weltedda21ed2017-08-12 15:07:02 +020037#include <sys/ioctl.h>
38
39#include <net/if.h>
40#include <arpa/inet.h>
jjako52c24142002-12-16 13:33:51 +000041#include <netinet/in.h>
Harald Welte63ebccd2017-08-02 21:10:09 +020042#include <netinet/ip.h>
Harald Weltea0d281d2017-08-02 21:48:16 +020043#include <netinet/ip6.h>
jjako52c24142002-12-16 13:33:51 +000044
Harald Weltedda21ed2017-08-12 15:07:02 +020045#include <osmocom/core/timer.h>
Max727417d2016-08-02 17:10:38 +020046#include <osmocom/ctrl/control_if.h>
Harald Weltedda21ed2017-08-12 15:07:02 +020047#include <osmocom/gsm/apn.h>
Max727417d2016-08-02 17:10:38 +020048
Emmanuel Bretelle2a103682010-09-07 17:01:20 +020049#include "../lib/tun.h"
50#include "../lib/ippool.h"
51#include "../lib/syserr.h"
Harald Welted12eab92017-08-02 19:49:47 +020052#include "../lib/in46_addr.h"
Harald Weltef2286392018-04-25 19:02:31 +020053#include "../lib/gtp-kernel.h"
Pau Espin Pedrolf612ffe2019-08-20 13:24:55 +020054#include "../lib/util.h"
jjako52c24142002-12-16 13:33:51 +000055#include "../gtp/pdp.h"
56#include "../gtp/gtp.h"
Pau Espin Pedrol1c8ae662020-04-09 18:51:05 +020057#include "../lib/icmpv6.h"
Pau Espin Pedrolf7884e82019-08-20 12:06:13 +020058#include "pco.h"
Harald Weltedda21ed2017-08-12 15:07:02 +020059#include "ggsn.h"
jjako52c24142002-12-16 13:33:51 +000060
Harald Weltedda21ed2017-08-12 15:07:02 +020061static int ggsn_tun_fd_cb(struct osmo_fd *fd, unsigned int what);
62static int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len);
63
Pau Espin Pedrolf5fbb412019-08-21 18:49:44 +020064void ggsn_close_one_pdp(struct pdp_t *pdp)
65{
66 LOGPPDP(LOGL_DEBUG, pdp, "Sending DELETE PDP CTX due to shutdown\n");
67 gtp_delete_context_req2(pdp->gsn, pdp, NULL, 1);
68 /* We have nothing more to do with pdp ctx, free it. Upon cb_delete_context
69 called during this call we'll clean up ggsn related stuff attached to this
70 pdp context. After this call, ippool member is cleared so
71 data is no longer valid and should not be accessed anymore. */
72 gtp_freepdp_teardown(pdp->gsn, pdp);
73}
74
Harald Weltedda21ed2017-08-12 15:07:02 +020075static void pool_close_all_pdp(struct ippool_t *pool)
Harald Weltebed35df2011-11-02 13:06:18 +010076{
Harald Weltedda21ed2017-08-12 15:07:02 +020077 unsigned int i;
jjako52c24142002-12-16 13:33:51 +000078
Harald Weltedda21ed2017-08-12 15:07:02 +020079 if (!pool)
Harald Weltebed35df2011-11-02 13:06:18 +010080 return;
Harald Weltedda21ed2017-08-12 15:07:02 +020081
82 for (i = 0; i < pool->listsize; i++) {
83 struct ippoolm_t *member = &pool->member[i];
84 struct pdp_t *pdp;
85
86 if (!member->inuse)
87 continue;
88 pdp = member->peer;
89 if (!pdp)
90 continue;
Pau Espin Pedrolf5fbb412019-08-21 18:49:44 +020091 ggsn_close_one_pdp(pdp);
Harald Weltebed35df2011-11-02 13:06:18 +010092 }
jjako52c24142002-12-16 13:33:51 +000093}
94
Pau Espin Pedrol72ab4bc2019-05-29 19:08:26 +020095int apn_stop(struct apn_ctx *apn)
Harald Weltebed35df2011-11-02 13:06:18 +010096{
Pau Espin Pedrol72ab4bc2019-05-29 19:08:26 +020097 LOGPAPN(LOGL_NOTICE, apn, "Stopping\n");
Harald Weltedda21ed2017-08-12 15:07:02 +020098 /* check if pools have any active PDP contexts and bail out */
99 pool_close_all_pdp(apn->v4.pool);
100 pool_close_all_pdp(apn->v6.pool);
101
102 /* shutdown whatever old state might be left */
103 if (apn->tun.tun) {
104 /* run ip-down script */
105 if (apn->tun.cfg.ipdown_script) {
106 LOGPAPN( LOGL_INFO, apn, "Running %s\n", apn->tun.cfg.ipdown_script);
107 tun_runscript(apn->tun.tun, apn->tun.cfg.ipdown_script);
108 }
Harald Weltef2286392018-04-25 19:02:31 +0200109 if (apn->cfg.gtpu_mode == APN_GTPU_MODE_TUN) {
110 /* release tun device */
111 LOGPAPN(LOGL_INFO, apn, "Closing TUN device %s\n", apn->tun.tun->devname);
112 osmo_fd_unregister(&apn->tun.fd);
113 }
Harald Weltedda21ed2017-08-12 15:07:02 +0200114 tun_free(apn->tun.tun);
115 apn->tun.tun = NULL;
116 }
117
118 if (apn->v4.pool) {
119 LOGPAPN(LOGL_INFO, apn, "Releasing IPv4 pool\n");
120 ippool_free(apn->v4.pool);
121 apn->v4.pool = NULL;
122 }
123 if (apn->v6.pool) {
124 LOGPAPN(LOGL_INFO, apn, "Releasing IPv6 pool\n");
125 ippool_free(apn->v6.pool);
126 apn->v6.pool = NULL;
127 }
128
129 apn->started = false;
130 return 0;
131}
132
Pau Espin Pedrol859f9b02017-10-16 14:52:25 +0200133
Harald Weltef55a0392017-11-08 14:33:55 +0900134static int alloc_ippool_blacklist(struct apn_ctx *apn, struct in46_prefix **blacklist, bool ipv6)
Pau Espin Pedrol859f9b02017-10-16 14:52:25 +0200135{
136
137 int flags, len, len2, i;
138
Harald Weltee2a1de52017-11-08 15:24:07 +0900139 *blacklist = NULL;
140
Pau Espin Pedrol859f9b02017-10-16 14:52:25 +0200141 if (ipv6)
142 flags = IP_TYPE_IPv6_NONLINK;
143 else
144 flags = IP_TYPE_IPv4;
145
146 while (1) {
Harald Weltee2a1de52017-11-08 15:24:07 +0900147 len = netdev_ip_local_get(apn->tun.cfg.dev_name, NULL, 0, flags);
Pau Espin Pedrol859f9b02017-10-16 14:52:25 +0200148 if (len < 1)
149 return len;
150
151 *blacklist = talloc_zero_size(apn, len * sizeof(struct in46_prefix));
Harald Weltee2a1de52017-11-08 15:24:07 +0900152 len2 = netdev_ip_local_get(apn->tun.cfg.dev_name, *blacklist, len, flags);
Pau Espin Pedrol859f9b02017-10-16 14:52:25 +0200153 if (len2 < 1) {
154 talloc_free(*blacklist);
Harald Weltee2a1de52017-11-08 15:24:07 +0900155 *blacklist = NULL;
Pau Espin Pedrol859f9b02017-10-16 14:52:25 +0200156 return len2;
157 }
158
Harald Weltee2a1de52017-11-08 15:24:07 +0900159 if (len2 > len) { /* iface was added between 2 calls, repeat operation */
Pau Espin Pedrol859f9b02017-10-16 14:52:25 +0200160 talloc_free(*blacklist);
Harald Weltee2a1de52017-11-08 15:24:07 +0900161 *blacklist = NULL;
162 } else
Pau Espin Pedrol859f9b02017-10-16 14:52:25 +0200163 break;
164 }
165
166 for (i = 0; i < len2; i++)
167 LOGPAPN(LOGL_INFO, apn, "Blacklist tun IP %s\n",
168 in46p_ntoa(&(*blacklist)[i]));
169
170 return len2;
171}
172
Harald Weltedda21ed2017-08-12 15:07:02 +0200173/* actually start the APN with its current config */
174int apn_start(struct apn_ctx *apn)
175{
Pau Espin Pedrol859f9b02017-10-16 14:52:25 +0200176 int ippool_flags = IPPOOL_NONETWORK | IPPOOL_NOBROADCAST;
Pau Espin Pedrola037e592017-10-16 14:41:37 +0200177 struct in46_prefix ipv6_tun_linklocal_ip;
Pau Espin Pedrol859f9b02017-10-16 14:52:25 +0200178 struct in46_prefix *blacklist;
179 int blacklist_size;
Harald Weltef2286392018-04-25 19:02:31 +0200180 struct gsn_t *gsn = apn->ggsn->gsn;
Pau Espin Pedrolbffc3f92017-12-14 11:19:10 +0100181 int rc;
Pau Espin Pedrol859f9b02017-10-16 14:52:25 +0200182
Harald Weltedda21ed2017-08-12 15:07:02 +0200183 if (apn->started)
184 return 0;
185
186 LOGPAPN(LOGL_INFO, apn, "Starting\n");
187 switch (apn->cfg.gtpu_mode) {
188 case APN_GTPU_MODE_TUN:
189 LOGPAPN(LOGL_INFO, apn, "Opening TUN device %s\n", apn->tun.cfg.dev_name);
Harald Weltef2286392018-04-25 19:02:31 +0200190 if (tun_new(&apn->tun.tun, apn->tun.cfg.dev_name, false, -1, -1)) {
Harald Weltedda21ed2017-08-12 15:07:02 +0200191 LOGPAPN(LOGL_ERROR, apn, "Failed to configure tun device\n");
192 return -1;
193 }
194 LOGPAPN(LOGL_INFO, apn, "Opened TUN device %s\n", apn->tun.tun->devname);
195
196 /* Register with libosmcoore */
197 osmo_fd_setup(&apn->tun.fd, apn->tun.tun->fd, BSC_FD_READ, ggsn_tun_fd_cb, apn, 0);
198 osmo_fd_register(&apn->tun.fd);
199
200 /* Set TUN library callback */
201 tun_set_cb_ind(apn->tun.tun, cb_tun_ind);
Harald Weltedda21ed2017-08-12 15:07:02 +0200202 break;
203 case APN_GTPU_MODE_KERNEL_GTP:
Harald Welte2fc2bc62017-11-08 15:50:53 +0900204 LOGPAPN(LOGL_INFO, apn, "Opening Kernel GTP device %s\n", apn->tun.cfg.dev_name);
Harald Welte490782d2017-11-08 14:09:51 +0900205 if (apn->cfg.apn_type_mask & (APN_TYPE_IPv6|APN_TYPE_IPv4v6)) {
206 LOGPAPN(LOGL_ERROR, apn, "Kernel GTP currently supports only IPv4\n");
Pau Espin Pedrol72ab4bc2019-05-29 19:08:26 +0200207 apn_stop(apn);
Harald Welte490782d2017-11-08 14:09:51 +0900208 return -1;
209 }
Harald Weltef2286392018-04-25 19:02:31 +0200210 if (gsn == NULL) {
Harald Welte07575042018-02-14 01:04:04 +0100211 /* skip bringing up the APN now if the GSN is not initialized yet.
212 * This happens during initial load of the config file, as the
213 * "no shutdown" in the ggsn node only happens after the "apn" nodes
214 * are brought up */
215 LOGPAPN(LOGL_NOTICE, apn, "Skipping APN start\n");
216 return 0;
217 }
Harald Weltedda21ed2017-08-12 15:07:02 +0200218 /* use GTP kernel module for data packet encapsulation */
Harald Weltef2286392018-04-25 19:02:31 +0200219 if (tun_new(&apn->tun.tun, apn->tun.cfg.dev_name, true, gsn->fd0, gsn->fd1u)) {
220 LOGPAPN(LOGL_ERROR, apn, "Failed to configure Kernel GTP device\n");
Harald Welte490782d2017-11-08 14:09:51 +0900221 return -1;
222 }
Harald Weltebed35df2011-11-02 13:06:18 +0100223 break;
224 default:
Harald Weltedda21ed2017-08-12 15:07:02 +0200225 LOGPAPN(LOGL_ERROR, apn, "Unknown GTPU Mode %d\n", apn->cfg.gtpu_mode);
226 return -1;
Harald Weltebed35df2011-11-02 13:06:18 +0100227 }
jjako0141d202004-01-09 15:19:20 +0000228
Harald Weltef2286392018-04-25 19:02:31 +0200229 /* common initialization below */
230
231 /* set back-pointer from TUN device to APN */
232 apn->tun.tun->priv = apn;
233
234 if (apn->v4.cfg.ifconfig_prefix.addr.len) {
235 LOGPAPN(LOGL_INFO, apn, "Setting tun IP address %s\n",
236 in46p_ntoa(&apn->v4.cfg.ifconfig_prefix));
237 if (tun_addaddr(apn->tun.tun, &apn->v4.cfg.ifconfig_prefix.addr, NULL,
238 apn->v4.cfg.ifconfig_prefix.prefixlen)) {
239 LOGPAPN(LOGL_ERROR, apn, "Failed to set tun IPv4 address %s: %s\n",
240 in46p_ntoa(&apn->v4.cfg.ifconfig_prefix), strerror(errno));
Pau Espin Pedrol72ab4bc2019-05-29 19:08:26 +0200241 apn_stop(apn);
Harald Weltef2286392018-04-25 19:02:31 +0200242 return -1;
243 }
244 }
245
246 if (apn->v6.cfg.ifconfig_prefix.addr.len) {
247 LOGPAPN(LOGL_INFO, apn, "Setting tun IPv6 address %s\n",
248 in46p_ntoa(&apn->v6.cfg.ifconfig_prefix));
249 if (tun_addaddr(apn->tun.tun, &apn->v6.cfg.ifconfig_prefix.addr, NULL,
250 apn->v6.cfg.ifconfig_prefix.prefixlen)) {
251 LOGPAPN(LOGL_ERROR, apn, "Failed to set tun IPv6 address %s: %s. "
252 "Ensure you have ipv6 support and not used the disable_ipv6 sysctl?\n",
253 in46p_ntoa(&apn->v6.cfg.ifconfig_prefix), strerror(errno));
Pau Espin Pedrol72ab4bc2019-05-29 19:08:26 +0200254 apn_stop(apn);
Harald Weltef2286392018-04-25 19:02:31 +0200255 return -1;
256 }
257 }
258
259 if (apn->v6.cfg.ll_prefix.addr.len) {
260 LOGPAPN(LOGL_INFO, apn, "Setting tun IPv6 link-local address %s\n",
261 in46p_ntoa(&apn->v6.cfg.ll_prefix));
262 if (tun_addaddr(apn->tun.tun, &apn->v6.cfg.ll_prefix.addr, NULL,
263 apn->v6.cfg.ll_prefix.prefixlen)) {
264 LOGPAPN(LOGL_ERROR, apn, "Failed to set tun IPv6 link-local address %s: %s. "
265 "Ensure you have ipv6 support and not used the disable_ipv6 sysctl?\n",
266 in46p_ntoa(&apn->v6.cfg.ll_prefix), strerror(errno));
Pau Espin Pedrol72ab4bc2019-05-29 19:08:26 +0200267 apn_stop(apn);
Harald Weltef2286392018-04-25 19:02:31 +0200268 return -1;
269 }
270 apn->v6_lladdr = apn->v6.cfg.ll_prefix.addr.v6;
271 }
272
273 if (apn->tun.cfg.ipup_script) {
274 LOGPAPN(LOGL_INFO, apn, "Running ip-up script %s\n",
275 apn->tun.cfg.ipup_script);
276 tun_runscript(apn->tun.tun, apn->tun.cfg.ipup_script);
277 }
278
279 if (apn->cfg.apn_type_mask & (APN_TYPE_IPv6|APN_TYPE_IPv4v6) &&
280 apn->v6.cfg.ll_prefix.addr.len == 0) {
281 rc = tun_ip_local_get(apn->tun.tun, &ipv6_tun_linklocal_ip, 1, IP_TYPE_IPv6_LINK);
282 if (rc < 1) {
283 LOGPAPN(LOGL_ERROR, apn, "Cannot obtain IPv6 link-local address of interface: %s\n",
284 rc ? strerror(errno) : "tun interface has no link-local IP assigned");
Pau Espin Pedrol72ab4bc2019-05-29 19:08:26 +0200285 apn_stop(apn);
Harald Weltef2286392018-04-25 19:02:31 +0200286 return -1;
287 }
288 apn->v6_lladdr = ipv6_tun_linklocal_ip.addr.v6;
289 }
290
Harald Weltedda21ed2017-08-12 15:07:02 +0200291 /* Create IPv4 pool */
292 if (apn->v4.cfg.dynamic_prefix.addr.len) {
293 LOGPAPN(LOGL_INFO, apn, "Creating IPv4 pool %s\n",
294 in46p_ntoa(&apn->v4.cfg.dynamic_prefix));
Harald Weltef55a0392017-11-08 14:33:55 +0900295 if ((blacklist_size = alloc_ippool_blacklist(apn, &blacklist, false)) < 0)
Pau Espin Pedrol859f9b02017-10-16 14:52:25 +0200296 LOGPAPN(LOGL_ERROR, apn, "Failed obtaining IPv4 tun IPs\n");
Harald Weltedda21ed2017-08-12 15:07:02 +0200297 if (ippool_new(&apn->v4.pool, &apn->v4.cfg.dynamic_prefix,
Pau Espin Pedrol859f9b02017-10-16 14:52:25 +0200298 &apn->v4.cfg.static_prefix, ippool_flags,
299 blacklist, blacklist_size)) {
Harald Weltedda21ed2017-08-12 15:07:02 +0200300 LOGPAPN(LOGL_ERROR, apn, "Failed to create IPv4 pool\n");
Pau Espin Pedrol859f9b02017-10-16 14:52:25 +0200301 talloc_free(blacklist);
Pau Espin Pedrol72ab4bc2019-05-29 19:08:26 +0200302 apn_stop(apn);
Harald Weltedda21ed2017-08-12 15:07:02 +0200303 return -1;
304 }
Pau Espin Pedrol859f9b02017-10-16 14:52:25 +0200305 talloc_free(blacklist);
Harald Weltebed35df2011-11-02 13:06:18 +0100306 }
Harald Weltedda21ed2017-08-12 15:07:02 +0200307
308 /* Create IPv6 pool */
309 if (apn->v6.cfg.dynamic_prefix.addr.len) {
310 LOGPAPN(LOGL_INFO, apn, "Creating IPv6 pool %s\n",
311 in46p_ntoa(&apn->v6.cfg.dynamic_prefix));
Harald Weltef55a0392017-11-08 14:33:55 +0900312 if ((blacklist_size = alloc_ippool_blacklist(apn, &blacklist, true)) < 0)
Pau Espin Pedrol859f9b02017-10-16 14:52:25 +0200313 LOGPAPN(LOGL_ERROR, apn, "Failed obtaining IPv6 tun IPs\n");
Harald Weltedda21ed2017-08-12 15:07:02 +0200314 if (ippool_new(&apn->v6.pool, &apn->v6.cfg.dynamic_prefix,
Pau Espin Pedrol859f9b02017-10-16 14:52:25 +0200315 &apn->v6.cfg.static_prefix, ippool_flags,
316 blacklist, blacklist_size)) {
Harald Weltedda21ed2017-08-12 15:07:02 +0200317 LOGPAPN(LOGL_ERROR, apn, "Failed to create IPv6 pool\n");
Pau Espin Pedrol859f9b02017-10-16 14:52:25 +0200318 talloc_free(blacklist);
Pau Espin Pedrol72ab4bc2019-05-29 19:08:26 +0200319 apn_stop(apn);
Harald Weltedda21ed2017-08-12 15:07:02 +0200320 return -1;
321 }
Pau Espin Pedrol859f9b02017-10-16 14:52:25 +0200322 talloc_free(blacklist);
Harald Weltedda21ed2017-08-12 15:07:02 +0200323 }
324
325 LOGPAPN(LOGL_NOTICE, apn, "Successfully started\n");
326 apn->started = true;
327 return 0;
jjako0141d202004-01-09 15:19:20 +0000328}
jjako0141d202004-01-09 15:19:20 +0000329
Max3142d8d2017-05-04 17:45:10 +0200330static bool send_trap(const struct gsn_t *gsn, const struct pdp_t *pdp, const struct ippoolm_t *member, const char *var)
331{
Harald Welted12eab92017-08-02 19:49:47 +0200332 char addrbuf[256];
Max3142d8d2017-05-04 17:45:10 +0200333 char val[NAMESIZE];
334
Harald Welted12eab92017-08-02 19:49:47 +0200335 const char *addrstr = in46a_ntop(&member->addr, addrbuf, sizeof(addrbuf));
336
Harald Welteb10ee082017-08-12 19:29:16 +0200337 snprintf(val, sizeof(val), "%s,%s", imsi_gtp2str(&pdp->imsi), addrstr);
Max3142d8d2017-05-04 17:45:10 +0200338
Harald Weltedda21ed2017-08-12 15:07:02 +0200339 if (ctrl_cmd_send_trap(g_ctrlh, var, val) < 0) {
340 LOGPPDP(LOGL_ERROR, pdp, "Failed to create and send TRAP %s\n", var);
Max3142d8d2017-05-04 17:45:10 +0200341 return false;
342 }
343 return true;
344}
345
Harald Weltedda21ed2017-08-12 15:07:02 +0200346static int delete_context(struct pdp_t *pdp)
Harald Weltebed35df2011-11-02 13:06:18 +0100347{
Harald Weltedda21ed2017-08-12 15:07:02 +0200348 struct gsn_t *gsn = pdp->gsn;
Pau Espin Pedrolf5fbb412019-08-21 18:49:44 +0200349 struct pdp_priv_t *pdp_priv = pdp->priv;
350 struct apn_ctx *apn;
Pau Espin Pedrol2d6a69e2017-12-06 19:26:25 +0100351 struct ippoolm_t *member;
352 int i;
Harald Weltedda21ed2017-08-12 15:07:02 +0200353
354 LOGPPDP(LOGL_INFO, pdp, "Deleting PDP context\n");
Maxdbd70242016-10-14 13:38:05 +0200355
Pau Espin Pedrol2d6a69e2017-12-06 19:26:25 +0100356 for (i = 0; i < 2; i++) {
357 if (pdp->peer[i]) {
358 member = pdp->peer[i];
359 send_trap(gsn, pdp, member, "imsi-rem-ip"); /* TRAP with IP removal */
360 ippool_freeip(member->pool, member);
Pau Espin Pedrol3eb05d22019-08-28 11:17:08 +0200361 } else if (i == 0) {
Pau Espin Pedrol2d6a69e2017-12-06 19:26:25 +0100362 LOGPPDP(LOGL_ERROR, pdp, "Cannot find/free IP Pool member\n");
Pau Espin Pedrol3eb05d22019-08-28 11:17:08 +0200363 }
Pau Espin Pedrol2d6a69e2017-12-06 19:26:25 +0100364 }
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100365
Pau Espin Pedrolf5fbb412019-08-21 18:49:44 +0200366 if (!pdp_priv) {
367 LOGPPDP(LOGL_NOTICE, pdp, "Deleting PDP context: without private structure!\n");
368 return 0;
369 }
370
371 /* Remove from SGSN */
372 sgsn_peer_remove_pdp_priv(pdp_priv);
373
374 apn = pdp_priv->apn;
Pau Espin Pedrold9501342019-08-21 15:24:29 +0200375 if (apn && apn->cfg.gtpu_mode == APN_GTPU_MODE_KERNEL_GTP) {
Harald Welte546884d2018-04-25 21:13:06 +0200376 if (gtp_kernel_tunnel_del(pdp, apn->tun.cfg.dev_name)) {
377 LOGPPDP(LOGL_ERROR, pdp, "Cannot delete tunnel from kernel:%s\n",
378 strerror(errno));
379 }
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100380 }
381
Pau Espin Pedrolf5fbb412019-08-21 18:49:44 +0200382 talloc_free(pdp_priv);
383
Harald Weltebed35df2011-11-02 13:06:18 +0100384 return 0;
jjako52c24142002-12-16 13:33:51 +0000385}
386
Harald Welte9d9d91b2017-10-14 16:22:16 +0200387static bool apn_supports_ipv4(const struct apn_ctx *apn)
388{
389 if (apn->v4.cfg.static_prefix.addr.len || apn->v4.cfg.dynamic_prefix.addr.len)
390 return true;
391 return false;
392}
393
394static bool apn_supports_ipv6(const struct apn_ctx *apn)
395{
396 if (apn->v6.cfg.static_prefix.addr.len || apn->v6.cfg.dynamic_prefix.addr.len)
397 return true;
398 return false;
399}
400
Pau Espin Pedrolf5fbb412019-08-21 18:49:44 +0200401static struct sgsn_peer* ggsn_find_sgsn(struct ggsn_ctx *ggsn, struct in_addr *peer_addr)
402{
403 struct sgsn_peer *sgsn;
404
405 llist_for_each_entry(sgsn, &ggsn->sgsn_list, entry) {
406 if (memcmp(&sgsn->addr, peer_addr, sizeof(*peer_addr)) == 0)
407 return sgsn;
408 }
409 return NULL;
410}
411
412static struct sgsn_peer* ggsn_find_or_create_sgsn(struct ggsn_ctx *ggsn, struct pdp_t *pdp)
413{
414 struct sgsn_peer *sgsn;
415 struct in_addr ia;
416
417 if (gsna2in_addr(&ia, &pdp->gsnrc)) {
418 LOGPPDP(LOGL_ERROR, pdp, "Failed parsing gsnrc (len=%u) to discover SGSN\n",
419 pdp->gsnrc.l);
420 return NULL;
421 }
422
423 if ((sgsn = ggsn_find_sgsn(ggsn, &ia)))
424 return sgsn;
425
426 sgsn = sgsn_peer_allocate(ggsn, &ia, pdp->version);
427 llist_add(&sgsn->entry, &ggsn->sgsn_list);
428 return sgsn;
429}
430
Harald Weltebed35df2011-11-02 13:06:18 +0100431int create_context_ind(struct pdp_t *pdp)
432{
Harald Weltedda21ed2017-08-12 15:07:02 +0200433 static char name_buf[256];
434 struct gsn_t *gsn = pdp->gsn;
435 struct ggsn_ctx *ggsn = gsn->priv;
Pau Espin Pedrol2d6a69e2017-12-06 19:26:25 +0100436 struct in46_addr addr[2];
Pau Espin Pedrol4e43ef52018-01-26 18:12:19 +0100437 struct ippoolm_t *member = NULL, *addrv4 = NULL, *addrv6 = NULL;
438 char straddrv4[INET_ADDRSTRLEN], straddrv6[INET6_ADDRSTRLEN];
Vadim Yanitskiy2e8e57a2019-05-13 22:09:15 +0700439 struct apn_ctx *apn = NULL;
Pau Espin Pedrol2d6a69e2017-12-06 19:26:25 +0100440 int rc, num_addr, i;
Vadim Yanitskiy2e8e57a2019-05-13 22:09:15 +0700441 char *apn_name;
Pau Espin Pedrolf5fbb412019-08-21 18:49:44 +0200442 struct sgsn_peer *sgsn;
443 struct pdp_priv_t *pdp_priv;
jjako52c24142002-12-16 13:33:51 +0000444
Vadim Yanitskiy2e8e57a2019-05-13 22:09:15 +0700445 apn_name = osmo_apn_to_str(name_buf, pdp->apn_req.v, pdp->apn_req.l);
446 LOGPPDP(LOGL_DEBUG, pdp, "Processing create PDP context request for APN '%s'\n",
447 apn_name ? name_buf : "(NONE)");
Harald Weltedda21ed2017-08-12 15:07:02 +0200448
449 /* First find an exact APN name match */
Vadim Yanitskiy2e8e57a2019-05-13 22:09:15 +0700450 if (apn_name != NULL)
451 apn = ggsn_find_apn(ggsn, name_buf);
Harald Welte2e84d2c2017-10-01 13:36:52 +0800452 /* ignore if the APN has not been started */
Pau Espin Pedrol958256f2017-10-11 20:32:55 +0200453 if (apn && !apn->started)
Harald Welte2e84d2c2017-10-01 13:36:52 +0800454 apn = NULL;
Harald Welteb16c46b2017-10-01 18:28:18 +0800455
Harald Weltedda21ed2017-08-12 15:07:02 +0200456 /* then try default (if any) */
457 if (!apn)
458 apn = ggsn->cfg.default_apn;
Harald Welteb16c46b2017-10-01 18:28:18 +0800459 /* ignore if the APN has not been started */
Pau Espin Pedrol958256f2017-10-11 20:32:55 +0200460 if (apn && !apn->started)
Harald Welteb16c46b2017-10-01 18:28:18 +0800461 apn = NULL;
462
Harald Weltedda21ed2017-08-12 15:07:02 +0200463 if (!apn) {
464 /* no APN found for what user requested */
465 LOGPPDP(LOGL_NOTICE, pdp, "Unknown APN '%s', rejecting\n", name_buf);
466 gtp_create_context_resp(gsn, pdp, GTPCAUSE_MISSING_APN);
467 return 0;
468 }
jjako52c24142002-12-16 13:33:51 +0000469
Harald Welted9d88622017-08-04 00:22:35 +0200470 /* FIXME: we manually force all context requests to dynamic here! */
471 if (pdp->eua.l > 2)
472 pdp->eua.l = 2;
jjako52c24142002-12-16 13:33:51 +0000473
Harald Weltebed35df2011-11-02 13:06:18 +0100474 memcpy(pdp->qos_neg0, pdp->qos_req0, sizeof(pdp->qos_req0));
jjako52c24142002-12-16 13:33:51 +0000475
Harald Weltebed35df2011-11-02 13:06:18 +0100476 memcpy(pdp->qos_neg.v, pdp->qos_req.v, pdp->qos_req.l); /* TODO */
477 pdp->qos_neg.l = pdp->qos_req.l;
jjako52c24142002-12-16 13:33:51 +0000478
Pau Espin Pedrol2d6a69e2017-12-06 19:26:25 +0100479 memset(addr, 0, sizeof(addr));
480 if ((num_addr = in46a_from_eua(&pdp->eua, addr)) < 0) {
Harald Weltedda21ed2017-08-12 15:07:02 +0200481 LOGPPDP(LOGL_ERROR, pdp, "Cannot decode EUA from MS/SGSN: %s\n",
Harald Welted1bf1e12017-08-03 00:00:23 +0200482 osmo_hexdump(pdp->eua.v, pdp->eua.l));
483 gtp_create_context_resp(gsn, pdp, GTPCAUSE_UNKNOWN_PDP);
484 return 0;
Harald Weltebed35df2011-11-02 13:06:18 +0100485 }
jjakoa7cd2492003-04-11 09:40:12 +0000486
Vadim Yanitskiyd7030d22019-05-13 22:10:24 +0700487 /* Store the actual APN for logging and the VTY */
488 rc = osmo_apn_from_str(pdp->apn_use.v, sizeof(pdp->apn_use.v), apn->cfg.name);
489 if (rc < 0) /* Unlikely this would happen, but anyway... */
490 LOGPPDP(LOGL_ERROR, pdp, "Failed to store APN '%s'\n", apn->cfg.name);
491 pdp->apn_use.l = rc;
492
Pau Espin Pedrol2d6a69e2017-12-06 19:26:25 +0100493 /* Allocate dynamic addresses from the pool */
494 for (i = 0; i < num_addr; i++) {
Pau Espin Pedrol60ee0db2019-08-20 11:43:25 +0200495 if (in46a_is_v4(&addr[i])) {
Pau Espin Pedrol2d6a69e2017-12-06 19:26:25 +0100496 /* does this APN actually have an IPv4 pool? */
497 if (!apn_supports_ipv4(apn))
498 goto err_wrong_af;
Harald Welte9d9d91b2017-10-14 16:22:16 +0200499
Pau Espin Pedrol2d6a69e2017-12-06 19:26:25 +0100500 rc = ippool_newip(apn->v4.pool, &member, &addr[i], 0);
501 if (rc < 0)
502 goto err_pool_full;
503 /* copy back */
504 memcpy(&addr[i].v4.s_addr, &member->addr.v4, 4);
jjakoa7cd2492003-04-11 09:40:12 +0000505
Pau Espin Pedrol4e43ef52018-01-26 18:12:19 +0100506 addrv4 = member;
507
Pau Espin Pedrol60ee0db2019-08-20 11:43:25 +0200508 } else if (in46a_is_v6(&addr[i])) {
Pau Espin Pedrol2d6a69e2017-12-06 19:26:25 +0100509
510 /* does this APN actually have an IPv6 pool? */
511 if (!apn_supports_ipv6(apn))
512 goto err_wrong_af;
513
514 rc = ippool_newip(apn->v6.pool, &member, &addr[i], 0);
515 if (rc < 0)
516 goto err_pool_full;
517
518 /* IPv6 doesn't really send the real/allocated address at this point, but just
519 * the link-identifier which the MS shall use for router solicitation */
520 /* initialize upper 64 bits to prefix, they are discarded by MS anyway */
521 memcpy(addr[i].v6.s6_addr, &member->addr.v6, 8);
522 /* use allocated 64bit prefix as lower 64bit, used as link id by MS */
523 memcpy(addr[i].v6.s6_addr+8, &member->addr.v6, 8);
Pau Espin Pedrol4e43ef52018-01-26 18:12:19 +0100524
525 addrv6 = member;
Pau Espin Pedrol2d6a69e2017-12-06 19:26:25 +0100526 } else
527 OSMO_ASSERT(0);
528
529 pdp->peer[i] = member;
530 member->peer = pdp;
531 }
532
533 in46a_to_eua(addr, num_addr, &pdp->eua);
534
Harald Welte546884d2018-04-25 21:13:06 +0200535 if (apn->cfg.gtpu_mode == APN_GTPU_MODE_KERNEL_GTP && apn_supports_ipv4(apn)) {
Harald Weltedda21ed2017-08-12 15:07:02 +0200536 /* TODO: In IPv6, EUA doesn't contain the actual IP addr/prefix! */
Harald Welte698a2332017-11-08 15:09:58 +0900537 if (gtp_kernel_tunnel_add(pdp, apn->tun.cfg.dev_name) < 0) {
Harald Weltedda21ed2017-08-12 15:07:02 +0200538 LOGPPDP(LOGL_ERROR, pdp, "Cannot add tunnel to kernel: %s\n", strerror(errno));
539 gtp_create_context_resp(gsn, pdp, GTPCAUSE_SYS_FAIL);
540 return 0;
541 }
Pau Espin Pedrol2d6a69e2017-12-06 19:26:25 +0100542 }
Harald Welte9d9d91b2017-10-14 16:22:16 +0200543
Harald Weltedda21ed2017-08-12 15:07:02 +0200544 pdp->ipif = apn->tun.tun; /* TODO */
Pau Espin Pedrolf5fbb412019-08-21 18:49:44 +0200545
546 pdp_priv = talloc_zero(ggsn, struct pdp_priv_t);
547 pdp->priv = pdp_priv;
548 pdp_priv->lib = pdp;
549 /* Create sgsn and assign pdp to it */
550 sgsn = ggsn_find_or_create_sgsn(ggsn, pdp);
551 sgsn_peer_add_pdp_priv(sgsn, pdp_priv);
552 pdp_priv->apn = apn;
Max3142d8d2017-05-04 17:45:10 +0200553
Pau Espin Pedrol4e43ef52018-01-26 18:12:19 +0100554 /* TODO: change trap to send 2 IPs */
Max3142d8d2017-05-04 17:45:10 +0200555 if (!send_trap(gsn, pdp, member, "imsi-ass-ip")) { /* TRAP with IP assignment */
Max727417d2016-08-02 17:10:38 +0200556 gtp_create_context_resp(gsn, pdp, GTPCAUSE_NO_RESOURCES);
557 return 0;
558 }
Pablo Neira Ayuso4b075b62015-11-17 12:22:42 +0100559
Harald Weltedda21ed2017-08-12 15:07:02 +0200560 process_pco(apn, pdp);
Harald Welte1ae98772017-08-09 20:28:52 +0200561
Harald Welte93fed3b2017-09-24 11:43:17 +0800562 /* Transmit G-PDU sequence numbers (only) if configured in APN */
563 pdp->tx_gpdu_seq = apn->cfg.tx_gpdu_seq;
564
Pau Espin Pedrol4e43ef52018-01-26 18:12:19 +0100565 LOGPPDP(LOGL_INFO, pdp, "Successful PDP Context Creation: APN=%s(%s), TEIC=%u, IPv4=%s, IPv6=%s\n",
566 name_buf, apn->cfg.name, pdp->teic_own,
567 addrv4 ? inet_ntop(AF_INET, &addrv4->addr.v4, straddrv4, sizeof(straddrv4)) : "none",
568 addrv6 ? inet_ntop(AF_INET6, &addrv6->addr.v6, straddrv6, sizeof(straddrv6)) : "none");
Harald Weltebed35df2011-11-02 13:06:18 +0100569 gtp_create_context_resp(gsn, pdp, GTPCAUSE_ACC_REQ);
570 return 0; /* Success */
Harald Weltedda21ed2017-08-12 15:07:02 +0200571
572err_pool_full:
573 LOGPPDP(LOGL_ERROR, pdp, "Cannot allocate IP address from pool (full!)\n");
574 gtp_create_context_resp(gsn, pdp, -rc);
575 return 0; /* Already in use, or no more available */
Harald Welte9d9d91b2017-10-14 16:22:16 +0200576
577err_wrong_af:
578 LOGPPDP(LOGL_ERROR, pdp, "APN doesn't support requested EUA / AF type\n");
579 gtp_create_context_resp(gsn, pdp, GTPCAUSE_UNKNOWN_PDP);
580 return 0;
jjako52c24142002-12-16 13:33:51 +0000581}
582
Harald Weltedda21ed2017-08-12 15:07:02 +0200583/* Internet-originated IP packet, needs to be sent via GTP towards MS */
584static int cb_tun_ind(struct tun_t *tun, void *pack, unsigned len)
Harald Weltebed35df2011-11-02 13:06:18 +0100585{
Harald Weltedda21ed2017-08-12 15:07:02 +0200586 struct apn_ctx *apn = tun->priv;
Harald Weltebed35df2011-11-02 13:06:18 +0100587 struct ippoolm_t *ipm;
Harald Welted12eab92017-08-02 19:49:47 +0200588 struct in46_addr dst;
Harald Welte63ebccd2017-08-02 21:10:09 +0200589 struct iphdr *iph = (struct iphdr *)pack;
Harald Weltea0d281d2017-08-02 21:48:16 +0200590 struct ip6_hdr *ip6h = (struct ip6_hdr *)pack;
Harald Weltedda21ed2017-08-12 15:07:02 +0200591 struct ippool_t *pool;
Pau Espin Pedrol134855c2018-01-30 16:04:53 +0100592 char straddr[INET6_ADDRSTRLEN];
Pau Espin Pedroldddbbaa2018-01-30 16:16:33 +0100593 uint8_t pref_offset;
jjakoc6762cf2004-04-28 14:52:58 +0000594
Pau Espin Pedrola4942e62018-01-30 16:01:27 +0100595 switch (iph->version) {
596 case 4:
Harald Welted12eab92017-08-02 19:49:47 +0200597 if (len < sizeof(*iph) || len < 4*iph->ihl)
598 return -1;
599 dst.len = 4;
Harald Welte63ebccd2017-08-02 21:10:09 +0200600 dst.v4.s_addr = iph->daddr;
Harald Weltedda21ed2017-08-12 15:07:02 +0200601 pool = apn->v4.pool;
Pau Espin Pedrola4942e62018-01-30 16:01:27 +0100602 break;
603 case 6:
Harald Welted4d6e092017-08-08 18:10:43 +0200604 /* Due to the fact that 3GPP requires an allocation of a
605 * /64 prefix to each MS, we must instruct
606 * ippool_getip() below to match only the leading /64
Pau Espin Pedroldddbbaa2018-01-30 16:16:33 +0100607 * prefix, i.e. the first 8 bytes of the address. If the ll addr
608 * is used, then the match should be done on the trailing 64
609 * bits. */
Harald Welted4d6e092017-08-08 18:10:43 +0200610 dst.len = 8;
Pau Espin Pedroldddbbaa2018-01-30 16:16:33 +0100611 pref_offset = IN6_IS_ADDR_LINKLOCAL(&ip6h->ip6_dst) ? 8 : 0;
612 memcpy(&dst.v6, ((uint8_t*)&ip6h->ip6_dst) + pref_offset, 8);
Harald Weltedda21ed2017-08-12 15:07:02 +0200613 pool = apn->v6.pool;
Pau Espin Pedrola4942e62018-01-30 16:01:27 +0100614 break;
615 default:
Pau Espin Pedrol012d51e2019-08-20 13:43:38 +0200616 LOGTUN(LOGL_NOTICE, tun, "non-IPv%u packet received\n", iph->version);
Harald Welted12eab92017-08-02 19:49:47 +0200617 return -1;
618 }
jjakoc6762cf2004-04-28 14:52:58 +0000619
Harald Weltedda21ed2017-08-12 15:07:02 +0200620 /* IPv6 packet but no IPv6 pool, or IPv4 packet with no IPv4 pool */
621 if (!pool)
622 return 0;
Harald Weltebed35df2011-11-02 13:06:18 +0100623
Harald Weltedda21ed2017-08-12 15:07:02 +0200624 if (ippool_getip(pool, &ipm, &dst)) {
Pau Espin Pedrol012d51e2019-08-20 13:43:38 +0200625 LOGTUN(LOGL_DEBUG, tun, "Received packet for APN(%s) with no PDP contex! (%s)\n",
626 apn->cfg.name,
627 iph->version == 4 ?
Pau Espin Pedrol134855c2018-01-30 16:04:53 +0100628 inet_ntop(AF_INET, &iph->saddr, straddr, sizeof(straddr)) :
629 inet_ntop(AF_INET6, &ip6h->ip6_src, straddr, sizeof(straddr)));
Harald Weltebed35df2011-11-02 13:06:18 +0100630 return 0;
631 }
Pau Espin Pedrol012d51e2019-08-20 13:43:38 +0200632 LOGTUN(LOGL_DEBUG, tun, "Received packet for APN(%s)\n", apn->cfg.name);
Harald Weltebed35df2011-11-02 13:06:18 +0100633
634 if (ipm->peer) /* Check if a peer protocol is defined */
Harald Weltedda21ed2017-08-12 15:07:02 +0200635 gtp_data_req(apn->ggsn->gsn, (struct pdp_t *)ipm->peer, pack, len);
Harald Weltebed35df2011-11-02 13:06:18 +0100636 return 0;
jjako52c24142002-12-16 13:33:51 +0000637}
638
Harald Weltedda21ed2017-08-12 15:07:02 +0200639/* MS-originated GTP1-U packet, needs to be sent via TUN device */
640static int encaps_tun(struct pdp_t *pdp, void *pack, unsigned len)
Harald Weltebed35df2011-11-02 13:06:18 +0100641{
Harald Welted46bcd22017-08-08 23:27:22 +0200642 struct iphdr *iph = (struct iphdr *)pack;
643 struct ip6_hdr *ip6h = (struct ip6_hdr *)pack;
Harald Weltef85fe972017-09-24 20:00:34 +0800644 struct tun_t *tun = (struct tun_t *)pdp->ipif;
645 struct apn_ctx *apn = tun->priv;
Pau Espin Pedrol5b1ef952018-01-25 20:50:59 +0100646 char straddr[INET6_ADDRSTRLEN];
Pau Espin Pedrol7d54ed42018-01-25 20:09:16 +0100647 struct ippoolm_t *peer;
Pau Espin Pedrol5b1ef952018-01-25 20:50:59 +0100648 uint8_t pref_offset;
Harald Weltef85fe972017-09-24 20:00:34 +0800649
650 OSMO_ASSERT(tun);
651 OSMO_ASSERT(apn);
Harald Welted46bcd22017-08-08 23:27:22 +0200652
Max427699e2017-12-05 16:30:37 +0100653 LOGPPDP(LOGL_DEBUG, pdp, "Packet received on APN(%s): forwarding to tun %s\n", apn->cfg.name, tun->devname);
Harald Welted46bcd22017-08-08 23:27:22 +0200654
655 switch (iph->version) {
656 case 6:
Pau Espin Pedrol7d54ed42018-01-25 20:09:16 +0100657 peer = pdp_get_peer_ipv(pdp, true);
658 if (!peer) {
659 LOGPPDP(LOGL_ERROR, pdp, "Packet from MS IPv6 with unassigned EUA: %s\n",
660 osmo_hexdump(pack, len));
661 return -1;
662 }
663
Pau Espin Pedrol5b1ef952018-01-25 20:50:59 +0100664 /* Validate packet comes from IPaddr assigned to the pdp ctx.
665 If packet is a LL addr, then EUA is in the lower 64 bits,
666 otherwise it's used as the 64 prefix */
667 pref_offset = IN6_IS_ADDR_LINKLOCAL(&ip6h->ip6_src) ? 8 : 0;
668 if (memcmp(((uint8_t*)&ip6h->ip6_src) + pref_offset, &peer->addr.v6, 8)) {
669 LOGPPDP(LOGL_ERROR, pdp, "Packet from MS using unassigned src IPv6: %s\n",
670 inet_ntop(AF_INET6, &ip6h->ip6_src, straddr, sizeof(straddr)));
671 return -1;
672 }
673
Harald Welted46bcd22017-08-08 23:27:22 +0200674 /* daddr: all-routers multicast addr */
675 if (IN6_ARE_ADDR_EQUAL(&ip6h->ip6_dst, &all_router_mcast_addr))
Pau Espin Pedrol7d54ed42018-01-25 20:09:16 +0100676 return handle_router_mcast(pdp->gsn, pdp, &peer->addr.v6,
677 &apn->v6_lladdr, pack, len);
Harald Welted46bcd22017-08-08 23:27:22 +0200678 break;
679 case 4:
Pau Espin Pedrol7d54ed42018-01-25 20:09:16 +0100680 peer = pdp_get_peer_ipv(pdp, false);
681 if (!peer) {
682 LOGPPDP(LOGL_ERROR, pdp, "Packet from MS IPv4 with unassigned EUA: %s\n",
683 osmo_hexdump(pack, len));
684 return -1;
685 }
Pau Espin Pedrol5b1ef952018-01-25 20:50:59 +0100686
687 /* Validate packet comes from IPaddr assigned to the pdp ctx */
688 if (memcmp(&iph->saddr, &peer->addr.v4, sizeof(peer->addr.v4))) {
689 LOGPPDP(LOGL_ERROR, pdp, "Packet from MS using unassigned src IPv4: %s\n",
690 inet_ntop(AF_INET, &iph->saddr, straddr, sizeof(straddr)));
691 return -1;
692 }
Harald Welted46bcd22017-08-08 23:27:22 +0200693 break;
694 default:
Harald Weltedda21ed2017-08-12 15:07:02 +0200695 LOGPPDP(LOGL_ERROR, pdp, "Packet from MS is neither IPv4 nor IPv6: %s\n",
696 osmo_hexdump(pack, len));
Harald Welted46bcd22017-08-08 23:27:22 +0200697 return -1;
698 }
Harald Weltebed35df2011-11-02 13:06:18 +0100699 return tun_encaps((struct tun_t *)pdp->ipif, pack, len);
jjako52c24142002-12-16 13:33:51 +0000700}
701
Harald Weltedda21ed2017-08-12 15:07:02 +0200702/* callback for tun device osmocom select loop integration */
703static int ggsn_tun_fd_cb(struct osmo_fd *fd, unsigned int what)
704{
705 struct apn_ctx *apn = fd->data;
706
707 OSMO_ASSERT(what & BSC_FD_READ);
708
709 return tun_decaps(apn->tun.tun);
710}
711
712/* callback for libgtp osmocom select loop integration */
713static int ggsn_gtp_fd_cb(struct osmo_fd *fd, unsigned int what)
714{
715 struct ggsn_ctx *ggsn = fd->data;
716 int rc;
717
718 OSMO_ASSERT(what & BSC_FD_READ);
719
720 switch (fd->priv_nr) {
721 case 0:
722 rc = gtp_decaps0(ggsn->gsn);
723 break;
724 case 1:
725 rc = gtp_decaps1c(ggsn->gsn);
726 break;
727 case 2:
728 rc = gtp_decaps1u(ggsn->gsn);
729 break;
730 default:
731 OSMO_ASSERT(0);
732 break;
733 }
734 return rc;
735}
736
Oliver Smith1cde2c12019-05-13 11:35:03 +0200737/* libgtp callback for confirmations */
738static int cb_conf(int type, int cause, struct pdp_t *pdp, void *cbp)
739{
Pau Espin Pedrolf5fbb412019-08-21 18:49:44 +0200740 struct sgsn_peer *sgsn;
Oliver Smith1cde2c12019-05-13 11:35:03 +0200741 int rc = 0;
742
743 if (cause == EOF)
744 LOGP(DGGSN, LOGL_NOTICE, "libgtp EOF (type=%u, pdp=%p, cbp=%p)\n",
745 type, pdp, cbp);
746
747 switch (type) {
748 case GTP_DELETE_PDP_REQ:
749 /* Remark: We actually never reach this path nowadays because
750 only place where we call gtp_delete_context_req2() is during
Pau Espin Pedrol26e300f2019-08-29 14:02:28 +0200751 ggsn_close_one_pdp() path, and in that case we free all pdp
752 contexts immediatelly without waiting for confirmation
753 (through gtp_freepdp_teardown()) since we want to tear down
754 the whole APN anyways. As a result, DeleteCtxResponse will
755 never reach here since it will be dropped at some point in
756 lower layers in the Rx path. This code is nevertheless left
757 here in order to ease future developent and avoid possible
758 future memleaks once more scenarios where GGSN sends a
759 DeleteCtxRequest are introduced. */
Pau Espin Pedrolf5fbb412019-08-21 18:49:44 +0200760 if (pdp)
Pau Espin Pedrol26e300f2019-08-29 14:02:28 +0200761 rc = gtp_freepdp(pdp->gsn, pdp);
Pau Espin Pedrolf5fbb412019-08-21 18:49:44 +0200762 break;
763 case GTP_ECHO_REQ:
764 sgsn = (struct sgsn_peer *)cbp;
765 sgsn_peer_echo_resp(sgsn, cause == EOF);
766 break;
Oliver Smith1cde2c12019-05-13 11:35:03 +0200767 }
768 return rc;
769}
Harald Weltedda21ed2017-08-12 15:07:02 +0200770
Pau Espin Pedrolf5fbb412019-08-21 18:49:44 +0200771static int cb_recovery3(struct gsn_t *gsn, struct sockaddr_in *peer, struct pdp_t *pdp, uint8_t recovery)
772{
773 struct ggsn_ctx *ggsn = (struct ggsn_ctx *)gsn->priv;
774 struct sgsn_peer *sgsn;
775
776 sgsn = ggsn_find_sgsn(ggsn, &peer->sin_addr);
777 if (!sgsn) {
778 LOGPGGSN(LOGL_NOTICE, ggsn, "Received Recovery IE for unknown SGSN (no PDP contexts active)\n");
779 return -EINVAL;
780 }
781
782 return sgsn_peer_handle_recovery(sgsn, pdp, recovery);
783}
784
Harald Weltedda21ed2017-08-12 15:07:02 +0200785/* Start a given GGSN */
786int ggsn_start(struct ggsn_ctx *ggsn)
787{
788 struct apn_ctx *apn;
789 int rc;
790
791 if (ggsn->started)
792 return 0;
793
794 LOGPGGSN(LOGL_INFO, ggsn, "Starting GGSN\n");
795
796 /* Start libgtp listener */
797 if (gtp_new(&ggsn->gsn, ggsn->cfg.state_dir, &ggsn->cfg.listen_addr.v4, GTP_MODE_GGSN)) {
798 LOGPGGSN(LOGL_ERROR, ggsn, "Failed to create GTP: %s\n", strerror(errno));
799 return -1;
800 }
801 ggsn->gsn->priv = ggsn;
802
Harald Welte98146772017-09-05 17:41:20 +0200803 /* patch in different addresses to use (in case we're behind NAT, the listen
804 * address is different from what we advertise externally) */
805 if (ggsn->cfg.gtpc_addr.v4.s_addr)
806 ggsn->gsn->gsnc = ggsn->cfg.gtpc_addr.v4;
807
808 if (ggsn->cfg.gtpu_addr.v4.s_addr)
809 ggsn->gsn->gsnu = ggsn->cfg.gtpu_addr.v4;
810
Harald Weltedda21ed2017-08-12 15:07:02 +0200811 /* Register File Descriptors */
812 osmo_fd_setup(&ggsn->gtp_fd0, ggsn->gsn->fd0, BSC_FD_READ, ggsn_gtp_fd_cb, ggsn, 0);
813 rc = osmo_fd_register(&ggsn->gtp_fd0);
814 OSMO_ASSERT(rc == 0);
815
816 osmo_fd_setup(&ggsn->gtp_fd1c, ggsn->gsn->fd1c, BSC_FD_READ, ggsn_gtp_fd_cb, ggsn, 1);
817 rc = osmo_fd_register(&ggsn->gtp_fd1c);
818 OSMO_ASSERT(rc == 0);
819
820 osmo_fd_setup(&ggsn->gtp_fd1u, ggsn->gsn->fd1u, BSC_FD_READ, ggsn_gtp_fd_cb, ggsn, 2);
821 rc = osmo_fd_register(&ggsn->gtp_fd1u);
822 OSMO_ASSERT(rc == 0);
823
Harald Weltedda21ed2017-08-12 15:07:02 +0200824 gtp_set_cb_data_ind(ggsn->gsn, encaps_tun);
825 gtp_set_cb_delete_context(ggsn->gsn, delete_context);
826 gtp_set_cb_create_context_ind(ggsn->gsn, create_context_ind);
Oliver Smith1cde2c12019-05-13 11:35:03 +0200827 gtp_set_cb_conf(ggsn->gsn, cb_conf);
Pau Espin Pedrolf5fbb412019-08-21 18:49:44 +0200828 gtp_set_cb_recovery3(ggsn->gsn, cb_recovery3);
Harald Weltedda21ed2017-08-12 15:07:02 +0200829
830 LOGPGGSN(LOGL_NOTICE, ggsn, "Successfully started\n");
831 ggsn->started = true;
832
833 llist_for_each_entry(apn, &ggsn->apn_list, list)
834 apn_start(apn);
835
836 return 0;
837}
838
839/* Stop a given GGSN */
840int ggsn_stop(struct ggsn_ctx *ggsn)
841{
842 struct apn_ctx *apn;
843
844 if (!ggsn->started)
845 return 0;
846
847 /* iterate over all APNs and stop them */
848 llist_for_each_entry(apn, &ggsn->apn_list, list)
Pau Espin Pedrol72ab4bc2019-05-29 19:08:26 +0200849 apn_stop(apn);
Harald Weltedda21ed2017-08-12 15:07:02 +0200850
Harald Weltedda21ed2017-08-12 15:07:02 +0200851 osmo_fd_unregister(&ggsn->gtp_fd1u);
852 osmo_fd_unregister(&ggsn->gtp_fd1c);
853 osmo_fd_unregister(&ggsn->gtp_fd0);
854
855 if (ggsn->gsn) {
856 gtp_free(ggsn->gsn);
857 ggsn->gsn = NULL;
858 }
859
860 ggsn->started = false;
861 return 0;
862}