view src/g23m-gprs/sm/sm_tft.c @ 480:41f2cc21bca9

hybrid fw: code change to support allowing GSM APDUs in AT+CSIM
author Mychaela Falconia <falcon@freecalypso.org>
date Tue, 19 Jun 2018 06:27:16 +0000
parents 219afcfc6250
children
line wrap: on
line source

/*----------------------------------------------------------------------------
|  Project :  3G PS
|  Module  :  SM
+-----------------------------------------------------------------------------
|             Copyright 2003 Texas Instruments.
|             All rights reserved. 
| 
|             This file is confidential and a trade secret of Texas 
|             Instruments .
|             The receipt of or possession of this file does not convey 
|             any rights to reproduce or disclose its contents or to 
|             manufacture, use, or sell anything it may describe, in 
|             whole, or in part, without the specific written consent of 
|             Texas Instruments. 
+-----------------------------------------------------------------------------
| Purpose:    3G TFT utility functions implementation in the SM entity.
|             For design details, see:
|             8010.908 SM Detailed Specification
+---------------------------------------------------------------------------*/

/*==== DECLARATION CONTROL =================================================*/

/*==== INCLUDES ============================================================*/

#include "sm.h"

#include "sm_tft.h"

/*==== CONST ===============================================================*/

/*==== LOCAL VARS ===========================================================*/

/*==== PRIVATE FUNCTIONS ====================================================*/

/*
+------------------------------------------------------------------------------
| Function    : sm_tft_count_tfts_in_mask
+------------------------------------------------------------------------------
| Description : Returns the number of filters (set bits) in a TFT mask.
|
| Parameters  : tft_mask    - TFT mask
+------------------------------------------------------------------------------
*/
static U8 sm_tft_count_tfts_in_mask(U8 tft_mask) /*@*/
{
  U8 count_array[16] = {
    (U8)0 /* 0000 */, (U8)1 /* 0001 */, (U8)1 /* 0010 */, (U8)2 /* 0011 */,
    (U8)1 /* 0100 */, (U8)2 /* 0101 */, (U8)2 /* 0110 */, (U8)3 /* 0111 */,
    (U8)1 /* 1000 */, (U8)2 /* 1001 */, (U8)2 /* 1010 */, (U8)3 /* 1011 */,
    (U8)2 /* 1100 */, (U8)3 /* 1101 */, (U8)3 /* 1110 */, (U8)4 /* 1111 */
  };
  return (count_array[(U16)tft_mask >> 4] + count_array[(U16)tft_mask & 15]);
}

/*
+------------------------------------------------------------------------------
| Function    : sm_tft_is_tft_in_mask
+------------------------------------------------------------------------------
| Description : Returns TRUE if the TFT with the specified ID (bit) is
|               set in the specified TFT mask.
|
| Parameters  : id          - TFT identifier
|               tft_mask    - TFT mask
+------------------------------------------------------------------------------
*/
static BOOL sm_tft_is_tft_in_mask(U8 id, U8 tft_mask) /*@*/
{
  return ((tft_mask & (1UL << id)) != 0);
}

/*
+------------------------------------------------------------------------------
| Function    : sm_tft_is_port_range
+------------------------------------------------------------------------------
| Description : Query function returning TRUE if high_limit is not zero, and
|               high_limit is greater than (but not equal) to low_limit.
|
| Parameters  : low_limit   - lower port limit
|               high_limit  - upper port limit
+------------------------------------------------------------------------------
*/
static BOOL sm_tft_is_port_range(U16 low_limit, U16 high_limit)
{
  return (high_limit != 0 && low_limit < high_limit);
}

static /*@exposed@*/T_NAS_tft_pf *
sm_tft_get_active_tft_by_id(/*@returned@*/ /*@partial@*/struct T_SM_CONTEXT_DATA *context,
			    U8 id)
{
  TRACE_ASSERT(id < (U8)NAS_SIZE_TFT_FILTER);
  TRACE_ASSERT(context->active_tft.ptr_tft_pf != NULL);
  return (&context->active_tft.ptr_tft_pf[id]);
}

