SAR

Vjacheslav Chekushin <[email protected]>
Newsgroups gmane.comp.mobile.kannel.devel
Organization LMT
Message-ID <[email protected]>
Hi, all.
Some time ago I wrote simple SAR handling for testing large MMS with
connection-oriented mode. It doesn't include negative ack now.
It works for me without problem (though not tested under heavy load).
I send patch against stable version 1.2.0.
If it looks normaly and can be applied (may be after some modifications),
I can do some cleanups and extend functionality with NACK.


-- 
Vjacheslav Chekushin                                mailto:[email protected]
Latvian Mobile Phone Company                        http://www.lmt.lv
sar.patch (text/plain, 13.6 KB)
diff -ub /home/wapgw/kannel/gateway-1.2.0/wap/wap_events.def wap/wap_events.def
--- /home/wapgw/kannel/gateway-1.2.0/wap/wap_events.def	Thu Dec 13 13:25:08 2001
+++ wap/wap_events.def	Thu Aug 29 15:53:25 2002
@@ -438,6 +438,17 @@
 	ADDRTUPLE(addr_tuple)
 	)
 
+WAPEVENT(RcvSegInvoke, "RcvSegInvoke",
+        OCTSTR(user_data)
+        INTEGER(tid)
+        INTEGER(rid)
+        INTEGER(no_cache_supported)
+        INTEGER(gtr)
+        INTEGER(ttr)
+        INTEGER(psn)
+        ADDRTUPLE(addr_tuple)
+        )
+
 WAPEVENT(RcvAbort, "RcvAbort",
 	INTEGER(tid)
 	INTEGER(abort_type)
diff -ub /home/wapgw/kannel/gateway-1.2.0/wap/wtp.c wap/wtp.c
--- /home/wapgw/kannel/gateway-1.2.0/wap/wtp.c	Mon Nov 20 21:55:54 2000
+++ wap/wtp.c	Thu Aug 29 15:53:25 2002
@@ -26,6 +26,7 @@
 static int concatenated_message(Octstr *user_data);
 static int truncated_datagram(WAPEvent *event);
 static WAPEvent *unpack_invoke(WTP_PDU *pdu, WAPAddrTuple *addr_tuple);
+static WAPEvent *unpack_segmented_invoke(WTP_PDU *pdu, WAPAddrTuple *addr_tuple);
 static WAPEvent *unpack_ack(WTP_PDU *pdu, WAPAddrTuple *addr_tuple);
 static WAPEvent *unpack_abort(WTP_PDU *pdu, WAPAddrTuple *addr_tuple);
 static WAPEvent *pack_error(WAPEvent *datagram);
@@ -101,6 +102,9 @@
      case RcvInvoke:
          return event->u.RcvInvoke.tid < INITIATOR_TID_LIMIT;
 
+     case RcvSegInvoke:
+       return event->u.RcvSegInvoke.tid < INITIATOR_TID_LIMIT;
+
      case RcvAck:
 	 return event->u.RcvAck.tid < INITIATOR_TID_LIMIT;
 
@@ -156,6 +160,24 @@
     return event;
 }
 
+static WAPEvent *unpack_segmented_invoke(WTP_PDU *pdu, WAPAddrTuple *addr_tuple)
+{
+    WAPEvent *event;
+
+    event = wap_event_create(RcvSegInvoke);
+    event->u.RcvSegInvoke.user_data = 
+        octstr_duplicate(pdu->u.Segmented_invoke.user_data);
+    event->u.RcvSegInvoke.tid = pdu->u.Segmented_invoke.tid;
+    event->u.RcvSegInvoke.rid = pdu->u.Segmented_invoke.rid;
+    event->u.RcvSegInvoke.no_cache_supported = 0;
+    event->u.RcvSegInvoke.gtr = pdu->u.Segmented_invoke.gtr;
+    event->u.RcvSegInvoke.ttr = pdu->u.Segmented_invoke.ttr;
+    event->u.RcvSegInvoke.psn = pdu->u.Segmented_invoke.psn;
+    event->u.RcvSegInvoke.addr_tuple = wap_addr_tuple_duplicate(addr_tuple);
+
+    return event;
+}
+
 static WAPEvent *unpack_ack(WTP_PDU *pdu, WAPAddrTuple *addr_tuple)
 {
     WAPEvent *event;
@@ -247,6 +269,9 @@
         }
 	break;
 
