view src/g23m-gprs/sm/sm_tft.c @ 268:f2e52cab0a73

abb_inth.c: check all interrupt causes, not just one The original code used if - else if - else if etc constructs, thus the first detected interrupt was the only one handled. However, Iota ITSTATREG is a clear-on-read register, thus if we only handle the first detected interrupt and skip checking the others, then the other interrupts will be lost, if more than one interrupt happened to occur in one ABB interrupt handling cycle - a form of rare race condition. Change the code to check all interrupts that were read in this cycle.
author Mychaela Falconia <falcon@freecalypso.org>
date Sun, 13 Jun 2021 18:17:53 +0000
parents fa8dc04885d8
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;
}