/*
+------------------------------------------------------------------------------
| Function    : sm_tft_pf_size
+------------------------------------------------------------------------------
| Description : Query function returning the packed size of a packet filter.
|
| Parameters  : tft_pf      - packet filter
+------------------------------------------------------------------------------
*/
static U16 sm_tft_pf_size(T_NAS_tft_pf *tft_pf)
{
  U16 tft_pf_size = (U16)3; /* Filter ID, precedence and length octet mandatory */

  U16 lower3bits[8] = {
  (U16)   0, /* 0x00: None */
  (U16)   9, /* 0x01: IPv4_SRC_ADDR_MASK */
  (U16)  33, /* 0x02: IPv6_SRC_ADDR_MASK */
  (U16)   0, /* 0x03: IPv4_SRC_ADDR_MASK + IPv6_SRC_ADDR_MASK -- INVALID! */
  (U16)   3, /* 0x04: PROTOCOL_OR_NEXT_HDR */
  (U16)  12, /* 0x05: IPv4_SRC_ADDR_MASK + PROTOCOL_OR_NEXT_HDR */
  (U16)  36, /* 0x06: IPv6_SRC_ADDR_MASK + PROTOCOL_OR_NEXT_HDR */
  (U16)   0  /* 0x07: IPv4_SRC_ADDR_MASK + IPv6_SRC_ADDR_MASK + PROTOCOL_OR_NEXT_HDR -- INVALID! */
  };
  U16 upper3bits[8] = {
  (U16)   0, /* 0x00: None */
  (U16)   5, /* 0x20: IPSEC_SPI */
  (U16)   3, /* 0x40: TOS_AND_MASK */
  (U16)   5, /* 0x60: IPSEC_SPI + TOS_AND_MASK */
  (U16)   4, /* 0x80: FLOW_LABEL */
  (U16)   0, /* 0xa0: IPSEC_SPI + FLOW_LABEL - INVALID! */
  (U16)   7, /* 0xc0: TOS_AND_MASK + FLOW_LABEL */
  (U16)   0  /* 0xe0: IPSEC_SPI + TOS_AND_MASK + FLOW_LABEL - INVALID! */
  };

  if ((tft_pf->tft_pf_valid_bits & (U8)0x07) != (U8)0)
  {
    tft_pf_size   += lower3bits[(U16)tft_pf->tft_pf_valid_bits & 0x07];
  }

  if ((tft_pf->tft_pf_valid_bits & (U8)0xe0) != (U8)0)
  {
    tft_pf_size   += upper3bits[(U16)tft_pf->tft_pf_valid_bits >> 5];
  }

  if ((tft_pf->tft_pf_valid_bits & (U8)NAS_TFT_ID_DEST_PORT_RANGE) != (U8)0)
  {
    T_NAS_tft_dest_port_range *range = &tft_pf->tft_pf_entry.tft_pf_ipv4.tft_dest_port_range;
    if (sm_tft_is_port_range(range->low_limit, range->high_limit))
    {
      tft_pf_size += (U16)5;  /* Destination port range */
    } else {
      tft_pf_size += (U16)3;  /* Single destination port */
    }
  }

  if ((tft_pf->tft_pf_valid_bits & (U8)NAS_TFT_ID_SRC_PORT_RANGE) != (U8)0)
  {
    T_NAS_tft_src_port_range *range = &tft_pf->tft_pf_entry.tft_pf_ipv4.tft_src_port_range;
    if (sm_tft_is_port_range(range->low_limit, range->high_limit))
    {
      tft_pf_size += (U16)5;  /* Source port range */
    } else {
      tft_pf_size += (U16)3;  /* Single source port */
    }
  }

  return tft_pf_size;
}