+    case Segmented_invoke:
+      event = unpack_segmented_invoke(pdu, datagram->u.T_DUnitdata_Ind.addr_tuple);
+      break;
         case Ack:
 	    event = unpack_ack(pdu, datagram->u.T_DUnitdata_Ind.addr_tuple);    
         break;
diff -ub /home/wapgw/kannel/gateway-1.2.0/wap/wtp.h wap/wtp.h
--- /home/wapgw/kannel/gateway-1.2.0/wap/wtp.h	Fri Dec  8 15:31:21 2000
+++ wap/wtp.h	Thu Aug 29 18:07:47 2002
@@ -127,6 +127,11 @@
 
 typedef struct machine_pattern machine_pattern;
 
+typedef struct sar_info_t {
+  int sar_psn;
+  Octstr *sar_data;
+} sar_info_t;
+
 /*
  * Handles possible concatenated messages. Returns a list of wap events, 
  * consisting of these events. 
diff -ub /home/wapgw/kannel/gateway-1.2.0/wap/wtp_pack.c wap/wtp_pack.c
--- /home/wapgw/kannel/gateway-1.2.0/wap/wtp_pack.c	Tue Mar 27 17:35:17 2001
+++ wap/wtp_pack.c	Thu Aug 29 15:53:25 2002
@@ -154,6 +154,29 @@
     return dgram;
 }
 
+WAPEvent *wtp_pack_sar_ack(long ack_type, long tid, WAPAddrTuple *address, int psn)
+{
+    WAPEvent *dgram = NULL;
+    WTP_PDU *pdu;
+    unsigned char cpsn;
+    sprintf(&cpsn,"%c",psn);
+
+    pdu = wtp_pdu_create(Ack);
+    pdu->u.Ack.con = 1;
+    pdu->u.Ack.tidverify = ack_type;
+    pdu->u.Ack.rid = 0;
+    pdu->u.Ack.tid = send_tid(tid);
+
+    wtp_pdu_append_tpi(pdu, 3, octstr_create_from_data((char *)&cpsn, 1));
+
+    dgram = wap_event_create(T_DUnitdata_Req);
+    dgram->u.T_DUnitdata_Req.addr_tuple = wap_addr_tuple_duplicate(address);
+    dgram->u.T_DUnitdata_Req.user_data = wtp_pdu_pack(pdu);
+    wtp_pdu_destroy(pdu);
+
+    return dgram;
+}
+
 /****************************************************************************
  *
  * INTERNAL FUNCTIONS:
diff -ub /home/wapgw/kannel/gateway-1.2.0/wap/wtp_pack.h wap/wtp_pack.h
--- /home/wapgw/kannel/gateway-1.2.0/wap/wtp_pack.h	Mon Nov 20 21:55:54 2000
+++ wap/wtp_pack.h	Thu Aug 29 15:53:25 2002
@@ -54,6 +54,8 @@
 WAPEvent *wtp_pack_ack(long ack_type, int rid_flag, long tid, 
                        WAPAddrTuple *address);
 
+WAPEvent *wtp_pack_sar_ack(long ack_type, long tid, WAPAddrTuple *address, int psn);
+
 /*
  * Set or unset the retransmission indicator on a PDU that has already
  * been packed as a datagram.  dgram must be of type T_DUnitdata_Req.
diff -ub /home/wapgw/kannel/gateway-1.2.0/wap/wtp_resp.c wap/wtp_resp.c
--- /home/wapgw/kannel/gateway-1.2.0/wap/wtp_resp.c	Fri Apr 19 19:01:07 2002
+++ wap/wtp_resp.c	Wed Sep 11 12:31:29 2002
@@ -54,6 +54,8 @@
  */
 extern int wtp_forced_sar;
 
