PdnConnectivity.c 44.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/*
 * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The OpenAirInterface Software Alliance licenses this file to You under
 * the OAI Public License, Version 1.0  (the "License"); you may not use this file
 * except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.openairinterface.org/?page_id=698
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *-------------------------------------------------------------------------------
 * For more information about the OpenAirInterface (OAI) Software Alliance:
 *      contact@openairinterface.org
 */

gauthier's avatar
 
gauthier committed
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
/*****************************************************************************
Source      PdnConnectivity.c

Version     0.1

Date        2013/01/02

Product     NAS stack

Subsystem   EPS Session Management

Author      Frederic Maurel

Description Defines the PDN connectivity ESM procedure executed by the
        Non-Access Stratum.

        The PDN connectivity procedure is used by the UE to request
        the setup of a default EPS bearer to a PDN.

        The procedure is used either to establish the 1st default
        bearer by including the PDN CONNECTIVITY REQUEST message
        into the initial attach message, or to establish subsequent
        default bearers to additional PDNs in order to allow the UE
        simultaneous access to multiple PDNs by sending the message
        stand-alone.

*****************************************************************************/

#include <stdlib.h> // malloc, free
#include <string.h> // memset, memcpy, memcmp
#include <ctype.h>  // isprint

#include "esm_proc.h"
#include "commonDef.h"
#include "nas_log.h"

#include "esmData.h"
#include "esm_cause.h"
#include "esm_pt.h"

#include "emm_sap.h"

#if defined(ENABLE_ITTI)
# include "assertions.h"
#endif

/****************************************************************************/
/****************  E X T E R N A L    D E F I N I T I O N S  ****************/
/****************************************************************************/

/****************************************************************************/
/*******************  L O C A L    D E F I N I T I O N S  *******************/
/****************************************************************************/

/*
 * --------------------------------------------------------------------------
 *  Internal data handled by the PDN connectivity procedure in the UE
 * --------------------------------------------------------------------------
 */
/*
 * PDN connection handlers
 */
84
static int _pdn_connectivity_create(esm_data_t *esm_data, int pid, const OctetString *apn,
gauthier's avatar
 
gauthier committed
85
                                    esm_proc_pdn_type_t pdn_type, int is_emergency);
86
static int _pdn_connectivity_update(esm_data_t *esm_data, int pid, const OctetString *apn,
gauthier's avatar
 
gauthier committed
87
                                    esm_proc_pdn_type_t pdn_type, const OctetString *pdn_addr, int esm_cause);
88
static int _pdn_connectivity_delete(esm_data_t *esm_data, int pid);
gauthier's avatar
 
gauthier committed
89

90 91 92
static int _pdn_connectivity_set_pti(esm_data_t *esm_data, int pid, int pti);
static int _pdn_connectivity_find_apn(esm_data_t *esm_data, const OctetString *apn);
static int _pdn_connectivity_find_pdn(esm_data_t * esm_data, const OctetString *apn,
gauthier's avatar
 
gauthier committed
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
                                      esm_proc_pdn_type_t pdn_type);

/*
 * Timer handlers
 */
static void *_pdn_connectivity_t3482_handler(void *);

/* Maximum value of the PDN connectivity request retransmission counter */
#define ESM_PDN_CONNECTIVITY_COUNTER_MAX 5


/****************************************************************************/
/******************  E X P O R T E D    F U N C T I O N S  ******************/
/****************************************************************************/

/*
 * --------------------------------------------------------------------------
 *      PDN connectivity procedure executed by the UE
 * --------------------------------------------------------------------------
 */
/****************************************************************************
 **                                                                        **
 ** Name:    esm_proc_pdn_connectivity()                               **
 **                                                                        **
 ** Description: Defines a new PDN connection for the specified Access     **
 **      Point Name or undefines the PDN connection with the given **
 **      identifier                                                **
 **                                                                        **
 ** Inputs:  cid:       PDN connection identifier                  **
 **      is_to_define:  Indicates whether the PDN connection has   **
 **             to be defined or undefined                 **
 **      pdn_type:  PDN connection type (IPv4, IPv6, IPv4v6)   **
 **      apn:       Access Point logical Name to be used       **
 **      is_emergency:  TRUE if the PDN connection has to be esta- **
 **             blished for emergency bearer services      **
 **      Others:    None                                       **
 **                                                                        **
 ** Outputs:     pti:       Procedure transaction identity assigned to **
 **             the new PDN connection or the released PDN **
 **             connection                                 **
 **      Return:    RETURNok, RETURNerror                      **
 **                                                                        **
 ***************************************************************************/
