RE: SAR

Igor Ivoilov <[email protected]>
Newsgroups gmane.comp.mobile.kannel.devel
Message-ID <[email protected]>
Hi all,
here is a patch to put above the one provided by Vjacheslav Chekushin to
implement
SAR result.
So with this 2 patches gw is working ok for mms proxy relay
Really I have not implemented yet error handling, it's going to be the next
step

Regards,
Igor
(www.thunderworx.com)

> -----Original Message-----
> From: Vjacheslav Chekushin [mailto:[email protected]]
> Sent: Wednesday, September 11, 2002 1:46 PM
> To: [email protected]
> Subject: SAR
> 
> 
> 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
>
sar2.patch (application/octet-stream, 15.5 KB)
diff -ub wap-slava/wap_events.def ../kannel-snapshot/wap/wap_events.def
--- wap-slava/wap_events.def	Fri Sep 13 03:43:32 2002
+++ ../kannel-snapshot/wap/wap_events.def	Fri Sep 13 05:55:02 2002
@@ -460,6 +460,15 @@
 	INTEGER(tid)
 	INTEGER(tid_ok)
 	INTEGER(rid)
+	INTEGER(psn)
+	ADDRTUPLE(addr_tuple)
+	)
+
+WAPEVENT(RcvNegativeAck, "RcvNack",
+	INTEGER(tid)
+	INTEGER(rid)
+	INTEGER(nmissing)
+	OPTIONAL_OCTSTR(missing)
 	ADDRTUPLE(addr_tuple)
 	)
 
diff -ub wap-slava/wtp.c ../kannel-snapshot/wap/wtp.c
--- wap-slava/wtp.c	Fri Sep 13 03:43:32 2002
+++ ../kannel-snapshot/wap/wtp.c	Fri Sep 13 05:55:02 2002
@@ -28,6 +28,7 @@
 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_negative_ack(WTP_PDU *pdu, WAPAddrTuple *addr_tuple);
 static WAPEvent *unpack_abort(WTP_PDU *pdu, WAPAddrTuple *addr_tuple);
 static WAPEvent *pack_error(WAPEvent *datagram);
 
@@ -108,6 +109,9 @@
      case RcvAck:
 	 return event->u.RcvAck.tid < INITIATOR_TID_LIMIT;
 
+	case RcvNegativeAck:
+		return event->u.RcvNegativeAck.tid < INITIATOR_TID_LIMIT;
+
      case RcvAbort:
 	 return event->u.RcvAbort.tid < INITIATOR_TID_LIMIT;
 
@@ -181,6 +185,8 @@
 static WAPEvent *unpack_ack(WTP_PDU *pdu, WAPAddrTuple *addr_tuple)
 {
     WAPEvent *event;
+	WTP_TPI  *tpi;
+	int	i,num_tpis;
 
     event = wap_event_create(RcvAck);
     event->u.RcvAck.tid = pdu->u.Ack.tid;
@@ -188,6 +194,30 @@
     event->u.RcvAck.rid = pdu->u.Ack.rid;
     event->u.RcvAck.addr_tuple = wap_addr_tuple_duplicate(addr_tuple);
 
+    event->u.RcvAck.psn = -1;
+	num_tpis = list_len(pdu->options);
+    for (i = 0; i < num_tpis; i++) {
+        tpi = list_get(pdu->options, i);
+		if (tpi->type == TPI_PSN ) {
+			event->u.RcvAck.psn = octstr_get_bits(tpi->data,0,8);
+			break;
+		}
+	}
+
+    return event;
+}
+
+static WAPEvent *unpack_negative_ack(WTP_PDU *pdu, WAPAddrTuple *addr_tuple)
+{
+	WAPEvent *event;
+
+	event = wap_event_create(RcvNegativeAck);
+	event->u.RcvNegativeAck.tid = pdu->u.Negative_ack.tid;
+	event->u.RcvNegativeAck.rid = pdu->u.Negative_ack.rid;
+	event->u.RcvNegativeAck.nmissing = pdu->u.Negative_ack.nmissing;
+	event->u.RcvNegativeAck.missing = octstr_duplicate(pdu->u.Negative_ack.missing);
+	event->u.RcvNegativeAck.addr_tuple = wap_addr_tuple_duplicate(addr_tuple);
+
     return event;
 }
 
