diff src/g23m-gprs/sm/sm_tft.c @ 183:219afcfc6250

src/g23m-gprs: initial import from TCS3.2/LoCosto
author Mychaela Falconia <falcon@freecalypso.org>
date Thu, 13 Oct 2016 04:24:13 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/g23m-gprs/sm/sm_tft.c	Thu Oct 13 04:24:13 2016 +0000
@@ -0,0 +1,870 @@
+/*----------------------------------------------------------------------------
+|  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;
+}