136
int esm_proc_pdn_connectivity(nas_user_t *user, int cid, int is_to_define,
gauthier's avatar
 
gauthier committed
137 138 139 140 141 142 143 144
                              esm_proc_pdn_type_t pdn_type,
                              const OctetString *apn, int is_emergency,
                              unsigned int *pti)
{
  LOG_FUNC_IN;

  int rc = RETURNerror;
  int pid = cid - 1;
145 146
  esm_data_t *esm_data = user-> esm_data;
  esm_pt_data_t *esm_pt_data = user-> esm_pt_data;
gauthier's avatar
 
gauthier committed
147 148 149 150

  if (!is_to_define) {
    LOG_TRACE(INFO, "ESM-PROC  - Undefine PDN connection (cid=%d)", cid);
    /* Delete the PDN connection entry */
151
    int pti = _pdn_connectivity_delete(esm_data, pid);
gauthier's avatar
 
gauthier committed
152 153 154

    if (pti != ESM_PT_UNASSIGNED) {
      /* Release the procedure transaction data */
155
      rc = esm_pt_release(esm_pt_data, pti);
gauthier's avatar
 
gauthier committed
156 157 158 159 160 161 162
    }

    LOG_FUNC_RETURN(rc);
  } else if (pti != NULL) {
    LOG_TRACE(INFO, "ESM-PROC  - Assign new procedure transaction identity "
              "(cid=%d)", cid);
    /* Assign new procedure transaction identity */
163
    *pti = esm_pt_assign(esm_pt_data);
gauthier's avatar
 
gauthier committed
164 165 166 167 168 169 170 171

    if (*pti == ESM_PT_UNASSIGNED) {
      LOG_TRACE(WARNING, "ESM-PROC  - Failed to assign new procedure "
                "transaction identity");
      LOG_FUNC_RETURN (RETURNerror);
    }

    /* Update the PDN connection data */
172
    rc = _pdn_connectivity_set_pti(esm_data, pid, *pti);
gauthier's avatar
 
gauthier committed
173 174 175 176 177 178 179 180 181 182 183 184 185

    if (rc != RETURNok) {
      LOG_TRACE(WARNING, "ESM-PROC  - Failed to update PDN connection");
    }

    LOG_FUNC_RETURN (rc);
  }

  LOG_TRACE(INFO,"ESM-PROC  - Define new %s PDN connection to APN %s (cid=%d)",
            (pdn_type == ESM_PDN_TYPE_IPV4)? "IPv4" :
            (pdn_type == ESM_PDN_TYPE_IPV6)? "IPv6" : "IPv4v6",
            apn->value, cid);

186
  if (is_emergency && esm_data->emergency) {
gauthier's avatar
 
gauthier committed
187 188 189 190 191 192
    /* The UE shall not request additional PDN connection for
     * emergency bearer services */
    LOG_TRACE(WARNING, "ESM-PROC  - PDN connection for emergency bearer "
              "services is already active");
    LOG_FUNC_RETURN (RETURNerror);
  } else if (pid < ESM_DATA_PDN_MAX) {
193
    if ((pid == esm_data->pdn[pid].pid) && (esm_data->pdn[pid].is_active)) {
gauthier's avatar
 
gauthier committed
194 195 196 197 198 199 200 201 202 203 204
      /* PDN connection with the specified identifier is active */
      LOG_TRACE(WARNING, "ESM-PROC  - PDN connection is active");
      LOG_FUNC_RETURN (RETURNerror);
    }
  } else {
    LOG_TRACE(WARNING, "ESM-PROC  - PDN connection identifier is not valid");
    LOG_FUNC_RETURN (RETURNerror);
  }

  if (apn && apn->length > 0) {
    /* The UE requested subsequent connectivity to additionnal PDNs */
205
    int pid = _pdn_connectivity_find_apn(esm_data, apn);
gauthier's avatar
 
gauthier committed
206

207
    if ( (pid >= 0) && esm_data->pdn[pid].is_active ) {
gauthier's avatar
 
gauthier committed
208
      /* An active PDN connection to this APN already exists */
209 210
      if ( (esm_data->pdn[pid].data->type != ESM_PDN_TYPE_IPV4V6) &&
           (esm_data->pdn[pid].data->type != pdn_type) ) {
gauthier's avatar
 
gauthier committed
211 212
        /* The UE is requesting PDN connection for other IP version
         * than the one already activated */
213
        if (!esm_data->pdn[pid].data->addr_realloc) {
gauthier's avatar
 
gauthier committed
214 215 216 217 218 219 220 221 222 223
          /* The network does not allow PDN connectivity using
           * IPv4 and IPv6 address versions to the same APN */
          if (pdn_type != ESM_PDN_TYPE_IPV4V6) {
            LOG_TRACE(WARNING, "ESM-PROC  - %s PDN connectivity to "
                      "%s is not allowed by the network",
                      (pdn_type != ESM_PDN_TYPE_IPV4)? "IPv6" :
                      "IPv4", apn->value);
          } else {
            LOG_TRACE(WARNING, "ESM-PROC  - %s PDN connection to %s "
                      "already exists",
224
                      (esm_data->pdn[pid].data->type !=
gauthier's avatar
 
gauthier committed
225 226 227 228 229 230 231 232 233 234 235
                       ESM_PDN_TYPE_IPV4)? "IPv6" : "IPv4",
                      apn->value);
          }

          LOG_FUNC_RETURN (RETURNerror);
        }
      } else {
        /* The UE is requesting PDN connection to this APN using the
         * same IP version than the one already activated */
        LOG_TRACE(WARNING, "ESM-PROC  - %s PDN connection to %s "
                  "already exists",
236 237
                  (esm_data->pdn[pid].data->type != ESM_PDN_TYPE_IPV4)?
                  (esm_data->pdn[pid].data->type != ESM_PDN_TYPE_IPV6)?
gauthier's avatar
 
gauthier committed
238 239 240 241 242 243 244 245 246 247 248 249 250 251
          "IPv4v6" : "IPv6" : "IPv4", apn->value);
        LOG_FUNC_RETURN (RETURNerror);
      }
    }
  }

  /*
   * New PDN context has to be defined to allow connectivity to an APN:
   * The UE may attempt to attach to the network using the default APN,
   * or request PDN connectivity to emergency bearer services. The UE
   * may also subsequently request connectivity to additional PDNs if
   * not already established, or may have been allowed to request PDN
   * connectivity for other IP version than the one already activated
   */
252
  rc = _pdn_connectivity_create(esm_data, pid, apn, pdn_type, is_emergency);
gauthier's avatar
 
gauthier committed
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282
  LOG_FUNC_RETURN(rc);
}

/****************************************************************************
 **                                                                        **
 ** Name:    esm_proc_pdn_connectivity_request()                       **
 **                                                                        **
 ** Description: Initiates PDN connectivity procedure to request setup of  **
 **      a default EPS bearer to a PDN.                            **
 **                                                                        **
 **              3GPP TS 24.301, section 6.5.1.2                           **
 **      The UE requests connectivity to an additional PDN by sen- **
 **      ding a PDN CONNECTIVITY REQUEST message to the MME, star- **
 **      ting timer T3482 and entering state PROCEDURE TRANSACTION **
 **      PENDING.                                                  **
 **                                                                        **
 ** Inputs:  is_standalone: Indicates whether the PDN connectivity     **
 **             procedure is initiated as part of the at-  **
 **             tach procedure                             **
 **      pti:       Procedure transaction identity             **
 **      msg:       Encoded PDN connectivity request message   **
 **             to be sent                                 **
 **      sent_by_ue:    Not used - Always TRUE                     **
 **      Others:    None                                       **
 **                                                                        **
 ** Outputs:     None                                                      **
 **      Return:    RETURNok, RETURNerror                      **
 **      Others:    None                                       **
 **                                                                        **
 ***************************************************************************/