+static void sar_info_destroy(void *sar_info);
+
 /*****************************************************************************
  *
  * Prototypes of internal functions:
@@ -124,6 +126,14 @@
 static void handle_wrong_version(WAPEvent *event);
 
 /*
+ * SAR functions
+ */
+static WAPEvent *assembly_sar_event (WTPRespMachine *machine, int last_psn);
+static int add_sar_transaction (WTPRespMachine *machine, Octstr *data, int psn);
+/* static int is_wanted_sar_data (void *a, void *b); */
+static int process_sar_transaction(WTPRespMachine *machine, WAPEvent **event);
+
+/*
  * Create a datagram with an Abort PDU and send it to the WDP layer.
  */
 static void send_abort(WTPRespMachine *machine, long type, long reason);
@@ -199,12 +209,15 @@
 
     while (resp_run_status == running && 
            (e = list_consume(resp_queue)) != NULL) {
+
 	sm = resp_machine_find_or_create(e);
-	if (sm == NULL)
+
+	if (sm == NULL) {
 	    wap_event_destroy(e);
-	else
+	} else {
 	    resp_event_handle(sm, e);
 	}
+    }
 }
 
 /*
@@ -230,6 +243,18 @@
 static void resp_event_handle(WTPRespMachine *resp_machine, WAPEvent *event)
 {
     WAPEvent *wsp_event = NULL;
+    WAPEvent *e = NULL; /* For SAR acknowledgment */
+
+    /* We doesn't feed sar packets into state machine untill get the whole message */
+    if (process_sar_transaction(resp_machine,&event) == 0) {
+      debug("wap.wtp", 0, "SAR event recieved, wait for continue");
+      /* For removing state machine in case of incomplete sar */
+      start_timer_W(resp_machine);
+      if (event != NULL) {
+	wap_event_destroy(event);  
+      }
+      return;
+    }
 
     debug("wap.wtp", 0, "WTP: resp_machine %ld, state %s, event %s.", 
 	  resp_machine->mid, 
@@ -297,6 +322,9 @@
      * If clients request WTP-SAR should we force to continue
      * or act as be should do by telling the client to call back.
      */
+  if (1 == 1)
+    return 0;
+
     if (wtp_forced_sar) 
         return 0;
 
@@ -317,11 +345,11 @@
 	       handle_wrong_version(event);
         }
 
-        if (!event->u.RcvInvoke.ttr || !event->u.RcvInvoke.gtr){
+	/*        if (!event->u.RcvInvoke.ttr || !event->u.RcvInvoke.gtr){
             debug("wap.wtp_resp", 0, "WTP_RESP: no sar implemented," 
                   "aborting transaction");
             handle_no_sar(event);
-        }
+	    } */
     }
 }
 
@@ -359,6 +387,11 @@
             }
             break;
 
+    case RcvSegInvoke:
+      tid = event->u.RcvSegInvoke.tid;
+      tuple = event->u.RcvSegInvoke.addr_tuple;
+      break;
+
        case RcvAck:
             tid = event->u.RcvAck.tid;
             tuple = event->u.RcvAck.addr_tuple;
@@ -433,6 +466,12 @@
         /*
          * This and the following branch implement test nro 3 in WTP 10.2.
          */
+	    
+	case RcvSegInvoke:
+	  info(0, "WTP_RESP: resp_machine_find_or_create:"
+	       " segmented invoke received, yet having no machine");
+	  break;
+
         case RcvAck: 
             info(0, "WTP_RESP: resp_machine_find_or_create:"
                  " ack received, yet having no machine");