@@ -272,10 +302,15 @@
     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;
 
+	case Negative_ack:
+		event = unpack_negative_ack(pdu, datagram->u.T_DUnitdata_Ind.addr_tuple);    
+		break;
+
 	case Abort:
 	    event = unpack_abort(pdu, datagram->u.T_DUnitdata_Ind.addr_tuple);
         break;         
diff -ub wap-slava/wtp.h ../kannel-snapshot/wap/wtp.h
--- wap-slava/wtp.h	Fri Sep 13 03:43:32 2002
+++ ../kannel-snapshot/wap/wtp.h	Fri Sep 13 05:55:02 2002
@@ -25,7 +25,7 @@
  */
 enum {
     NUMBER_OF_ABORT_TYPES = 2,
-    NUMBER_OF_ABORT_REASONS  = 9,
+    NUMBER_OF_ABORT_REASONS  = 10,
     NUMBER_OF_TRANSACTION_CLASSES = 3
 };
 
@@ -82,7 +82,8 @@
     WTPVERSIONZERO = 0x06,
     CAPTEMPEXCEEDED = 0x07,
     NORESPONSE = 0x08,
-    MESSAGETOOLARGE = 0x09
+	MESSAGETOOLARGE = 0x09,
+	NOTIMPLEMENTEDESAR = 0x0A
 };    
 
 /*
@@ -110,6 +111,15 @@
     RESPONDER_INDICATION = 1 
 };
 
+enum {
+	TPI_ERROR			= 0,
+	TPI_INFO			= 1,
+	TPI_OPTION			= 2,
+	TPI_PSN				= 3,
+	TPI_SDU_BOUNDARY	= 4,
+	TPI_FRAME_BOUNDARY	= 5
+};
+
 /*
  * Responder set first tid, initiator not. So all tids send by initiator are 
  * greater than 2**15.
@@ -127,11 +137,6 @@
 
 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 wap-slava/wtp_pack.c ../kannel-snapshot/wap/wtp_pack.c
--- wap-slava/wtp_pack.c	Fri Sep 13 03:43:32 2002
+++ ../kannel-snapshot/wap/wtp_pack.c	Fri Sep 13 05:55:02 2002
@@ -27,7 +27,9 @@
     ERROR_DATA = 0x00,
     INFO_DATA = 0x01,
     OPTION = 0x02,
-    PACKET_SEQUENCE_NUMBER = 0x03
+    PACKET_SEQUENCE_NUMBER = 0x03,
+	SDU_BOUNDARY = 0x04,
+	FRAME_BOUNDARY = 0x05
 };
 
 /*****************************************************************************
@@ -106,6 +108,54 @@
     return dgram;
 }
 
+WAPEvent *wtp_pack_sar_result(WTPRespMachine *machine, int psn)
+{
+	WAPEvent *dgram = NULL;
+	WTP_PDU *pdu = NULL;
+	Octstr *data = NULL;
+	int gtr,ttr;
+	
+	gw_assert(machine->sar && machine->sar->data);
+
+	if (psn > machine->sar->nsegm)
+		return dgram;
+
+	ttr = psn == machine->sar->nsegm ? 1 : 0;
+	gtr = ttr ? 0 : (psn+1)%SAR_GROUP_LEN ? 0 : 1;
+	if (gtr || ttr) 
+		machine->sar->tr = 1;
+
+	data = octstr_copy(machine->sar->data,psn*SAR_SEGM_SIZE,SAR_SEGM_SIZE);
+
+	if (!psn) {
+		pdu = wtp_pdu_create(Result);
+		pdu->u.Result.con = 0;
+		pdu->u.Result.gtr = gtr;
+		pdu->u.Result.ttr = ttr;
+		pdu->u.Result.rid = 0;
+		pdu->u.Result.tid = send_tid(machine->tid);
+		pdu->u.Result.user_data = data;
+	}
+	else {
+		pdu = wtp_pdu_create(Segmented_result);
+		pdu->u.Segmented_result.con = 0;
+		pdu->u.Segmented_result.gtr = gtr;
+		pdu->u.Segmented_result.ttr = ttr;
+		pdu->u.Segmented_result.rid = 0;
+		pdu->u.Segmented_result.tid = send_tid(machine->tid);
+		pdu->u.Segmented_result.psn = psn;
+		pdu->u.Segmented_result.user_data = data;
+	}
+
+	dgram = wap_event_create(T_DUnitdata_Req);
+	dgram->u.T_DUnitdata_Req.addr_tuple =
+		wap_addr_tuple_duplicate(machine->addr_tuple);
+	dgram->u.T_DUnitdata_Req.user_data = wtp_pdu_pack(pdu);
+	wtp_pdu_destroy(pdu);
+
+	return dgram;
+}
+
 void wtp_pack_set_rid(WAPEvent *dgram, long rid)
 {
     gw_assert(dgram != NULL);
diff -ub wap-slava/wtp_pack.h ../kannel-snapshot/wap/wtp_pack.h
--- wap-slava/wtp_pack.h	Fri Sep 13 03:43:32 2002
+++ ../kannel-snapshot/wap/wtp_pack.h	Fri Sep 13 05:55:02 2002
@@ -34,6 +34,8 @@
 
 WAPEvent *wtp_pack_result(WTPRespMachine *resp_machine, WAPEvent *event); 
 
+WAPEvent *wtp_pack_sar_result(WTPRespMachine *resp_machine, int psn); 
+
 /*
  * Create a datagram event, having abort PDU as user data. Fetches SDU
  * from WTP event, address four-tuple from WTP machine. 
diff -ub wap-slava/wtp_pdu.c ../kannel-snapshot/wap/wtp_pdu.c
--- wap-slava/wtp_pdu.c	Fri Sep 13 03:42:44 2002
+++ ../kannel-snapshot/wap/wtp_pdu.c	Fri Sep 13 05:55:02 2002
@@ -38,7 +38,7 @@
 #undef UINT
 #undef PDU
 	default:
-		warning(0, "Cannot destroy unknown WTP PDU type %d", pdu->type);
+		warning(0, "Cannot create unknown WTP PDU type %d", pdu->type);
 		break;
 	}
 
diff -ub wap-slava/wtp_resp.c ../kannel-snapshot/wap/wtp_resp.c
--- wap-slava/wtp_resp.c	Fri Sep 13 03:43:32 2002
+++ ../kannel-snapshot/wap/wtp_resp.c	Fri Sep 13 07:08:51 2002
@@ -55,6 +55,7 @@
 extern int wtp_forced_sar;
 
 static void sar_info_destroy(void *sar_info);
+static void sardata_destroy(void *sardata);
 
 /*****************************************************************************
  *
@@ -133,6 +134,9 @@
 /* static int is_wanted_sar_data (void *a, void *b); */
 static int process_sar_transaction(WTPRespMachine *machine, WAPEvent **event);
 