283
int esm_proc_pdn_connectivity_request(nas_user_t *user, int is_standalone, int pti,
gauthier's avatar
 
gauthier committed
284 285 286
                                      OctetString *msg, int sent_by_ue)
{
  LOG_FUNC_IN;
287
  esm_pt_data_t *esm_pt_data = user->esm_pt_data;
gauthier's avatar
 
gauthier committed
288 289 290 291 292 293 294 295 296 297 298
  int rc = RETURNok;

  LOG_TRACE(INFO, "ESM-PROC  - Initiate PDN connectivity (pti=%d)", pti);

  if (is_standalone) {
    emm_sap_t emm_sap;
    emm_esm_data_t *emm_esm = &emm_sap.u.emm_esm.u.data;
    /*
     * Notity EMM that ESM PDU has to be forwarded to lower layers
     */
    emm_sap.primitive = EMMESM_UNITDATA_REQ;
Frédéric Leroy's avatar
Frédéric Leroy committed
299
    emm_sap.u.emm_esm.ueid = user->ueid;
gauthier's avatar
 
gauthier committed
300 301
    emm_esm->msg.length = msg->length;
    emm_esm->msg.value = msg->value;
302
    rc = emm_sap_send(user, &emm_sap);
gauthier's avatar
 
gauthier committed
303 304 305

    if (rc != RETURNerror) {
      /* Start T3482 retransmission timer */
306
      rc = esm_pt_start_timer(user, pti, msg, T3482_DEFAULT_VALUE,
gauthier's avatar
 
gauthier committed
307 308 309 310 311 312
                              _pdn_connectivity_t3482_handler);
    }
  }

  if (rc != RETURNerror) {
    /* Set the procedure transaction state to PENDING */
313
    rc = esm_pt_set_status(esm_pt_data, pti, ESM_PT_PENDING);
gauthier's avatar
 
gauthier committed
314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349

    if (rc != RETURNok) {
      /* The procedure transaction was already in PENDING state */
      LOG_TRACE(WARNING, "ESM-PROC  - PTI %d was already PENDING", pti);
    }
  }

  LOG_FUNC_RETURN(rc);
}

/****************************************************************************
 **                                                                        **
 ** Name:    esm_proc_pdn_connectivity_accept()                        **
 **                                                                        **
 ** Description: Performs PDN connectivity procedure accepted by the net-  **
 **      work.                                                     **
 **                                                                        **
 **              3GPP TS 24.301, section 6.5.1.3                           **
 **      The UE shall stop timer T3482 and enter the state PROCE-  **
 **      DURE TRANSACTION INACTIVE.                                **
 **                                                                        **
 ** Inputs:  pti:       Identifies the UE requested PDN connecti-  **
 **             vity procedure accepted by the network     **
 **      pdn_type:  PDN type value (IPv4, IPv6, IPv4v6)        **
 **      pdn_addr:  PDN address                                **
 **      apn:       Access Point Name of the PDN connection    **
 **      Others:    None                                       **
 **                                                                        **
 ** Outputs:     esm_cause: Cause code returned upon ESM procedure     **
 **             failure                                    **
 **      Return:    The identifier of the PDN connection when  **
 **             successfully updated;                      **
 **             RETURNerror otherwise.                     **
 **      Others:    None                                       **
 **                                                                        **
 ***************************************************************************/
350
int esm_proc_pdn_connectivity_accept(nas_user_t *user, int pti, esm_proc_pdn_type_t pdn_type,
gauthier's avatar
 
gauthier committed
351 352 353 354
                                     const OctetString *pdn_addr,
                                     const OctetString *apn, int *esm_cause)
{
  LOG_FUNC_IN;
355
  esm_data_t *esm_data  = user->esm_data;
356
  esm_pt_data_t *esm_pt_data = user->esm_pt_data;
gauthier's avatar
 
gauthier committed
357 358 359
  int     rc;
  int     pid = RETURNerror;
  char    apn_first_char[4];
360
  char    str[128];
gauthier's avatar
 
gauthier committed
361 362 363 364 365 366 367 368 369

  if (isprint(apn->value[0])) {
    apn_first_char[0] = '\0';
  } else {
    sprintf (apn_first_char, "%02X", apn->value[0]);
  }

  LOG_TRACE(INFO, "ESM-PROC  - PDN connectivity accepted by the network "
            "(pti=%d) APN = %s\"%s\", IP address = %s", pti, apn_first_char, isprint(apn->value[0]) ? &apn->value[0] : &apn->value[1],
370 371 372
            (pdn_type == ESM_PDN_TYPE_IPV4)? esm_data_get_ipv4_addr(pdn_addr, str) :
            (pdn_type == ESM_PDN_TYPE_IPV6)? esm_data_get_ipv6_addr(pdn_addr, str) :
            esm_data_get_ipv4v6_addr(pdn_addr, str));
gauthier's avatar
 
gauthier committed
373 374

  /* Stop T3482 timer if running */
375
  esm_pt_stop_timer(esm_pt_data, pti);
gauthier's avatar
 
gauthier committed
376
  /* Set the procedure transaction state to INACTIVE */
377
  rc = esm_pt_set_status(esm_pt_data, pti, ESM_PT_INACTIVE);
gauthier's avatar
 
gauthier committed
378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396

  if (rc != RETURNok) {
    /* The procedure transaction was already in INACTIVE state
     * as the request may have already been accepted; consider
     * this request message with same PTI as a network re-
     * transmission */
    LOG_TRACE(WARNING, "ESM-PROC  - PTI %d network retransmission", pti);
    *esm_cause = ESM_CAUSE_PTI_ALREADY_IN_USE;
  } else {
    /* XXX - 3GPP TS 24.301, section 6.5.1.3 and 7.3.1
     * The UE should ensure that the procedure transaction identity
     * (PTI) assigned to this procedure is not released immediately.
     * While the PTI value is not released, the UE regards any received
     * ACTIVATE DEFAULT EPS BEARER CONTEXT REQUEST message with the same
     * PTI value as a network retransmission.
     * The way to achieve this is implementation dependent.
     */

    /* Check whether a PDN connection exists to this APN */
397
    pid = _pdn_connectivity_find_pdn(esm_data, apn, pdn_type);
gauthier's avatar
 
gauthier committed
398 399 400 401 402 403 404 405 406 407 408

    if (pid < 0) {
      /* No any PDN connection has been defined to establish connectivity
       * to this APN */
      LOG_TRACE(WARNING, "ESM-PROC  - PDN connection entry for "
                "APN \"%s\" (type=%d) not found", apn->value, pdn_type);
      *esm_cause = ESM_CAUSE_UNKNOWN_ACCESS_POINT_NAME;
      LOG_FUNC_RETURN(RETURNerror);
    }

    /* Update the PDN connection */
409
    rc = _pdn_connectivity_update(esm_data, pid, apn, pdn_type, pdn_addr, *esm_cause);
gauthier's avatar
 
gauthier committed
410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443

    if (rc != RETURNok) {
      LOG_TRACE(WARNING, "ESM-PROC  - Failed to update PDN connection "
                "(pid=%d)", pid);
      *esm_cause = ESM_CAUSE_REQUEST_REJECTED_UNSPECIFIED;
      LOG_FUNC_RETURN(RETURNerror);
    }
  }

  LOG_FUNC_RETURN (pid);
}