/*
+------------------------------------------------------------------------------
| Function    : sm_tft_are_equal
+------------------------------------------------------------------------------
| Description : Query function returning TRUE if the two input packet filters
|               are equal (same parameters).
|
| Parameters  : tft1        - packet filter 1
|               tft2        - packet filter 2
+------------------------------------------------------------------------------
*/
static BOOL sm_tft_are_equal(T_NAS_tft_pf *tft1, T_NAS_tft_pf *tft2)
{
  U8 valid_bits;

  /* Compare outer-level parameters */
  if (tft1->tft_pf_id         != tft2->tft_pf_id ||
      tft1->tft_pf_precedence != tft2->tft_pf_precedence ||
      tft1->tft_pf_valid_bits != tft2->tft_pf_valid_bits ||
      tft1->ctrl_tft_pf_entry != tft2->ctrl_tft_pf_entry)
  {
    return FALSE;
  }

  valid_bits = tft1->tft_pf_valid_bits;

  if (tft1->ctrl_tft_pf_entry == NAS_is_tft_pf_ipv4) {
    T_NAS_tft_pf_ipv4 *v4_pf_1 = &tft1->tft_pf_entry.tft_pf_ipv4;
    T_NAS_tft_pf_ipv4 *v4_pf_2 = &tft2->tft_pf_entry.tft_pf_ipv4;

    if (((valid_bits & (U8)NAS_TFT_ID_PROTOCOL_OR_NEXT_HDR) != (U8)0
	    && v4_pf_1->tft_protocol != v4_pf_2->tft_protocol)
	|| ((valid_bits & (U8)NAS_TFT_ID_TOS_AND_MASK) != (U8)0
	    && v4_pf_1->tft_tos_and_mask.tos_value != v4_pf_2->tft_tos_and_mask.tos_value
	    && v4_pf_1->tft_tos_and_mask.tos_mask  != v4_pf_2->tft_tos_and_mask.tos_mask)
	|| ((valid_bits & (U8)NAS_TFT_ID_DEST_PORT_RANGE) != (U8)0
	    && memcmp(&v4_pf_1->tft_dest_port_range, &v4_pf_2->tft_dest_port_range, sizeof(T_NAS_tft_dest_port_range)) != 0)
	|| ((valid_bits & (U8)NAS_TFT_ID_SRC_PORT_RANGE) != (U8)0
	    && memcmp(&v4_pf_1->tft_src_port_range, &v4_pf_2->tft_src_port_range, sizeof(T_NAS_tft_src_port_range)) != 0)
	|| ((valid_bits & (U8)NAS_TFT_ID_IPSEC_SPI) != (U8)0
	    && v4_pf_1->tft_ipsec_spi != v4_pf_2->tft_ipsec_spi)
	|| ((valid_bits & (U8)NAS_TFT_ID_IPv4_SRC_ADDR_MASK) != (U8)0
	    && memcmp(&v4_pf_1->tft_ipv4_src_addr_mask, &v4_pf_2->tft_ipv4_src_addr_mask, sizeof(T_NAS_tft_ipv4_src_addr_mask)) != 0))
    {
      return FALSE;
    }
  } else if (tft1->ctrl_tft_pf_entry == NAS_is_tft_pf_ipv6)
  {
    T_NAS_tft_pf_ipv6 *v6_pf_1 = &tft1->tft_pf_entry.tft_pf_ipv6;
    T_NAS_tft_pf_ipv6 *v6_pf_2 = &tft2->tft_pf_entry.tft_pf_ipv6;

    if (((valid_bits & (U8)NAS_TFT_ID_PROTOCOL_OR_NEXT_HDR) != (U8)0
	    && v6_pf_1->tft_next_hdr != v6_pf_2->tft_next_hdr)
	|| ((valid_bits & (U8)NAS_TFT_ID_TOS_AND_MASK) != (U8)0
	    && v6_pf_1->tft_tos_and_mask.tos_value != v6_pf_2->tft_tos_and_mask.tos_value
	    && v6_pf_1->tft_tos_and_mask.tos_mask  != v6_pf_2->tft_tos_and_mask.tos_mask)
	|| ((valid_bits & (U8)NAS_TFT_ID_DEST_PORT_RANGE) != (U8)0
	    && memcmp(&v6_pf_1->tft_dest_port_range, &v6_pf_2->tft_dest_port_range, sizeof(T_NAS_tft_dest_port_range)) != 0)
	|| ((valid_bits & (U8)NAS_TFT_ID_SRC_PORT_RANGE) != (U8)0
	    && memcmp(&v6_pf_1->tft_src_port_range, &v6_pf_2->tft_src_port_range, sizeof(T_NAS_tft_src_port_range)) != 0)
	|| ((valid_bits & (U8)NAS_TFT_ID_IPSEC_SPI) != (U8)0
	    && v6_pf_1->tft_ipsec_spi != v6_pf_2->tft_ipsec_spi)
	|| ((valid_bits & (U8)NAS_TFT_ID_FLOW_LABEL) != (U8)0
	    && v6_pf_1->tft_flow_label != v6_pf_2->tft_flow_label)
	|| ((valid_bits & (U8)NAS_TFT_ID_IPv6_SRC_ADDR_MASK) != (U8)0
	    && memcmp(&v6_pf_1->tft_ipv6_src_addr_mask, &v6_pf_2->tft_ipv6_src_addr_mask, sizeof(T_NAS_tft_ipv6_src_addr_mask)) != 0))
    {
      return FALSE;
    }
  } else {
    (void)TRACE_EVENT_P1("ERROR: Invalid union controller %d in ctrl_tft_pf_entry!",
                         tft1->ctrl_tft_pf_entry);
  }
  return TRUE;
}

/*
+------------------------------------------------------------------------------
| Function    : sm_tft_copy_ipv4_pf
+------------------------------------------------------------------------------
| Description : Copies an IPv4 TFT filter from src_tft to dst_tft AIM struct.
|
| Parameters  : dst_tft     - destination aim TFT structs
|               src_tft     - source TFT structs
|               valid_flag  - valid flags for filter components
+------------------------------------------------------------------------------
*/
static void sm_tft_copy_ipsec_spi(/*@out@*/T_M_SM_tft_ipsec_spi *dst_spi,
				  U32 spi)
{
  M_SM_BUF_ipsec_spi_value *dst = &dst_spi->ipsec_spi_value;
#ifdef TFT_DEBUG
  (void)TRACE_EVENT_P1("tft_copy: Copying IPSEC SPI filter (SPI 0x%08x)", spi);
#endif
  dst->l_ipsec_spi_value = (U16)32;
  dst->o_ipsec_spi_value = (U16)0;
  dst->b_ipsec_spi_value[0] = (U8)((spi >> 24) & 255);
  dst->b_ipsec_spi_value[1] = (U8)((spi >> 16) & 255);
  dst->b_ipsec_spi_value[2] = (U8)((spi >>  8) & 255);
  dst->b_ipsec_spi_value[3] = (U8)( spi        & 255);
}