+static void begin_sar_result(WTPRespMachine *machine, WAPEvent *event);
+static void continue_sar_result(WTPRespMachine *machine, WAPEvent *event);
+
 /*
  * Create a datagram with an Abort PDU and send it to the WDP layer.
  */
@@ -318,18 +322,7 @@
  */
 static int erroneous_field_in(WAPEvent *event)
 {
-    /*
-     * 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;
-
-    return event->type == RcvInvoke && (event->u.RcvInvoke.version != 0 || 
-               !event->u.RcvInvoke.ttr || !event->u.RcvInvoke.gtr);
+    return event->type == RcvInvoke && event->u.RcvInvoke.version != 0;
 }
 
 /*
@@ -345,11 +338,13 @@
 	       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);
-	    } */
+	    } 
+*/
     }
 }
 
@@ -461,6 +456,15 @@
         case RcvInvoke:
 	       resp_machine = resp_machine_create(tuple, tid, 
                                               event->u.RcvInvoke.tcl);
+			/* if SAR requested */
+			if (!event->u.RcvInvoke.gtr || !event->u.RcvInvoke.ttr) {
+				resp_machine->sar = gw_malloc(sizeof(WTPSARData));
+				resp_machine->sar->nsegm = 0;
+				resp_machine->sar->csegm = 0;
+				resp_machine->sar->lsegm = 0;
+				resp_machine->sar->data = NULL;		
+			}
+
             break;
 
         /*
@@ -552,6 +556,7 @@
     #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 SARDATA(name) resp_machine->name = NULL;
     #define MACHINE(field) field
     #include "wtp_resp_machine.def"
 
@@ -589,6 +594,7 @@
     #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 SARDATA(name) sardata_destroy(resp_machine->name);
     #define MACHINE(field) field
     #include "wtp_resp_machine.def"
     gw_free(resp_machine);
@@ -819,3 +825,81 @@
   octstr_destroy(sar_info->sar_data);
   gw_free(sar_info);
 }
+
+
+static void sardata_destroy(void *p) 
+{
+	WTPSARData * sardata;
+	if (p) {
+		sardata = p;
+		octstr_destroy(sardata->data);
+		gw_free(sardata);
+	}
+}
+
+static void begin_sar_result(WTPRespMachine *resp_machine, WAPEvent *event) 
+{
+	WAPEvent *result;
+	WTPSARData *sar;
+	int psn;
+
+	gw_assert(resp_machine->sar != NULL);
+
+	sar = resp_machine->sar;
+	
+	sar->data = octstr_duplicate(event->u.TR_Result_Req.user_data);
+	sar->nsegm = (octstr_len(sar->data)-1)/SAR_SEGM_SIZE;
+	sar->tr = sar->lsegm = 0;
+	sar->csegm = -1;
+
+	debug("wap.wtp", 0, "WTP: begin_sar_result(): data len = %lu",octstr_len(sar->data));
+
+	for (psn=0;!sar->tr;psn++) {
+		result = wtp_pack_sar_result(resp_machine, psn);
+		if (sar->tr) 
+			resp_machine->result = wap_event_duplicate(result);
+
+		debug("wap.wtp", 0, "WTP: dispath_to_wdp(): psn = %u",psn);
+		
+		dispatch_to_wdp(result);
+		sar->lsegm = psn;
+	}
+
+	resp_machine->rid = 1;
+}
+
+static void continue_sar_result(WTPRespMachine *resp_machine, WAPEvent *event) 
+{
+	WAPEvent *result;
+	WTPSARData *sar;
+	int psn;
+
+	gw_assert(resp_machine->sar != NULL && event->type == RcvAck);
+	
+	sar = resp_machine->sar;
+
+	debug("wap.wtp", 0, "WTP: continue_sar_result(): lsegm=%d, nsegm=%d, csegm=%d",sar->lsegm,sar->nsegm,sar->csegm);
+
+    start_timer_R(resp_machine);
+
+	if (event->u.RcvAck.psn>sar->csegm) {
+		sar->csegm = event->u.RcvAck.psn;
+	}
+	sar->tr = 0;
+	wap_event_destroy(resp_machine->result);
+	resp_machine->result = NULL;
+
+	for (psn=sar->csegm+1;!sar->tr;psn++) {
+		result = wtp_pack_sar_result(resp_machine, psn);
+		if (sar->tr) 
+			resp_machine->result = wap_event_duplicate(result);
+
+		debug("wap.wtp", 0, "WTP: dispath_to_wdp(): psn = %u",psn);
+
+		dispatch_to_wdp(result);
+		sar->lsegm = psn;
+	}
+	
+}
+
+
diff -ub wap-slava/wtp_resp.h ../kannel-snapshot/wap/wtp_resp.h
--- wap-slava/wtp_resp.h	Fri Sep 13 03:43:32 2002
+++ ../kannel-snapshot/wap/wtp_resp.h	Fri Sep 13 05:55:02 2002
@@ -13,6 +13,27 @@
 #include "wap_events.h"
 #include "timers.h"
 
+typedef struct sar_info_t {
+	int sar_psn;
+	Octstr *sar_data;
+} sar_info_t;
+
+/*
+ * Structure to keep SAR data during transmission
+ */
+typedef struct WTPSARData {
+	int nsegm;  /* number of the last segment, i.e. total number - 1 */
+	int csegm;  /* last segment confirmed by recipient */
+	int lsegm;  /* last sent segment */
+	int tr;		/* if current psn is gtr or ttr */
+	Octstr *data;
+} WTPSARData;
+
+/* Nokia wap gw uses the size of 576, but mobiles use 1,5K size, 
+ * I will think later what is better to use
+ */
+#define		SAR_SEGM_SIZE   576
+#define		SAR_GROUP_LEN	3
 /*
  * Responder machine states and responder WTP machine.
  * See file wtp_resp_state-decl.h for comments. Note that we must define macro
@@ -39,6 +60,7 @@
        #define ENUM(name) resp_states name;
        #define EVENT(name) WAPEvent *name;
        #define LIST(name) List *name;
+	#define SARDATA(name) WTPSARData *name;
        #define MACHINE(field) field
        #include "wtp_resp_machine.def"
 };
diff -ub wap-slava/wtp_resp_machine.def ../kannel-snapshot/wap/wtp_resp_machine.def
--- wap-slava/wtp_resp_machine.def	Fri Sep 13 03:43:32 2002
+++ ../kannel-snapshot/wap/wtp_resp_machine.def	Fri Sep 13 05:55:02 2002
@@ -44,6 +44,8 @@
     #error "Macro EVENT is missing."
 #elif !defined(LIST)
     #error "Macro LIST is missing."
+#elif !defined(SARDATA)
+	#error "Macro SARDATA is missing."
 #elif !defined(ADDRTUPLE)
     #error "Macro ADDRTUPLE is missing."
 #endif
@@ -67,6 +69,7 @@
                                          verification */
 	EVENT(sar_invoke)     /* initial invoke for SAR, accumulate user_data */
 	LIST(sar_info)