/****************************************************************************
 **                                                                        **
 ** Name:    esm_proc_pdn_connectivity_reject()                        **
 **                                                                        **
 ** Description: Performs PDN connectivity procedure not accepted by the   **
 **      network.                                                  **
 **                                                                        **
 **              3GPP TS 24.301, section 6.5.1.4                           **
 **      Upon receipt of the PDN CONNECTIVITY REJECT message, the  **
 **      UE shall stop timer T3482 and enter the state PROCEDURE   **
 **      TRANSACTION INACTIVE.                                     **
 **                                                                        **
 ** Inputs:  pti:       Identifies the UE requested PDN connecti-  **
 **             vity procedure rejected by the network     **
 **      Others:    None                                       **
 **                                                                        **
 ** Outputs:     esm_cause: Cause code returned upon ESM procedure     **
 **             failure                                    **
 **      Return:    RETURNok, RETURNerror                      **
 **      Others:    None                                       **
 **                                                                        **
 ***************************************************************************/
444
int esm_proc_pdn_connectivity_reject(nas_user_t *user, int pti, int *esm_cause)
gauthier's avatar
 
gauthier committed
445 446
{
  LOG_FUNC_IN;
447
  esm_pt_data_t *esm_pt_data = user->esm_pt_data;
gauthier's avatar
 
gauthier committed
448 449 450 451 452 453
  int rc;

  LOG_TRACE(WARNING, "ESM-PROC  - PDN connectivity rejected by "
            "the network (pti=%d), ESM cause = %d", pti, *esm_cause);

  /* Stop T3482 timer if running */
454
  (void) esm_pt_stop_timer(esm_pt_data, pti);
gauthier's avatar
 
gauthier committed
455
  /* Set the procedure transaction state to INACTIVE */
456
  rc = esm_pt_set_status(esm_pt_data, pti, ESM_PT_INACTIVE);
gauthier's avatar
 
gauthier committed
457 458 459 460 461 462 463

  if (rc != RETURNok) {
    /* The procedure transaction was already in INACTIVE state */
    LOG_TRACE(WARNING, "ESM-PROC  - PTI %d was already INACTIVE", pti);
    *esm_cause = ESM_CAUSE_MESSAGE_TYPE_NOT_COMPATIBLE;
  } else {
    /* Release the procedure transaction identity */
464
    rc = esm_pt_release(user->esm_pt_data, pti);
gauthier's avatar
 
gauthier committed
465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494

    if (rc != RETURNok) {
      LOG_TRACE(WARNING, "ESM-PROC  - Failed to release PTI %d", pti);
      *esm_cause = ESM_CAUSE_REQUEST_REJECTED_UNSPECIFIED;
    }
  }

  LOG_FUNC_RETURN(rc);
}

/****************************************************************************
 **                                                                        **
 ** Name:    esm_proc_pdn_connectivity_complete()                      **
 **                                                                        **
 ** Description: Terminates the PDN connectivity procedure upon receiving  **
 **      indication from the EPS Mobility Management sublayer that **
 **      the ACTIVATE DEFAULT EPS BEARER CONTEXT ACCEPT message    **
 **      has been successfully delivered to the MME.               **
 **                                                                        **
 **      The UE releases the transaction identity assigned to this **
 **      PDN connectivity procedure.                               **
 **                                                                        **
 ** Inputs:  None                                                      **
 **      Others:    None                                       **
 **                                                                        **
 ** Outputs:     None                                                      **
 **      Return:    RETURNok, RETURNerror                      **
 **      Others:    None                                       **
 **                                                                        **
 ***************************************************************************/
495
int esm_proc_pdn_connectivity_complete(nas_user_t *user)
gauthier's avatar
 