static void
sm_tft_copy_ipv4_pf(/*@out@*/ T_M_SM_tft_filter_entry *dst,
		    T_NAS_tft_pf_ipv4 *src, U8 valid_flag)
{
  /* Copy IPv4 address, if present */
  if ((valid_flag & NAS_TFT_ID_IPv4_SRC_ADDR_MASK) != (U8)0)
  {
    dst->v_tft_ipv4_addr_mask = (U8)TRUE;
    memcpy(dst->tft_ipv4_addr_mask.src_addr,
	   src->tft_ipv4_src_addr_mask.tft_ipv4_addr,
	   (size_t)NAS_SIZE_IPv4_ADDR);
    memcpy(dst->tft_ipv4_addr_mask.addr_mask,
	   src->tft_ipv4_src_addr_mask.tft_ipv4_mask,
	   (size_t)NAS_SIZE_IPv4_ADDR);
#ifdef TFT_DEBUG
    {
      U8 *addr, *mask;
      addr = dst->tft_ipv4_addr_mask.src_addr;
      mask = dst->tft_ipv4_addr_mask.addr_mask;
      (void)TRACE_EVENT_P8("tft_copy: Copy IPv4 src addr/mask filter: "
			   "%u.%u.%u.%u/%u.%u.%u.%u",
			   addr[0], addr[1], addr[2], addr[3],
			   mask[0], mask[1], mask[2], mask[3]);
    }
#endif
  } else {
    dst->v_tft_ipv4_addr_mask            = (U8)FALSE;
  } /* NAS_TFT_ID_IPv4_SRC_ADDR_MASK */

  /* IPv6 addr/mask never invluded in IPv4 packet filter */
  dst->v_tft_ipv6_addr_mask              = (U8)FALSE;

  /* Copy IPv4 protocol number field */
  if ((valid_flag & NAS_TFT_ID_PROTOCOL_OR_NEXT_HDR) != (U8)0)
  {
    dst->v_tft_protocol = (U8)TRUE;
    dst->tft_protocol.tft_protocol_val   = src->tft_protocol;
#ifdef TFT_DEBUG
    (void)TRACE_EVENT_P1("tft_copy: Copy IPv4 protocol: 0x%02x",
			 src->tft_protocol);
#endif
  } else {
    dst->v_tft_protocol = (U8)FALSE;
  } /* NAS_TFT_ID_PROTOCOL_OR_NEXT_HDR */

  /* Copy destination single port/port range parameter */
  if ((valid_flag & NAS_TFT_ID_DEST_PORT_RANGE) != (U8)0)
  {
    T_NAS_tft_dest_port_range *port_range = &src->tft_dest_port_range;
    if (sm_tft_is_port_range(port_range->low_limit, port_range->high_limit)) {
      dst->v_tft_dest_port_range          = (U8)TRUE;
      dst->v_tft_dest_port                = (U8)FALSE;
      dst->tft_dest_port_range.low_limit  = port_range->low_limit;
      dst->tft_dest_port_range.high_limit = port_range->high_limit;
#ifdef TFT_DEBUG
      (void)TRACE_EVENT_P2("tft_copy: Copying destination port range filter (ports %d-%d)",
			   port_range->low_limit, port_range->high_limit);
#endif
    } else {
      dst->v_tft_dest_port_range          = (U8)FALSE;
      dst->v_tft_dest_port                = (U8)TRUE;
      dst->tft_dest_port.low_limit        = port_range->low_limit;
#ifdef TFT_DEBUG
      (void)TRACE_EVENT_P1("tft_copy: Copying single destination port filter (port %d)",
			 port_range->low_limit);
#endif
    }
  } else {
    dst->v_tft_dest_port_range           = (U8)FALSE;
    dst->v_tft_dest_port                 = (U8)FALSE;
  } /* NAS_TFT_ID_DEST_PORT_RANGE */

  /* Copy source single port/port range parameter */
  if ((valid_flag & NAS_TFT_ID_SRC_PORT_RANGE) != (U8)0)
  {
    T_NAS_tft_src_port_range *port_range = &src->tft_src_port_range;
    if (sm_tft_is_port_range(port_range->low_limit, port_range->high_limit))
    {
      dst->v_tft_src_port_range          = (U8)TRUE;
      dst->v_tft_src_port                = (U8)FALSE;
      dst->tft_src_port_range.low_limit  = port_range->low_limit;
      dst->tft_src_port_range.high_limit = port_range->high_limit;
#ifdef TFT_DEBUG
      (void)TRACE_EVENT_P2("tft_copy: Copying source port range filter (ports %d-%d)",
			   port_range->low_limit, port_range->high_limit);
#endif
    } else {
      dst->v_tft_src_port_range          = (U8)FALSE;
      dst->v_tft_src_port                = (U8)TRUE;
      dst->tft_src_port.low_limit        = port_range->low_limit;
#ifdef TFT_DEBUG
    (void)TRACE_EVENT_P1("tft_copy: Copying single source port filter (port %d)",
			 port_range->low_limit);
#endif
    }
  } else {
    dst->v_tft_src_port_range = (U8)FALSE;
    dst->v_tft_src_port       = (U8)FALSE;
  } /* NAS_TFT_ID_SRC_PORT_RANGE */

  if ((valid_flag & NAS_TFT_ID_IPSEC_SPI) != (U8)0)
  {
    dst->v_tft_ipsec_spi = (U8)TRUE;
    sm_tft_copy_ipsec_spi(&dst->tft_ipsec_spi, src->tft_ipsec_spi);
  } else {
    dst->v_tft_ipsec_spi = (U8)FALSE;
  }/* NAS_TFT_ID_IPSEC_SPI */

  if ((valid_flag & NAS_TFT_ID_TOS_AND_MASK) != (U8)0)
  {
    dst->v_tft_tos_and_mask = (U8)TRUE;
    dst->tft_tos_and_mask.tos_value = src->tft_tos_and_mask.tos_value;
    dst->tft_tos_and_mask.tos_mask  = src->tft_tos_and_mask.tos_mask;
  } else {
    dst->v_tft_tos_and_mask = (U8)FALSE;
  } /* NAS_TFT_ID_TOS_AND_MASK */

  if ((valid_flag & NAS_TFT_ID_FLOW_LABEL) != (U8)0) {
    (void)TRACE_ERROR("tft_copy: Found flow label in IPv4 packet filter - Discarded...");
  } /* NAS_TFT_ID_FLOW_LABEL */
  dst->v_tft_flow_label = (U8)FALSE;
}