+        SARDATA(sar)              /* ! NULL if were we asked for SAR */
 	)
 
 #undef MACHINE
@@ -76,3 +79,4 @@
 #undef EVENT
 #undef ADDRTUPLE
 #undef LIST
+#undef SARDATA
diff -ub wap-slava/wtp_resp_states.def ../kannel-snapshot/wap/wtp_resp_states.def
--- wap-slava/wtp_resp_states.def	Fri Sep 13 03:43:32 2002
+++ ../kannel-snapshot/wap/wtp_resp_states.def	Fri Sep 13 07:09:04 2002
@@ -284,7 +284,7 @@
 
 ROW(INVOKE_RESP_WAIT,
     TR_Result_Req,
-    1,
+    resp_machine->sar == NULL,
     {
      WAPEvent *result;
 
@@ -300,6 +300,21 @@
     },
     RESULT_RESP_WAIT)
 
+ROW(INVOKE_RESP_WAIT,
+    TR_Result_Req,
+    resp_machine->sar != NULL,
+    {
+     WAPEvent *result;
+
+     resp_machine->rcr = 0;
+
+     start_timer_R(resp_machine);
+     wap_event_destroy(resp_machine->result);
+     resp_machine->rid = 0;
+	 begin_sar_result(resp_machine, event);	
+    },
+    RESULT_RESP_WAIT)
+
 /*
  * Conditions below do not correspond wholly ones found from the spec. (If 
  * they does, user acknowledgement flag would never be used by the protocol, 
@@ -377,7 +392,7 @@
 
 ROW(RESULT_WAIT,
     TR_Result_Req,
-    1,
+    resp_machine->sar == NULL,
     {
      WAPEvent *result;
      resp_machine->rcr = 0;
@@ -393,6 +408,23 @@
     },
     RESULT_RESP_WAIT)
 
+
+ROW(RESULT_WAIT,
+    TR_Result_Req,
+    resp_machine->sar != NULL,
+    {
+     WAPEvent *result;
+     resp_machine->rcr = 0;
+
+     start_timer_R(resp_machine);
+
+     wap_event_destroy(resp_machine->result);
+     resp_machine->rid = 0;
+	 begin_sar_result(resp_machine, event);	
+    },
+    RESULT_RESP_WAIT)
+
+
 ROW(RESULT_WAIT,
     RcvAbort,
     1,
@@ -482,13 +514,21 @@
 
 ROW(RESULT_RESP_WAIT,
     RcvAck,
-    1,
+    resp_machine->sar == NULL || event->u.RcvAck.psn == resp_machine->sar->nsegm,
     {
      wsp_event = create_tr_result_cnf(resp_machine);
      wsp_session_dispatch_event(wsp_event);
     },
     LISTEN)
 
+ROW(RESULT_RESP_WAIT,
+    RcvAck,
+    resp_machine->sar != NULL && event->u.RcvAck.psn != resp_machine->sar->nsegm,
+    {
+	 continue_sar_result(resp_machine, event);	 
+    },
+    RESULT_RESP_WAIT)
+
 /*
  * Specs does not tell what to do, when wtp responder receives invoke pdu and
  * its state is RESULT_RESP_WAIT. This can happen, however: event causing the
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.