gauthier committed
496 497
{
  LOG_FUNC_IN;
498
  esm_pt_data_t *esm_pt_data = user->esm_pt_data;
gauthier's avatar
 
gauthier committed
499 500 501 502 503 504
  int rc = RETURNerror;

  LOG_TRACE(INFO, "ESM-PROC  - PDN connectivity complete");

  /* Get the procedure transaction identity assigned to the PDN connection
   * entry which is still pending in the inactive state */
505
  int pti = esm_pt_get_pending_pti(esm_pt_data, ESM_PT_INACTIVE);
gauthier's avatar
 
gauthier committed
506 507 508

  if (pti != ESM_PT_UNASSIGNED) {
    /* Release the procedure transaction identity */
509
    rc = esm_pt_release(esm_pt_data, pti);
gauthier's avatar
 
gauthier committed
510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535
  }

  LOG_FUNC_RETURN(rc);
}

/****************************************************************************
 **                                                                        **
 ** Name:    esm_proc_pdn_connectivity_failure()                       **
 **                                                                        **
 ** Description: Performs PDN connectivity procedure upon receiving trans- **
 **      mission failure of ESM message indication from the EPS    **
 **      Mobility Management sublayer                              **
 **                                                                        **
 **      The UE releases the transaction identity allocated to the **
 **      PDN connectivity procedure which is still pending in the  **
 **      PROCEDURE TRANSACTION INACTIVE or PENDING state.          **
 **                                                                        **
 ** Inputs:  is_pending:    TRUE if this PDN connectivity procedure    **
 **             transaction is in the PENDING state        **
 **      Others:    None                                       **
 **                                                                        **
 ** Outputs:     None                                                      **
 **      Return:    RETURNok, RETURNerror                      **
 **      Others:    None                                       **
 **                                                                        **
 ***************************************************************************/
536
int esm_proc_pdn_connectivity_failure(nas_user_t *user, int is_pending)
gauthier's avatar
 
gauthier committed
537 538
{
  LOG_FUNC_IN;
539
  esm_pt_data_t *esm_pt_data = user->esm_pt_data;
gauthier's avatar
 
gauthier committed
540 541 542 543 544 545 546 547 548
  int rc;
  int pti;

  LOG_TRACE(WARNING, "ESM-PROC  - PDN connectivity failure in state %s",
            (is_pending)? "PENDING" : "INACTIVE");

  if (is_pending) {
    /* Get the procedure transaction identity assigned to the pending PDN
     * connection entry */
549
    pti = esm_pt_get_pending_pti(esm_pt_data, ESM_PT_PENDING);
gauthier's avatar
 
gauthier committed
550 551 552 553 554 555 556

    if (pti == ESM_PT_UNASSIGNED) {
      LOG_TRACE(ERROR, "ESM-PROC  - No procedure transaction is PENDING");
      return (RETURNerror);
    }

    /* Set the procedure transaction state to INACTIVE */
557
    (void) esm_pt_set_status(esm_pt_data, pti, ESM_PT_INACTIVE);
gauthier's avatar
 
gauthier committed
558 559 560
  } else {
    /* Get the procedure transaction identity assigned to the PDN
     * connection entry which is still pending in the inactive state */
561
    pti = esm_pt_get_pending_pti(esm_pt_data, ESM_PT_INACTIVE);
gauthier's avatar
 
gauthier committed
562 563 564
  }

  /* Release the procedure transaction identity */
565
  rc = esm_pt_release(esm_pt_data, pti);
gauthier's avatar
 
gauthier committed
566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608

  if (rc != RETURNok) {
    LOG_TRACE(WARNING, "ESM-PROC  - Failed to release PTI %d", pti);
  }

  LOG_FUNC_RETURN(rc);
}



/****************************************************************************/
/*********************  L O C A L    F U N C T I O N S  *********************/
/****************************************************************************/

/*
 *---------------------------------------------------------------------------
 *              Timer handlers
 *---------------------------------------------------------------------------
 */

/****************************************************************************
 **                                                                        **
 ** Name:    _pdn_connectivity_t3482_handler()                         **
 **                                                                        **
 ** Description: T3482 timeout handler                                     **
 **                                                                        **
 **              3GPP TS 24.301, section 6.5.1.5, case a                   **
 **      On the first expiry of the timer T3482, the UE shall re-  **
 **      send the PDN CONNECTIVITY REQUEST and shall reset and re- **
 **      start timer T3482. This retransmission is repeated four   **
 **      times, i.e. on the fifth expiry of timer T3482, the UE    **
 **      shall abort the procedure, release the PTI allocated for  **
 **      this invocation and enter the state PROCEDURE TRANSACTION **
 **      INACTIVE.                                                 **
 **                                                                        **
 ** Inputs:  args:      handler parameters                         **
 **      Others:    None                                       **
 **                                                                        **
 ** Outputs:     None                                                      **
 **      Return:    None                                       **
 **      Others:    None                                       **
 **                                                                        **
 ***************************************************************************/
609
// FIXME 
gauthier's avatar
 