/*
+------------------------------------------------------------------------------
| Function    : sm_tft_copy_ipv6_pf
+------------------------------------------------------------------------------
| Description : Copies an IPv6 TFT filter from src_tft to dst_tft AIM struct.
|
| Parameters  : dst_tft     - destination aim TFT structs
|               src_tft     - source TFT structs
|               valid_flag  - valid flags for filter components
+------------------------------------------------------------------------------
*/
static void
sm_tft_copy_ipv6_pf(/*@out@*/ T_M_SM_tft_filter_entry *dst,
		    T_NAS_tft_pf_ipv6 *src, U8 valid_flag)
{
  /* IPv4 addr/mask never invluded in IPv4 packet filter */
  dst->v_tft_ipv4_addr_mask              = (U8)FALSE;

  /* Copy IPv6 address, if present */
  if ((valid_flag & NAS_TFT_ID_IPv6_SRC_ADDR_MASK) != (U8)0)
  {
    dst->v_tft_ipv6_addr_mask = (U8)TRUE;
    memcpy(dst->tft_ipv6_addr_mask.src_addr,
	   src->tft_ipv6_src_addr_mask.tft_ipv6_addr,
	   (size_t)NAS_SIZE_IPv6_ADDR);
    memcpy(dst->tft_ipv6_addr_mask.addr_mask,
	   src->tft_ipv6_src_addr_mask.tft_ipv6_mask,
	   (size_t)NAS_SIZE_IPv6_ADDR);
#ifdef TFT_DEBUG
    /* Dump address/mask */
    {
      char addr[SM_SIZE_FORMATTED_IPv6_ADDR], mask[SM_SIZE_FORMATTED_IPv6_ADDR];
      (void)TRACE_EVENT_P2("tft_copy: Copy IPv6 src addr/mask filter: %s/%s",
            sm_format_ipv6_addr(dst->tft_ipv6_addr_mask.src_addr, addr),
            sm_format_ipv6_addr(dst->tft_ipv6_addr_mask.addr_mask, mask));
    }
#endif
  } else {
    dst->v_tft_ipv6_addr_mask = (U8)FALSE;
  } /* NAS_TFT_ID_IPv6_SRC_ADDR_MASK */

  /* Copy IPv6 next header field */
  if ((valid_flag & NAS_TFT_ID_PROTOCOL_OR_NEXT_HDR) != (U8)0)
  {
    dst->v_tft_protocol = (U8)TRUE;
    dst->tft_protocol.tft_protocol_val = src->tft_next_hdr;
#ifdef TFT_DEBUG
    (void)TRACE_EVENT_P1("tft_copy: Copy IPv6 next header field: 0x%02x",
		   src->tft_next_hdr);
#endif
  } else {
    dst->v_tft_protocol = (U8)FALSE;
  } /* NAS_TFT_ID_PROTOCOL_OR_NEXT_HDR */

  /* Copy destination single port/port range parameter */
  if ((valid_flag & NAS_TFT_ID_DEST_PORT_RANGE) != (U8)0)
  {
    T_NAS_tft_dest_port_range *port_range = &src->tft_dest_port_range;
    if (sm_tft_is_port_range(port_range->low_limit, port_range->high_limit))
    {
      dst->v_tft_dest_port_range = (U8)TRUE;
      dst->v_tft_dest_port       = (U8)FALSE;
      dst->tft_dest_port_range.low_limit  = port_range->low_limit;
      dst->tft_dest_port_range.high_limit = port_range->high_limit;
#ifdef TFT_DEBUG
    (void)TRACE_EVENT_P2("tft_copy: Copying destination port range filter (ports %d-%d)",
			 port_range->low_limit, port_range->high_limit);
#endif
    } else {
      dst->v_tft_dest_port_range = (U8)FALSE;
      dst->v_tft_dest_port       = (U8)TRUE;
      dst->tft_dest_port.low_limit        = port_range->low_limit;
#ifdef TFT_DEBUG
    (void)TRACE_EVENT_P1("tft_copy: Copying single destination port filter (port %d)",
			 port_range->low_limit);
#endif
    }
  } else {
    dst->v_tft_dest_port_range = (U8)FALSE;
    dst->v_tft_dest_port       = (U8)FALSE;
  } /* NAS_TFT_ID_DEST_PORT_RANGE */

  /* Copy source single port/port range parameter */
  if ((valid_flag & NAS_TFT_ID_SRC_PORT_RANGE) != (U8)0)
  {
    T_NAS_tft_src_port_range *port_range = &src->tft_src_port_range;
    if (sm_tft_is_port_range(port_range->low_limit, port_range->high_limit))
    {
      dst->v_tft_src_port_range = (U8)TRUE;
      dst->v_tft_src_port       = (U8)FALSE;
      dst->tft_src_port_range.low_limit  = port_range->low_limit;
      dst->tft_src_port_range.high_limit = port_range->high_limit;
#ifdef TFT_DEBUG
    (void)TRACE_EVENT_P2("tft_copy: Copying source port range filter (ports %d-%d)",
			 port_range->low_limit, port_range->high_limit);
#endif
    } else {
      dst->v_tft_src_port_range = (U8)FALSE;
      dst->v_tft_src_port       = (U8)TRUE;
      dst->tft_src_port.low_limit        = port_range->low_limit;
#ifdef TFT_DEBUG
    (void)TRACE_EVENT_P1("tft_copy: Copying single source port filter (port %d)",
			 port_range->low_limit);
#endif
    }
  } else {
    dst->v_tft_src_port_range = (U8)FALSE;
    dst->v_tft_src_port       = (U8)FALSE;
  } /* NAS_TFT_ID_SRC_PORT_RANGE */

  if ((valid_flag & NAS_TFT_ID_IPSEC_SPI) != (U8)0)
  {
    dst->v_tft_ipsec_spi = (U8)TRUE;
    sm_tft_copy_ipsec_spi(&dst->tft_ipsec_spi, src->tft_ipsec_spi);
  } else {
    dst->v_tft_ipsec_spi = (U8)FALSE;
  } /* NAS_TFT_ID_IPSEC_SPI */

  if ((valid_flag & NAS_TFT_ID_TOS_AND_MASK) != (U8)0)
  {
    dst->v_tft_tos_and_mask = (U8)TRUE;
    dst->tft_tos_and_mask.tos_value = src->tft_tos_and_mask.tos_value;
    dst->tft_tos_and_mask.tos_mask  = src->tft_tos_and_mask.tos_mask;
  } else {
    dst->v_tft_tos_and_mask = (U8)FALSE;
  } /* NAS_TFT_ID_TOS_AND_MASK */

  if ((valid_flag & NAS_TFT_ID_FLOW_LABEL) != (U8)0)
  {
    dst->v_tft_flow_label = (U8)TRUE;
    dst->tft_flow_label.flow_label_value = src->tft_flow_label;
  } else {
    dst->v_tft_flow_label = (U8)FALSE;
  } /* NAS_TFT_ID_FLOW_LABEL */
}