@@ -512,6 +551,7 @@
     #define INTEGER(name) resp_machine->name = 0; 
     #define TIMER(name) resp_machine->name = gwtimer_create(resp_queue); 
     #define ADDRTUPLE(name) resp_machine->name = NULL; 
+    #define LIST(name) resp_machine->name = NULL;
     #define MACHINE(field) field
     #include "wtp_resp_machine.def"
 
@@ -548,6 +588,7 @@
     #define INTEGER(name) resp_machine->name = 0; 
     #define TIMER(name) gwtimer_destroy(resp_machine->name); 
     #define ADDRTUPLE(name) wap_addr_tuple_destroy(resp_machine->name); 
+    #define LIST(name) list_destroy(resp_machine->name,sar_info_destroy);
     #define MACHINE(field) field
     #include "wtp_resp_machine.def"
     gw_free(resp_machine);
@@ -653,3 +694,128 @@
     e = wtp_pack_ack(ack_type, rid_flag, machine->tid, machine->addr_tuple);
     dispatch_to_wdp(e);
 }
+
+/* Process incoming event, checking for WTP SAR */
+
+static int process_sar_transaction(WTPRespMachine *machine, WAPEvent **event) {
+  WAPEvent *e,*orig_event;
+  int psn;
+  
+  orig_event = *event;
+
+  if (orig_event->type == RcvInvoke) { 
+    if (!orig_event->u.RcvInvoke.ttr || !orig_event->u.RcvInvoke.gtr) { /* SAR */
+      /* Ericcson set TTR flag even if we have the only part */
+      if (orig_event->u.RcvInvoke.ttr == 1) {
+	return 1; /* Not SAR although TTR flag was set */
+      } else {
+	/* save initial event */
+	machine->sar_invoke = wap_event_duplicate(orig_event);
+
+	/* save data into list with psn = 0 */
+	add_sar_transaction(machine, orig_event->u.RcvInvoke.user_data, 0);
+
+	if (orig_event->u.RcvInvoke.gtr == 1) { /* Need to acknowledge */
+	  e = wtp_pack_sar_ack(ACKNOWLEDGEMENT, machine->tid,
+			       machine->addr_tuple, 0);
+	  dispatch_to_wdp(e);
+	}
+	return 0;
+      }
+    } else {
+      return 1; /* Not SAR */
+    } 
+  }
+
+  if (orig_event->type == RcvSegInvoke) {
+    add_sar_transaction(machine, orig_event->u.RcvSegInvoke.user_data, 
+			orig_event->u.RcvSegInvoke.psn);
+
+    if (orig_event->u.RcvSegInvoke.gtr == 1) { /* Need to acknowledge */
+      e = wtp_pack_sar_ack(ACKNOWLEDGEMENT, machine->tid, machine->addr_tuple,
+			   orig_event->u.RcvSegInvoke.psn);
+      dispatch_to_wdp(e);
+    }
+
+    if (orig_event->u.RcvSegInvoke.ttr == 1) { /* Need to feed to WSP */
+      /* Create assembled event */
+
+      psn = orig_event->u.RcvSegInvoke.psn;
+      wap_event_destroy(orig_event);
+      
+      *event = assembly_sar_event(machine,psn);
+
+      gw_assert(event != NULL);
+
+      return 1;
+    }
+    return 0;
+  }
+
+  /* Not SAR message */
+  return 1;
+}
+
+static int is_wanted_sar_data (void *a, void *b) {
+  sar_info_t *s;
+  int *i;
+  s = a;
+  i = b;
+
+  if (*i == s->sar_psn) {
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
+/* 
+ * Return 0 if transaction added suscessufully, 1 overwise 
+ */
+
+static int add_sar_transaction (WTPRespMachine *machine, Octstr *data, int psn) {
+  sar_info_t *sar_info;
+
+  if (machine->sar_info == NULL) {
+    machine->sar_info = list_create();
+  }
+
+  if (list_search(machine->sar_info, &psn, is_wanted_sar_data) == NULL) {
+    sar_info = gw_malloc(sizeof(sar_info_t));
+    sar_info->sar_psn = psn;
+    sar_info->sar_data = octstr_duplicate(data);
+    list_append(machine->sar_info, sar_info);
+    return 0;
+  } else {
+    debug("wap.wtp", 0, "Duplicated psn found, ignore packet");
+    return 1;
+  } 
+}
+
+static WAPEvent *assembly_sar_event (WTPRespMachine *machine, int last_psn) {
+  WAPEvent *e;
+  int i;
+  sar_info_t *sar_info;
+  
+
+  e = wap_event_duplicate(machine->sar_invoke);
+
+  for (i = 1; i<=last_psn; i++) {
+    if ((sar_info = list_search(machine->sar_info, &i, is_wanted_sar_data)) != NULL) {
+      octstr_append(e->u.RcvInvoke.user_data,sar_info->sar_data);
+    } else {
+      debug("wap.wtp", 0, "Packet with psn %d not found",i);
+      return e;
+    }
+  }
+  return e;
+}
+
+static void sar_info_destroy(void *p) {
+  sar_info_t *sar_info;
+
+  sar_info = p;
+  
+  octstr_destroy(sar_info->sar_data);
+  gw_free(sar_info);
+}
diff -ub /home/wapgw/kannel/gateway-1.2.0/wap/wtp_resp.h wap/wtp_resp.h
--- /home/wapgw/kannel/gateway-1.2.0/wap/wtp_resp.h	Tue May 14 14:05:08 2002
+++ wap/wtp_resp.h	Thu Aug 29 18:20:13 2002
@@ -38,6 +38,7 @@
        #define ADDRTUPLE(name) WAPAddrTuple *name;
        #define ENUM(name) resp_states name;
        #define EVENT(name) WAPEvent *name;
+       #define LIST(name) List *name;
        #define MACHINE(field) field
        #include "wtp_resp_machine.def"
 };
diff -ub /home/wapgw/kannel/gateway-1.2.0/wap/wtp_resp_machine.def wap/wtp_resp_machine.def
--- /home/wapgw/kannel/gateway-1.2.0/wap/wtp_resp_machine.def	Mon Nov 20 21:55:54 2000
+++ wap/wtp_resp_machine.def	Thu Aug 29 17:44:43 2002
@@ -42,6 +42,8 @@
     #error "Macro TIMER is missing."
 #elif !defined(EVENT) 
     #error "Macro EVENT is missing."
+#elif !defined(LIST)
+    #error "Macro LIST is missing."
 #elif !defined(ADDRTUPLE)
     #error "Macro ADDRTUPLE is missing."
 #endif
@@ -63,6 +65,8 @@
                                       in the global timers list */
         EVENT(invoke_indication) /* packed wsp invoke indication - for tid
                                          verification */
+	EVENT(sar_invoke)     /* initial invoke for SAR, accumulate user_data */
+	LIST(sar_info)
 	)
 
 #undef MACHINE
@@ -71,3 +75,4 @@
 #undef TIMER
 #undef EVENT
 #undef ADDRTUPLE
+#undef LIST
diff -ub /home/wapgw/kannel/gateway-1.2.0/wap/wtp_resp_states.def wap/wtp_resp_states.def
--- /home/wapgw/kannel/gateway-1.2.0/wap/wtp_resp_states.def	Tue Mar 27 17:35:39 2001
+++ wap/wtp_resp_states.def	Wed Sep 11 13:05:33 2002
@@ -165,6 +165,15 @@
     LISTEN)
 
 /*
+ * Need to control SAR incomplete packets
+ */	
+ROW(LISTEN,
+    TimerTO_W,
+    1,
+    {},
+    LISTEN)
+
+/*
  * We must cache the newly accepted tid item, otherwise every tid after a 
  * suspected one will be validated. We use wsp event stored by the responder
  * machine.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.