gauthier committed
610 611 612 613 614 615 616
static void *_pdn_connectivity_t3482_handler(void *args)
{
  LOG_FUNC_IN;

  int rc;

  /* Get retransmission timer parameters data */
617 618
  esm_pt_timer_data_t *data = args;
  nas_user_t *user = data->user;
619
  esm_pt_data_t *esm_pt_data = user->esm_pt_data;
gauthier's avatar
 
gauthier committed
620 621 622 623 624 625 626 627 628 629 630 631 632 633 634

  /* Increment the retransmission counter */
  data->count += 1;

  LOG_TRACE(WARNING, "ESM-PROC  - T3482 timer expired (pti=%d), "
            "retransmission counter = %d", data->pti, data->count);

  if (data->count < ESM_PDN_CONNECTIVITY_COUNTER_MAX) {
    emm_sap_t emm_sap;
    emm_esm_data_t *emm_esm = &emm_sap.u.emm_esm.u.data;
    /*
     * Notify EMM that the PDN connectivity request message
     * has to be sent again
     */
    emm_sap.primitive = EMMESM_UNITDATA_REQ;
Frédéric Leroy's avatar
Frédéric Leroy committed
635
    emm_sap.u.emm_esm.ueid = user->ueid;
gauthier's avatar
 
gauthier committed
636 637
    emm_esm->msg.length = data->msg.length;
    emm_esm->msg.value = data->msg.value;
638
    rc = emm_sap_send(user, &emm_sap);
gauthier's avatar
 
gauthier committed
639 640 641

    if (rc != RETURNerror) {
      /* Restart the timer T3482 */
642
      rc = esm_pt_start_timer(user, data->pti, &data->msg, T3482_DEFAULT_VALUE,
gauthier's avatar
 
gauthier committed
643 644 645 646
                              _pdn_connectivity_t3482_handler);
    }
  } else {
    /* Set the procedure transaction state to INACTIVE */
647
    rc = esm_pt_set_status(esm_pt_data, data->pti, ESM_PT_INACTIVE);
gauthier's avatar
 
gauthier committed
648 649 650 651 652 653 654

    if (rc != RETURNok) {
      /* The procedure transaction was already in INACTIVE state */
      LOG_TRACE(WARNING, "ESM-PROC  - PTI %d was already INACTIVE",
                data->pti);
    } else {
      /* Release the transaction identity assigned to this procedure */
655
      rc = esm_pt_release(esm_pt_data, data->pti);
gauthier's avatar
 
gauthier committed
656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689

      if (rc != RETURNok) {
        LOG_TRACE(WARNING, "ESM-PROC  - Failed to release PTI %d",
                  data->pti);
      }
    }
  }

  LOG_FUNC_RETURN(NULL);
}

/*
 *---------------------------------------------------------------------------
 *              PDN connection handlers
 *---------------------------------------------------------------------------
 */

/****************************************************************************
 **                                                                        **
 ** Name:    _pdn_connectivity_create()                                **
 **                                                                        **
 ** Description: Creates a new PDN connection entry or updates existing    **
 **      non-active PDN connection entry                           **
 **                                                                        **
 ** Inputs:  pid:       Identifier of the PDN connection entry     **
 **      apn:       Access Point Name of the PDN connection    **
 **      pdn_type:  PDN type (IPv4, IPv6, IPv4v6)              **
 **      is_emergency:  TRUE if the PDN connection has to be esta- **
 **             blished for emergency bearer services      **
 **                                                                        **
 ** Outputs:     None                                                      **
 **      Return:    RETURNok, RETURNerror                      **
 **                                                                        **
 ***************************************************************************/
690
static int _pdn_connectivity_create(esm_data_t *esm_data, int pid, const OctetString *apn,
gauthier's avatar
 
gauthier committed
691 692 693 694 695 696 697 698 699
                                    esm_proc_pdn_type_t pdn_type,
                                    int is_emergency)
{
  esm_pdn_t *pdn = NULL;

  LOG_TRACE(INFO, "ESM-PROC  - Create new PDN connection (pid=%d)", pid);

  if (pid >= ESM_DATA_PDN_MAX) {
    return (RETURNerror);
700
  } else if (esm_data->pdn[pid].is_active) {
gauthier's avatar
 
gauthier committed
701 702 703 704
    LOG_TRACE(ERROR, "ESM-PROC  - PDN connection is active");
    return (RETURNerror);
  }

705
  if (esm_data->pdn[pid].data != NULL) {
gauthier's avatar
 
gauthier committed
706
    /* Update existing non-active PDN connection */
707
    pdn = esm_data->pdn[pid].data;
gauthier's avatar
 
gauthier committed
708 709 710 711 712 713 714 715 716 717 718 719
  } else {
    /* Create new PDN connection */
    pdn = (esm_pdn_t *)malloc(sizeof(esm_pdn_t));

    if (pdn == NULL) {
      LOG_TRACE(WARNING, "ESM-PROC  - "
                "Failed to create new PDN connection");
      return (RETURNerror);
    }

    memset(pdn, 0, sizeof(esm_pdn_t));
    /* Increment the number of PDN connections */
720
    esm_data->n_pdns += 1;
gauthier's avatar
 
gauthier committed
721
    /* Set the PDN connection identifier */
722
    esm_data->pdn[pid].pid = pid;
gauthier's avatar
 
gauthier committed
723
    /* Reset the PDN connection active indicator */
724
    esm_data->pdn[pid].is_active = FALSE;
gauthier's avatar
 
gauthier committed
725
    /* Setup the PDN connection data */
726
    esm_data->pdn[pid].data = pdn;
gauthier's avatar
 
gauthier committed
727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768
  }

  /* Update the PDN connection data */
  pdn->is_emergency = is_emergency;

  if ( apn && (apn->length > 0) ) {
    if (pdn->apn.length > 0) {
      free(pdn->apn.value);
      pdn->apn.length = 0;
    }

    pdn->apn.value = (uint8_t *)malloc(apn->length + 1);

    if (pdn->apn.value) {
      pdn->apn.length = apn->length;
      memcpy(pdn->apn.value, apn->value, apn->length);
      pdn->apn.value[pdn->apn.length] = '\0';
    }
  }

  pdn->type = pdn_type;
  pdn->addr_realloc = FALSE;

  return (RETURNok);
}

/****************************************************************************
 **                                                                        **
 ** Name:    _pdn_connectivity_update()                                **
 **                                                                        **
 ** Description: Updates PDN connection entry with the given identifier    **
 **                                                                        **
 ** Inputs:  pid:       Identifier of the PDN connection entry     **
 **      pdn_type:  PDN type (IPv4, IPv6, IPv4v6)              **
 **      pdn_addr:  Network allocated PDN IPv4 or IPv6 address **
 **      esm_cause: ESM cause code                             **
 **      Others:    None                                       **
 **                                                                        **
 ** Outputs:     None                                                      **
 **      Return:    RETURNok, RETURNerror                      **
 **                                                                        **
 ***************************************************************************/