/*
+------------------------------------------------------------------------------
| Function    : sm_tft_copy_filters
+------------------------------------------------------------------------------
| Description : Copies TFT filters from src_tft to dst_tft AIM structs.
|
| Parameters  : dst_tft     - destination aim TFT structs
|               src_tft     - source TFT structs
|               tft_mask    - TFT mask (determines which TFTs to consider)
+------------------------------------------------------------------------------
*/
static U8
sm_tft_copy_filters(/*@special@*/T_M_SM_tft   *dst_tft,
		    /*@in@*/     T_NAS_tft_pf *src_tft,
		    U8 max_count, U8 tft_mask)
{
  int                i, count;
  T_M_SM_tft_filter *dst;
  U16                size, total_size = 0;
  U8                 change_mask = (U8)0;

  dst_tft->v_tft_filter = (U8)TRUE;

  /* Initialize pointer to air interface TFT */
  dst = dst_tft->tft_filter;

  for (i = 0, count = 0; i < (int)max_count; i++)
  {
    if (sm_tft_is_tft_in_mask(src_tft->tft_pf_id, tft_mask))
    {
      size = sm_tft_pf_size(src_tft);
      /* Only include packet filter, if it fits in air interface message */
      if (total_size + size > (U16)255) {
	break;
      }

      dst->tft_filter_id   = src_tft->tft_pf_id;
      dst->tft_filter_prio = src_tft->tft_pf_precedence;

      if (src_tft->ctrl_tft_pf_entry == NAS_is_tft_pf_ipv4) {
	sm_tft_copy_ipv4_pf(&dst->tft_filter_entry,
			    &src_tft->tft_pf_entry.tft_pf_ipv4,
			    src_tft->tft_pf_valid_bits);
      } else if (src_tft->ctrl_tft_pf_entry == NAS_is_tft_pf_ipv6) {
	sm_tft_copy_ipv6_pf(&dst->tft_filter_entry,
			    &src_tft->tft_pf_entry.tft_pf_ipv6,
			    src_tft->tft_pf_valid_bits);
      } else {
	(void)TRACE_EVENT_P1("tft_copy: ERROR! Wrong union controller (%d) "
			     "for tft_pf_entry; discarded...",
			     src_tft->ctrl_tft_pf_entry);
      }
      /* Update change mask and total TFT size */
      change_mask |= (U8)(1UL << dst->tft_filter_id);
      total_size  += size;
      /* Advance to next destination TFT */
      dst++;
      count++;
    } /* if (sm_tft_is_tft_in_mask()) */
    /* Skip to next input TFT */
    src_tft++;
  }

  dst_tft->c_tft_filter = (U8)count;

  return change_mask;
}