769
static int _pdn_connectivity_update(esm_data_t *esm_data, int pid, const OctetString *apn,
gauthier's avatar
 
gauthier committed
770 771 772 773 774 775 776 777
                                    esm_proc_pdn_type_t pdn_type,
                                    const OctetString *pdn_addr,
                                    int esm_cause)
{
  LOG_TRACE(INFO, "ESM-PROC  - Update PDN connection (pid=%d)", pid);

  if (pid >= ESM_DATA_PDN_MAX) {
    return (RETURNerror);
778
  } else if (pid != esm_data->pdn[pid].pid) {
gauthier's avatar
 
gauthier committed
779 780
    LOG_TRACE(ERROR, "ESM-PROC  - PDN connection identifier is not valid");
    return (RETURNerror);
781
  } else if (esm_data->pdn[pid].data == NULL) {
gauthier's avatar
 
gauthier committed
782 783
    LOG_TRACE(ERROR, "ESM-PROC  - PDN connection has not been allocated");
    return (RETURNerror);
784
  } else if (esm_data->pdn[pid].is_active) {
gauthier's avatar
 
gauthier committed
785
    LOG_TRACE(WARNING, "ESM-PROC  - Active %s PDN connection to %s already "
786 787
              "exists", (esm_data->pdn[pid].data->type != ESM_PDN_TYPE_IPV4)?
              "IPv6" : "IPv4", esm_data->pdn[pid].data->apn.value);
gauthier's avatar
 
gauthier committed
788 789 790 791
    return (RETURNerror);
  }

  /* Get the PDN connection */
792
  esm_pdn_t *pdn = esm_data->pdn[pid].data;
gauthier's avatar
 
gauthier committed
793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860

  /* Setup the Access Point Name value */
  if ( apn && (apn->length > 0) ) {
    if (pdn->apn.length > 0) {
      free(pdn->apn.value);
      pdn->apn.length = 0;
    }

    pdn->apn.value = (uint8_t *)malloc(apn->length + 1);

    if (pdn->apn.value) {
      pdn->apn.length = apn->length;
      memcpy(pdn->apn.value, apn->value, apn->length);
      pdn->apn.value[pdn->apn.length] = '\0';
    }
  }

  /* Setup the IP address allocated by the network */
  if ( pdn_addr && (pdn_addr->length > 0) ) {
    int length = ((pdn_addr->length < ESM_DATA_IP_ADDRESS_SIZE) ?
                  pdn_addr->length : ESM_DATA_IP_ADDRESS_SIZE);
    memcpy(pdn->ip_addr, pdn_addr->value, length);
    pdn->type = pdn_type;
  }

  /*
   * 3GPP TS 24.301, section 6.2.2
   * Update the address re-allocation indicator
   */
  if (esm_cause == ESM_CAUSE_SINGLE_ADDRESS_BEARERS_ONLY_ALLOWED) {
    /* The UE requested IPv4 or IPv6 address and the network allows
     * single addressing per bearer:
     * The UE should subsequently request another PDN connection for
     * the other IP version using the UE requested PDN connectivity
     * procedure to the same APN with a single address PDN type
     * (IPv4 or IPv6) other than the one already activated */
    pdn->addr_realloc = TRUE;
  } else if ( (esm_cause == ESM_CAUSE_PDN_TYPE_IPV4_ONLY_ALLOWED) ||
              (esm_cause == ESM_CAUSE_PDN_TYPE_IPV6_ONLY_ALLOWED) ) {
    /* The UE requested IPv4 or IPv6 address and the network allows
     * IPv4 or IPv6 PDN address only:
     * The UE shall not subsequently initiate another UE requested
     * PDN connectivity procedure to the same APN to obtain a PDN
     * type different from the one allowed by the network */
    pdn->addr_realloc = FALSE;
  } else if (pdn_type != ESM_PDN_TYPE_IPV4V6) {
    pdn->addr_realloc = TRUE;
  }

  return (RETURNok);
}

/****************************************************************************
 **                                                                        **
 ** Name:    _pdn_connectivity_delete()                                **
 **                                                                        **
 ** Description: Deletes the PDN connection entry with given identifier    **
 **                                                                        **
 ** Inputs:  pid:       Identifier of the PDN connection to be     **
 **             released                                   **
 **                                                                        **
 ** Outputs:     None                                                      **
 **      Return:    The identity of the procedure transaction  **
 **             assigned to the PDN connection when suc-   **
 **             cessfully released;                        **
 **             UNASSIGNED value otherwise.                **
 **                                                                        **
 ***************************************************************************/
861
static int _pdn_connectivity_delete(esm_data_t *esm_data, int pid)
gauthier's avatar
 
gauthier committed
862 863 864 865
{
  int pti = ESM_PT_UNASSIGNED;

  if (pid < ESM_DATA_PDN_MAX) {
866
    if (pid != esm_data->pdn[pid].pid) {
gauthier's avatar
 
gauthier committed
867 868
      LOG_TRACE(ERROR,
                "ESM-PROC  - PDN connection identifier is not valid");
869
    } else if (esm_data->pdn[pid].data == NULL) {
gauthier's avatar
 
gauthier committed
870 871
      LOG_TRACE(ERROR,
                "ESM-PROC  - PDN connection has not been allocated");
872
    } else if (esm_data->pdn[pid].is_active) {
gauthier's avatar
 
gauthier committed
873 874 875 876
      LOG_TRACE(ERROR, "ESM-PROC  - PDN connection is active");
    } else {
      /* Get the identity of the procedure transaction that created
       * the PDN connection */
877
      pti = esm_data->pdn[pid].data->pti;
gauthier's avatar
 
gauthier committed
878 879 880 881 882
    }
  }

  if (pti != ESM_PT_UNASSIGNED) {
    /* Decrement the number of PDN connections */
883
    esm_data->n_pdns -= 1;
gauthier's avatar
 
gauthier committed
884
    /* Set the PDN connection as available */
885
    esm_data->pdn[pid].pid = -1;
gauthier's avatar
 
gauthier committed
886 887

    /* Release allocated PDN connection data */
888 889
    if (esm_data->pdn[pid].data->apn.length > 0) {
      free(esm_data->pdn[pid].data->apn.value);
gauthier's avatar
 
gauthier committed
890 891
    }

892 893
    free(esm_data->pdn[pid].data);
    esm_data->pdn[pid].data = NULL;
gauthier's avatar
 
gauthier committed
894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915
    LOG_TRACE(WARNING, "ESM-PROC  - PDN connection %d released", pid);
  }

  /* Return the procedure transaction identity */
  return (pti);
}

/****************************************************************************
 **                                                                        **
 ** Name:    _pdn_connectivity_set_pti()                               **
 **                                                                        **
 ** Description: Update the procedure transaction identity assigned to the **
 **      PDN connection entry with the given identifier            **
 **                                                                        **
 ** Inputs:  pid:       PDN connection identifier                  **
 **      pti:       Procedure transaction identity             **
 **      Others:    None                                       **
 **                                                                        **
 ** Outputs:     None                                                      **
 **      Return:    RETURNok, RETURNerror                      **
 **                                                                        **
 ***************************************************************************/
916
static int _pdn_connectivity_set_pti(esm_data_t *esm_data, int pid, int pti)
gauthier's avatar
 
gauthier committed
917 918
{
  if (pid < ESM_DATA_PDN_MAX) {
919
    if (pid != esm_data->pdn[pid].pid) {
gauthier's avatar
 
gauthier committed
920 921
      LOG_TRACE(ERROR,
                "ESM-PROC  - PDN connection identifier is not valid");
922
    } else if (esm_data->pdn[pid].data == NULL) {
gauthier's avatar
 
gauthier committed
923 924
      LOG_TRACE(ERROR,
                "ESM-PROC  - PDN connection has not been allocated");
925
    } else if (esm_data->pdn[pid].is_active) {
gauthier's avatar
 
gauthier committed
926 927 928 929
      LOG_TRACE(ERROR, "ESM-PROC  - PDN connection is active");
    } else {
      /* Update the identity of the procedure transaction assigned to
       * the PDN connection */
930
      esm_data->pdn[pid].data->pti = pti;
gauthier's avatar
 
gauthier committed
931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952
      return (RETURNok);
    }
  }

  return (RETURNerror);
}

/****************************************************************************
 **                                                                        **
 ** Name:    _pdn_connectivity_find_apn()                              **
 **                                                                        **
 ** Description: Search the list of PDN connections for an entry defined   **
 **      for the specified APN                                     **
 **                                                                        **
 ** Inputs:  apn:       Access Point Name of the PDN connection    **
 **                                                                        **
 ** Outputs:     None                                                      **
 **      Return:    The identifier of the PDN connection if    **
 **             found in the list; -1 otherwise.           **
 **      Others:    None                                       **
 **                                                                        **
 ***************************************************************************/
953
static int _pdn_connectivity_find_apn(esm_data_t *esm_data, const OctetString *apn)
gauthier's avatar
 
gauthier committed
954 955 956 957
{
  int i;

  for (i = 0; i < ESM_DATA_PDN_MAX; i++) {
958 959
    if ( (esm_data->pdn[i].pid != -1) && esm_data->pdn[i].data ) {
      if (esm_data->pdn[i].data->apn.length != apn->length) {
gauthier's avatar
 
gauthier committed
960 961 962
        continue;
      }

963
      if (memcmp(esm_data->pdn[i].data->apn.value,
gauthier's avatar
 
gauthier committed
964 965 966 967 968 969 970 971 972 973
                 apn->value, apn->length) != 0) {
        continue;
      }

      /* PDN entry found */
      break;
    }
  }

  /* Return the identifier of the PDN connection */
974
  return (esm_data->pdn[i].pid);
gauthier's avatar
 
gauthier committed
975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992
}

/****************************************************************************
 **                                                                        **
 ** Name:    _pdn_connectivity_find_pdn()                              **
 **                                                                        **
 ** Description: Search the list of PDN connections for an entry defined   **
 **      for the specified APN with the same PDN type              **
 **                                                                        **
 ** Inputs:  apn:       Access Point Name of the PDN connection    **
 **      pdn_type:  PDN address type                           **
 **                                                                        **
 ** Outputs:     None                                                      **
 **      Return:    The identifier of the PDN connection if    **
 **             found in the list; -1 otherwise.           **
 **      Others:    None                                       **
 **                                                                        **
 ***************************************************************************/
993
static int _pdn_connectivity_find_pdn(esm_data_t *esm_data, const OctetString *apn,
gauthier's avatar
 
gauthier committed
994 995 996 997 998
                                      const esm_proc_pdn_type_t pdn_type)
{
  int i;

  for (i = 0; i < ESM_DATA_PDN_MAX; i++) {
999
    if ( (esm_data->pdn[i].pid != -1) && esm_data->pdn[i].data ) {
gauthier's avatar
 
gauthier committed
1000
      /* PDN connection established during initial network attachment */
1001
      if (esm_data->pdn[i].data->apn.length == 0) {
gauthier's avatar
 
gauthier committed
1002 1003 1004 1005
        break;
      }

      /* Subsequent PDN connection established for the specified APN */
1006
      if (esm_data->pdn[i].data->apn.length != apn->length) {
gauthier's avatar
 
gauthier committed
1007 1008 1009
        continue;
      }

1010
      if (memcmp(esm_data->pdn[i].data->apn.value,
gauthier's avatar
 
gauthier committed
1011 1012 1013 1014
                 apn->value, apn->length) != 0) {
        continue;
      }

1015
      if (esm_data->pdn[i].data->type == ESM_PDN_TYPE_IPV4V6) {
gauthier's avatar
 
gauthier committed
1016 1017 1018
        break;
      }

1019
      if (esm_data->pdn[i].data->type == pdn_type) {
gauthier's avatar
 
gauthier committed
1020 1021 1022 1023 1024 1025
        break;
      }
    }
  }

  /* Return the identifier of the PDN connection */
1026
  return (esm_data->pdn[i].pid);
gauthier's avatar
 
gauthier committed
1027
}