/*
+------------------------------------------------------------------------------
| Function    : sm_tft_copy_filter_numbers
+------------------------------------------------------------------------------
| Description : Copies filter IDs to dst_tft AIM struct (derived from tft_mask)
|
| Parameters  : dst_tft     - destination aim TFT structs
|               tft_mask    - TFT mask (determines which TFTs to consider)
+------------------------------------------------------------------------------
*/
static void
sm_tft_copy_filter_numbers(/*@special@*/T_M_SM_tft *dst_tft,
			   U8 tft_mask)
{
  int  index, dst_idx;

  dst_tft->v_tft_filter_id = (U8)TRUE;

  for (index = 0, dst_idx = 0; index < (int)NAS_SIZE_TFT_FILTER; index++)
  {
    if (sm_tft_is_tft_in_mask((U8)index, tft_mask))
    {
      dst_tft->tft_filter_id[dst_idx] = (U8)index;
      dst_idx++;
    }
  } /* for */

  /* Set counter to the number of filters numbers inserted */
  dst_tft->c_tft_filter_id = (U8)dst_idx;
}

/*
+------------------------------------------------------------------------------
| Function    : sm_tft_update_active_tft
+------------------------------------------------------------------------------
| Description : Update context data structure with new active TFT as it would
|               be would the modification succeed.
|
| Parameters  : context     - context data
|               tft_mask    - TFT mask (determines which TFTs to consider)
+------------------------------------------------------------------------------
*/
static void
sm_tft_update_active_tft(/*@special@*/struct T_SM_CONTEXT_DATA *context,
			 U8 change_mask)
{
  T_NAS_tft_pf *req_tft, *act_tft;
  U16           index;
  U8            req_mask, act_mask, add_mask, delete_mask;

  req_mask    = context->requested_tft.tft_precence_mask;
  act_mask    = context->active_tft.tft_precence_mask;
  add_mask    = req_mask & change_mask;
  delete_mask = (U8)(0xffUL ^ req_mask) & change_mask;

  if (req_mask == (U8)0) {
    sm_nw_free_active_tft(context);
    return;
  }

  TRACE_ASSERT(context->requested_tft.ptr_tft_pf != NULL);

  /* Allocate active TFT memory, if required */
  if (act_mask == (U8)0 &&
      /*req_mask != (U8)0 &&*/  /*commented out because of previous return*/
      context->active_tft.ptr_tft_pf == NULL)
  {
    sm_nw_allocate_active_tft(context);
    TRACE_ASSERT(context->active_tft.ptr_tft_pf != NULL);
  }

  /* Loop over TFTs, in order to find any TFTs to delete */
  for (index = 0; index < (U16)NAS_SIZE_TFT_FILTER; index++)
  {
    if (sm_tft_is_tft_in_mask((U8)index, delete_mask))
    {
      /* Was filter removed ?  If so, clear filter from mask and store */
      act_mask  &= (U8)(0xffUL ^ 1UL << index);
      act_tft    = sm_tft_get_active_tft_by_id(context, (U8)index);
      memset(act_tft, 0, sizeof(T_NAS_tft_pf));
    }
  }

  for (index = 0, req_tft = context->requested_tft.ptr_tft_pf;
       index < (U16)context->requested_tft.c_tft_pf;
       index++, req_tft++)
  {
    U8  pf_id                 = req_tft->tft_pf_id;

    if (sm_tft_is_tft_in_mask(pf_id, add_mask))
    {
      /* Copy any remaining requested TFTs into active TFT */
      act_mask  |= (U8)(1UL << pf_id);
      act_tft    = sm_tft_get_active_tft_by_id(context, pf_id);
      memcpy(act_tft, req_tft, sizeof(T_NAS_tft_pf));
    }
  } /* for */

  context->active_tft.tft_precence_mask = act_mask;
  context->active_tft.c_tft_pf          = sm_tft_count_tfts_in_mask(act_mask);
}

/*
+------------------------------------------------------------------------------
| Function    : sm_tft_next_action
+------------------------------------------------------------------------------
| Description : Determines the next action to perform (opcode) on the TFTs
|               for a context, and the filters affected.
|               (8 upper bits is change mask; 8 lower bits is opcode)
|
| Parameters  : context     - context data
+------------------------------------------------------------------------------
*/
static U16 sm_tft_next_action(struct T_SM_CONTEXT_DATA *context)
{
  U8  req_mask, act_mask, missing_mask, surplus_mask, replace_mask;
  U16 index;

  req_mask = context->requested_tft.tft_precence_mask;
  act_mask = context->active_tft.tft_precence_mask;

  /* If requested TFT contains 0 filters, remove TFT altogether */
  if (context->requested_tft.c_tft_pf == (U8)0)
  {
    return (U16)M_SM_TFT_OPCODE_DELETE_TFT;
  }

  TRACE_ASSERT(context->requested_tft.ptr_tft_pf != NULL);

  /* If there are no activate TFT filters, and requested != 0, create new TFT */
  if (act_mask == (U8)0 && req_mask != (U8)0)
  {
    return (U16)M_SM_TFT_OPCODE_CREATE_TFT | (U16)req_mask << 8;
  }

  /* Add filters, if requested filter count > active filter count or
   * requested filters exist, that are not active. */
  missing_mask = req_mask & (0xff ^ act_mask);

  if (context->requested_tft.c_tft_pf > context->active_tft.c_tft_pf ||
      missing_mask != (U8)0)
  {
    return (U16)M_SM_TFT_OPCODE_ADD_FILTERS | (U16)missing_mask << 8;
  }

  /* Delete filters, if requested filter count < active filter count or
   * active filters exist, that are not requested. */
  surplus_mask = act_mask & (0xff ^ req_mask);

  if (context->requested_tft.c_tft_pf < context->active_tft.c_tft_pf ||
      surplus_mask != (U8)0)
  {
    return (U16)M_SM_TFT_OPCODE_DELETE_FILTERS | (U16)surplus_mask << 8;
  }

  /* Now, requested and active TFTs have the same filter *numbers* active;
   * We then need to check, whether the *contents* of the filters that
   * have the same number also match. */
  for (index = 0, replace_mask = (U8)0;
       index < (U16)context->requested_tft.c_tft_pf;
       index++)
  {
    T_NAS_tft_pf *req_tft = &context->requested_tft.ptr_tft_pf[index];
    T_NAS_tft_pf *act_tft = sm_tft_get_active_tft_by_id(context, req_tft->tft_pf_id);

    if (!sm_tft_are_equal(req_tft, act_tft))
    {
      replace_mask |= (U8)(1UL << req_tft->tft_pf_id);
    }
  }

  if (replace_mask != (U8)0) {
    return (U16)M_SM_TFT_OPCODE_REPLACE_FILTERS | (U16)replace_mask << 8;
  }

  /* Error.  Nothing to change, or invalid configuration */
  return (U16)M_SM_TFT_OPCODE_SPARE;
}

/*==== PUBLIC FUNCTIONS =====================================================*/

U8 sm_tft_precence_mask(T_NAS_tft_pf *tft, U8 count)
{
  U16 index;
  U8  precence_mask = (U8)0;

  for (index = 0; index < (U16)count; index++)
  {
    precence_mask |= (1UL << tft[index].tft_pf_id);
  }

  return precence_mask;
}

void sm_tft_convert_to_aim(struct T_SM_CONTEXT_DATA *context,
			   /*@out@*/ T_M_SM_tft     *dst_tft)
{
  U8	opcode, filter_mask, change_mask, max_count;
  U16   opcode_and_mask;

  T_NAS_tft_pf *src_tft     = context->requested_tft.ptr_tft_pf;
  max_count                 = context->requested_tft.c_tft_pf;

  if (src_tft == NULL)
  {
    return;
  }

  opcode_and_mask           = sm_tft_next_action(context);
  opcode                    = (U8)(opcode_and_mask & 0xff);
  filter_mask               = (U8)(opcode_and_mask >> 8);

  dst_tft->tft_opcode       = opcode;

  switch (opcode)
  {
  case M_SM_TFT_OPCODE_CREATE_TFT:
  case M_SM_TFT_OPCODE_ADD_FILTERS:
  case M_SM_TFT_OPCODE_REPLACE_FILTERS:
    TRACE_ASSERT(src_tft != NULL);
    /* Copy filter count to air interface message TFT struct */
    change_mask = sm_tft_copy_filters(dst_tft, src_tft, max_count, filter_mask);
    sm_tft_update_active_tft(context, change_mask);
    dst_tft->tft_filter_count = sm_tft_count_tfts_in_mask(change_mask);
    break;

  case M_SM_TFT_OPCODE_DELETE_TFT:
    /*
     * DELETE TFT can have no fields other than TFT opcode and
     * filter count (== 0), and is thus complete.
     */
    context->requested_tft.tft_precence_mask = (U8)0;
    sm_tft_update_active_tft(context, (U8)0xff);
    dst_tft->tft_filter_count                = (U8)0;
    break;

  case M_SM_TFT_OPCODE_DELETE_FILTERS:
    TRACE_ASSERT(src_tft != NULL);
    /* Copy filter count to air interface message TFT struct */
    sm_tft_copy_filter_numbers(dst_tft, filter_mask);
    sm_tft_update_active_tft(context, filter_mask);
    dst_tft->tft_filter_count = sm_tft_count_tfts_in_mask(filter_mask);
    break;

  default:
    (void)TRACE_EVENT_P1("tft_convert_to_aim: ERROR!"
			 "Invalid opcode (%d) - TFT skipped!", opcode);
    break;
  }
}

BOOL sm_tft_more_to_modify(struct T_SM_CONTEXT_DATA *context)
{
  int          filter;

  (void)TRACE_FUNCTION("sm_tft_more_to_modify");

  if (context->requested_tft.c_tft_pf != context->active_tft.c_tft_pf ||
      context->requested_tft.tft_precence_mask != context->active_tft.tft_precence_mask)
  {
    return TRUE;
  }
  if (context->requested_tft.c_tft_pf > (U8)0
      && context->requested_tft.ptr_tft_pf != NULL
      && context->active_tft.ptr_tft_pf != NULL)
  {
    for (filter = 0; filter < (int)context->requested_tft.c_tft_pf; filter++)
    {
      if (!sm_tft_are_equal(&context->requested_tft.ptr_tft_pf[filter],
			    sm_tft_get_active_tft_by_id(context, context->requested_tft.ptr_tft_pf[filter].tft_pf_id)))
      {
	return TRUE;
      }
    } /* for */
  } /* if (c_tft_pf > 0) */
  return FALSE;
}