1 /* $Id$
2  * --------------------------------------------------------------------------
3  *
4  *           //=====   //===== ===//=== //===//  //       //   //===//
5  *          //        //         //    //    // //       //   //    //
6  *         //====//  //         //    //===//  //       //   //===<<
7  *              //  //         //    //       //       //   //    //
8  *       ======//  //=====    //    //       //=====  //   //===//
9  *
10  * -------------- An SCTP implementation according to RFC 4960 --------------
11  *
12  * Copyright (C) 2000 by Siemens AG, Munich, Germany.
13  * Copyright (C) 2001-2004 Andreas Jungmaier
14  * Copyright (C) 2004-2019 Thomas Dreibholz
15  *
16  * Acknowledgements:
17  * Realized in co-operation between Siemens AG and the University of
18  * Duisburg-Essen, Institute for Experimental Mathematics, Computer
19  * Networking Technology group.
20  * This work was partially funded by the Bundesministerium fuer Bildung und
21  * Forschung (BMBF) of the Federal Republic of Germany
22  * (Förderkennzeichen 01AK045).
23  * The authors alone are responsible for the contents.
24  *
25  * This library is free software: you can redistribute it and/or modify it
26  * under the terms of the GNU Lesser General Public License as published by
27  * the Free Software Foundation, either version 2.1 of the License, or
28  * (at your option) any later version.
29  *
30  * This library is distributed in the hope that it will be useful,
31  * but WITHOUT ANY WARRANTY; without even the implied warranty of
32  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
33  * GNU General Public License for more details.
34  *
35  * You should have received a copy of the GNU Lesser General Public License
36  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
37  *
38  * Contact: sctp-discussion@sctp.de
39  *          dreibh@iem.uni-due.de
40  *          tuexen@fh-muenster.de
41  *          andreas.jungmaier@web.de
42  */
43 
44 #include "globals.h"
45 #include "chunkHandler.h"
46 #include "SCTP-control.h"
47 #include "adaptation.h"
48 #include "bundling.h"
49 #include "pathmanagement.h"
50 
51 /*------------------------ defines -----------------------------------------------------------*/
52 #define RTO_ALPHA            0.125
53 #define RTO_BETA              0.25
54 
55 
56 /*----------------------- Typedefs ------------------------------------------------------------*/
57 
58 
59 /**
60  * this struct contains the necessary data per (destination or) path.
61  * There may be more than one within an association
62  */
63 typedef struct PATHDATA
64 {
65     /*@{ */
66     /** operational state of pathmanagement for one path */
67     short state;
68     /** true if heartbeat is enabled */
69     boolean heartbeatEnabled;
70     /** true as long as RTO-Calc. has been done */
71     boolean firstRTO;
72     /** Only once per HB-intervall */
73     boolean timerBackoff;
74     /** set to true when data chunks are acknowledged */
75     boolean chunksAcked;
76     /** TRUE, if chunks have been sent over that path within last RTO */
77     boolean chunksSent;
78     /** set to true when a heartbeat is sent. */
79     boolean heartbeatSent;
80     /** set to true when a hearbeat is acknowledged and to false when a
81        heartbeat is sent when the heartbeat timer expires. */
82     boolean heartbeatAcked;
83     /** Counter for retransmissions on a single path */
84     unsigned int pathRetranscount;
85     /** Retransmission time out used for all retrans. timers */
86     unsigned int rto;
87     /** smoothed round trip time */
88     unsigned int srtt;
89     /** round trip time variation */
90     unsigned int rttvar;
91     /** defines the rate at which heartbeats are sent */
92     unsigned int heartbeatIntervall;
93     /** ID of the heartbeat timer */
94     TimerID hearbeatTimer;
95     /** time of last rto update */
96     struct timeval rto_update;
97     /** ID of path */
98     unsigned int pathID;
99     /*@} */
100 } PathData;
101 
102 
103 /**
104  * this struct contains all necessary data for one instance of the path management
105  * module. There is one such module per existing association.
106  */
107 typedef struct PATHMANDATA
108 {
109     /*@{ */
110     /** stores the current primary path */
111     short primaryPath;
112     /** the number of paths used by this assoc. */
113     short numberOfPaths;
114     /** Counter for all retransmissions over all paths */
115     unsigned int peerRetranscount;
116     /** pointer to path-specific data */
117     PathData *pathData;
118     /** association-ID */
119     unsigned int associationID;
120     /** maximum retransmissions per path parameter */
121     int maxPathRetransmissions;
122     /** initial RTO, a configurable parameter */
123     int rto_initial;
124     /** minimum RTO, a configurable parameter */
125     int rto_min;
126     /** maximum RTO, a configurable parameter */
127     int rto_max;
128     /*@} */
129 } PathmanData;
130 
131 
132 /*----------------------Declares ----------------------------------------------------------------*/
133 
134 /**
135  * this pointer is set to point to the current asssociation's path management struct
136  * it becomes zero after we have treated an incoming/outgoing datagram
137  */
138 PathmanData *pmData;
139 
140 /*-------------------------- Function Implementations -------------------------------------------*/
141 
142 /*------------------- Internal Functions --------------------------------------------------------*/
143 
144 /**
145   return the current system time converted to a value of  milliseconds.
146   MSB of tv_sec field are removed in order
147   to make representation in millisecs possible. This done by taking the remainder of
148   a division by 1728000 = 20x24x60x60, restarting millisecs count every 20 days.
149   @return unsigned 32 bit value representing system time in milliseconds. Hmmmh.
150 */
pm_getTime(void)151 unsigned int pm_getTime(void)
152 {
153     unsigned int curTimeMilli;
154     struct timeval curTime;
155 
156     adl_gettime(&curTime);
157 
158     /* modulo operation overlfows every 20 days */
159     curTimeMilli = (curTime.tv_sec % 1728000) * 1000 + curTime.tv_usec / 1000;
160 
161     return curTimeMilli;
162 }                               /* end: pm_ sctp_getTime */
163 
164 
165 /**
166  *  handleChunksRetransmitted is called whenever datachunks are retransmitted or a hearbeat-request
167  *  has not been acknowledged within the current heartbeat-intervall. It increases path- and peer-
168  *  retransmission counters and compares these counters to the corresonding thresholds.
169  *  @param  pathID index to the path that CAUSED retransmission
170  *  @return TRUE if association was deleted, FALSE if not
171  */
handleChunksRetransmitted(short pathID)172 static gboolean handleChunksRetransmitted(short pathID)
173 {
174     short pID;
175     boolean allPathsInactive;
176     PathmanData *old_pmData;
177 
178     if (!pmData->pathData) {
179         error_logi(ERROR_MAJOR, "handleChunksRetransmitted(%d): Path Data Structures not initialized yet, returning !", pathID);
180         return FALSE;
181     }
182 
183     event_logiii(INTERNAL_EVENT_0,
184                  "handleChunksRetransmitted(%d) : path-rtx-count==%u, peer-rtx-count==%u",
185                  pathID, pmData->pathData[pathID].pathRetranscount, pmData->peerRetranscount);
186 
187     if (pmData->pathData[pathID].state == PM_PATH_UNCONFIRMED) {
188 
189         pmData->pathData[pathID].pathRetranscount++;
190 
191     } else if (pmData->pathData[pathID].state == PM_ACTIVE) {
192 
193         pmData->pathData[pathID].pathRetranscount++;
194         pmData->peerRetranscount++;
195 
196     } else {
197         event_log(INTERNAL_EVENT_0,
198                   "handleChunksRetransmitted: ignored, because already inactive");
199         return FALSE;
200     }
201 
202     if (pmData->peerRetranscount >= (unsigned int)sci_getMaxAssocRetransmissions()) {
203         mdi_deleteCurrentAssociation();
204         mdi_communicationLostNotif(SCTP_COMM_LOST_EXCEEDED_RETRANSMISSIONS);
205         mdi_clearAssociationData();
206 
207         event_log(INTERNAL_EVENT_0, "handleChunksRetransmitted: communication lost");
208         return TRUE;
209     }
210 
211     if (pmData->pathData[pathID].pathRetranscount >= (unsigned int)pmData->maxPathRetransmissions) {
212         /* Set state of this path to inactive and notify change of state to ULP */
213         pmData->pathData[pathID].state = PM_INACTIVE;
214         event_logi(INTERNAL_EVENT_0, "handleChunksRetransmitted: path %d to INACTIVE ", pathID);
215         /* check if an active path is left */
216         allPathsInactive = TRUE;
217         for (pID = 0; pID < pmData->numberOfPaths; pID++) {
218             if (pmData->pathData[pID].state == PM_ACTIVE) {
219                 allPathsInactive = FALSE;
220             }
221         }
222         if (allPathsInactive) {
223             /* No active parts are left, communication lost to ULP */
224             mdi_deleteCurrentAssociation();
225             mdi_communicationLostNotif(SCTP_COMM_LOST_ENDPOINT_UNREACHABLE);
226             /* will be called later anyway !
227                 mdi_clearAssociationData(); */
228             event_log(INTERNAL_EVENT_0,
229                       "handleChunksRetransmitted: communication lost (all paths are INACTIVE)");
230             return TRUE;
231         } else {
232             old_pmData = pmData;
233             mdi_networkStatusChangeNotif(pathID, PM_INACTIVE);
234             pmData = old_pmData;
235         }
236     }
237 
238     return FALSE;
239 }                               /* end: handleChunksRetransmitted */
240 
241 
242 
243 
244 /**
245  * Function is used to update RTT, SRTT, RTO values after chunks have been acked.
246  * CHECKME : this function is called too often with RTO == 0;
247  * Is there one update per RTT ?
248  * @param  pathID index of the path where data was acked
249  * @param  newRTT new RTT measured, when data was acked, or zero if it was retransmitted
250 */
handleChunksAcked(short pathID,unsigned int newRTT)251 static void handleChunksAcked(short pathID, unsigned int newRTT)
252 {
253 
254     if (!pmData->pathData) {
255         error_logi(ERROR_MAJOR, "handleChunksAcked(%d): Path Data Structures not initialized yet, returning !", pathID);
256         return;
257     }
258 
259     event_logii(INTERNAL_EVENT_0, "handleChunksAcked: pathID: %u, new RTT: %u msecs", pathID, newRTT);
260 
261     if (newRTT > 0) {
262         /* RTO measurement done */
263         /* calculate new RTO, SRTT and RTTVAR */
264         if (pmData->pathData[pathID].firstRTO) {
265             pmData->pathData[pathID].srtt = newRTT;
266             pmData->pathData[pathID].rttvar = max(newRTT / 2, GRANULARITY);;
267             pmData->pathData[pathID].rto = max(min(newRTT * 3, (unsigned int)pmData->rto_max), (unsigned int)pmData->rto_min);
268             pmData->pathData[pathID].firstRTO = FALSE;
269         } else {
270             pmData->pathData[pathID].rttvar = (unsigned int)
271                 ((1. - RTO_BETA) * pmData->pathData[pathID].rttvar +
272                 RTO_BETA * abs((int)pmData->pathData[pathID].srtt - (int)newRTT));
273             pmData->pathData[pathID].rttvar = max((unsigned int)pmData->pathData[pathID].rttvar, GRANULARITY);
274 
275             pmData->pathData[pathID].srtt = (unsigned int)
276                 ((1. - RTO_ALPHA) * pmData->pathData[pathID].srtt + RTO_ALPHA * newRTT);
277 
278             pmData->pathData[pathID].rto = pmData->pathData[pathID].srtt +
279                 4 * pmData->pathData[pathID].rttvar;
280             pmData->pathData[pathID].rto = max(min((unsigned int)pmData->pathData[pathID].rto, (unsigned int)pmData->rto_max), (unsigned int)pmData->rto_min);
281         }
282         event_logiii(INTERNAL_EVENT_0,
283                      "handleChunksAcked: RTO update done: RTTVAR: %u msecs, SRTT: %u msecs, RTO: %u msecs",
284                      pmData->pathData[pathID].rttvar,
285                      pmData->pathData[pathID].srtt, pmData->pathData[pathID].rto);
286     } else {
287         event_log(INTERNAL_EVENT_0, "handleChunksAcked: chunks acked without RTO-update");
288     }
289 
290     /* reset counters */
291     pmData->pathData[pathID].pathRetranscount = 0;
292     pmData->peerRetranscount = 0;
293 }                               /* end: handleChunksAcked */
294 
295 
296 
297 /*----------------- Functions to answer peer HB requests -----------------------------------------*/
298 
299 /**
300   pm_heartbeat is called when a heartbeat was received from the peer.
301   This function just takes that chunk, and sends it back.
302   @param heartbeatChunk pointer to the heartbeat chunk
303   @param source_address address we received the HB chunk from (and where it is echoed)
304 */
pm_heartbeat(SCTP_heartbeat * heartbeatChunk,unsigned int source_address)305 void pm_heartbeat(SCTP_heartbeat * heartbeatChunk, unsigned int source_address)
306 {
307     heartbeatChunk->chunk_header.chunk_id = CHUNK_HBACK;
308 
309     bu_put_Ctrl_Chunk((SCTP_simple_chunk *) heartbeatChunk, &source_address);
310     bu_sendAllChunks(&source_address);
311 }                               /* end: pm_heartbeat */
312 
313 
314 
315 /*------------------- Signals --------------------------------------------------------------------*/
316 
317 /*------------------- Signals from the Unix-Interface --------------------------------------------*/
318 
319 /**
320   pm_heartbeatTimer is called by the adaption-layer when the heartbeat timer expires.
321   It may set the path to inactive, or restart timer, or even cause COMM LOST
322   As all timer callbacks, it takes three arguments  (two pointers to necessary data)
323   @param timerID  ID of the HB timer that expired.
324   @param associationIDvoid  pointer to the association-ID
325   @param pathIDvoid         pointer to the path-ID
326 */
pm_heartbeatTimer(TimerID timerID,void * associationIDvoid,void * pathIDvoid)327 void pm_heartbeatTimer(TimerID timerID, void *associationIDvoid, void *pathIDvoid)
328 {
329     unsigned int associationID;
330     unsigned int pathID;
331     ChunkID heartbeatCID;
332     gboolean removed_association = FALSE;
333 
334     associationID = *((unsigned int *) associationIDvoid);
335     pathID = *((unsigned int *) pathIDvoid);
336     if (mdi_setAssociationData(associationID)) {
337         /* error log: expired timer refers to a non existent association. */
338         error_logi(ERROR_MAJOR,
339                    "init timer expired association %08u does not exist", associationID);
340         return;
341     }
342     pmData = (PathmanData *) mdi_readPathMan();
343     if (pmData == NULL) {
344         error_log(ERROR_MAJOR, "pm_heartbeatTimer: mdi_readPathMan failed");
345         mdi_clearAssociationData();
346         return;
347     }
348     if (!(pathID >= 0 && pathID < (unsigned int)pmData->numberOfPaths)) {
349         error_logi(ERROR_MAJOR, "pm_heartbeatTimer: invalid path ID %d", pathID);
350         mdi_clearAssociationData();
351         return;
352     }
353     event_logi(INTERNAL_EVENT_0, "Heartbeat timer expired for path %u", pathID);
354 
355     if (pmData->pathData[pathID].heartbeatSent && !pmData->pathData[pathID].heartbeatAcked) {
356         /* Heartbeat has been sent and not acknowledged: handle as retransmission */
357         if (pmData->pathData[pathID].state == PM_ACTIVE) {
358             /* Handling of unacked heartbeats is the same as that of unacked data chunks.
359                The state after calling pm_chunksRetransmitted may have changed to inactive. */
360            removed_association = handleChunksRetransmitted((short)pathID);
361            if (removed_association)
362                 event_logi(INTERNAL_EVENT_0, "Association was removed by handleChunksRetransmitted(%u)!!!!",pathID);
363         } else if (pmData->pathData[pathID].state == PM_INACTIVE) {
364             /* path already inactive, dont increase counter etc. */
365             ;
366         }
367 
368         if (!removed_association) {
369             if (!pmData->pathData[pathID].timerBackoff) {
370                 /* Timer backoff */
371                 pmData->pathData[pathID].rto = min(2 * pmData->pathData[pathID].rto, (unsigned int)pmData->rto_max);
372                 event_logii(VERBOSE, "Backing off timer : Path %d, RTO= %u", pathID,pmData->pathData[pathID].rto);
373             }
374         }
375     }
376 
377     if (!removed_association &&
378         !pmData->pathData[pathID].chunksAcked &&
379          pmData->pathData[pathID].heartbeatEnabled &&
380         !pmData->pathData[pathID].chunksSent) {
381         /* Remark: If commLost is detected in handleChunksRetransmitted, the current association
382            is marked for deletetion. Doing so, all timers are stop. The HB-timers are
383            stopped by calling pm_disableHB in mdi_deleteCurrentAssociation. This is why
384            heartBeatEnabled is checked above.
385          */
386         /* send heartbeat if no chunks have been acked in the last HB-intervall (path is idle). */
387         event_log(VERBOSE, "--------------> Sending HB");
388         heartbeatCID = ch_makeHeartbeat(pm_getTime(), pathID);
389         bu_put_Ctrl_Chunk(ch_chunkString(heartbeatCID), &pathID);
390         bu_sendAllChunks(&pathID);
391         ch_deleteChunk(heartbeatCID);
392         pmData->pathData[pathID].heartbeatSent = TRUE;
393     } else if (!removed_association) {
394         pmData->pathData[pathID].heartbeatSent = FALSE;
395     }
396 
397     if (!removed_association) {
398         if (pmData->pathData[pathID].heartbeatEnabled) {
399             /* heartbeat could have been disabled when the association went down after commLost
400                detected in handleChunksRetransmitted */
401                pmData->pathData[pathID].hearbeatTimer =
402                    adl_startTimer(pmData->pathData[pathID].heartbeatIntervall +
403                                     pmData->pathData[pathID].rto,
404                                     &pm_heartbeatTimer,
405                                     TIMER_TYPE_HEARTBEAT,
406                                     (void *) &pmData->associationID,
407                                     (void *) &pmData->pathData[pathID].pathID);
408 
409                    /* reset this flag, so we can check, whether the path was idle */
410                    pmData->pathData[pathID].chunksSent = FALSE;
411 
412                    event_logiii(INTERNAL_EVENT_0,
413                         "Heartbeat timer started with %u msecs for path %u, RTO=%u msecs",
414                         (pmData->pathData[pathID].heartbeatIntervall+pmData->pathData[pathID].rto), pathID,
415                         pmData->pathData[pathID].rto);
416         }
417     }
418 
419     if (!removed_association) {
420         pmData->pathData[pathID].heartbeatAcked = FALSE;
421         pmData->pathData[pathID].timerBackoff = FALSE;
422         pmData->pathData[pathID].chunksAcked = FALSE;
423     }
424     mdi_clearAssociationData();
425 }                               /* end: pm_heartbeatTimer */
426 
427 
428 /**
429  * simple function that sends a heartbeat chunk to the indicated address
430  * @param  pathID index to the address, where HB is to be sent to
431  */
pm_doHB(gshort pathID)432 int pm_doHB(gshort pathID)
433 {
434     ChunkID heartbeatCID;
435     guint32 pid;
436 
437     pmData = (PathmanData *) mdi_readPathMan();
438 
439     if (pmData == NULL) {
440         error_log(ERROR_MAJOR, "pm_doHB: mdi_readPathMan failed");
441         return SCTP_MODULE_NOT_FOUND;
442     }
443     if (!pmData->pathData) {
444         error_logi(ERROR_MAJOR, "pm_doHB(%d): Path Data Structures not initialized yet, returning !", pathID);
445         return SCTP_UNSPECIFIED_ERROR;
446     }
447 
448     if (!(pathID >= 0 && pathID < pmData->numberOfPaths)) {
449         error_logi(ERROR_MAJOR, "pm_doHB : invalid path ID: %d", pathID);
450         return SCTP_PARAMETER_PROBLEM;
451     }
452     pid = (guint32)pathID;
453     heartbeatCID = ch_makeHeartbeat(pm_getTime(), pathID);
454     bu_put_Ctrl_Chunk(ch_chunkString(heartbeatCID),&pid);
455     bu_sendAllChunks(&pid);
456     ch_deleteChunk(heartbeatCID);
457     pmData->pathData[pathID].heartbeatSent = TRUE;
458 
459     return SCTP_SUCCESS;
460 }
461 
462 
463 /**
464  * pm_heartbeatAck is called when a heartbeat acknowledgement was received from the peer.
465  * checks RTTs, normally resets error counters, may set path back to ACTIVE state
466  * @param heartbeatChunk pointer to the received heartbeat ack chunk
467  */
pm_heartbeatAck(SCTP_heartbeat * heartbeatChunk)468 void pm_heartbeatAck(SCTP_heartbeat * heartbeatChunk)
469 {
470     unsigned int roundtripTime;
471     unsigned int sendingTime;
472     short pathID;
473     ChunkID heartbeatCID;
474     PathmanData *old_pmData = NULL;
475     gboolean hbSignatureOkay = FALSE;
476 
477     pmData = (PathmanData *) mdi_readPathMan();
478 
479     if (pmData == NULL) {
480         error_log(ERROR_MAJOR, "pm_heartbeatAck: mdi_readPathMan failed");
481         return;
482     }
483     if (!pmData->pathData) {
484         error_log(ERROR_MAJOR, "pm_heartbeatAck: Path Data Structures not initialized yet, returning !");
485         return;
486     }
487 
488     heartbeatCID = ch_makeChunk((SCTP_simple_chunk *) heartbeatChunk);
489     pathID = ch_HBpathID(heartbeatCID);
490     sendingTime = ch_HBsendingTime(heartbeatCID);
491     roundtripTime = pm_getTime() - sendingTime;
492     event_logii(INTERNAL_EVENT_0, "HBAck for path %u, RTT = %u msecs", pathID, roundtripTime);
493 
494     hbSignatureOkay = ch_verifyHeartbeat(heartbeatCID);
495     event_logi(EXTERNAL_EVENT, "HB Signature is %s", (hbSignatureOkay == TRUE)?"correct":"FALSE");
496 
497     if (hbSignatureOkay == FALSE) {
498         error_log(ERROR_FATAL, "pm_heartbeatAck: FALSE SIGNATURE !!!!!!!!!!!!!!!");
499         return;
500     }
501 
502     ch_forgetChunk(heartbeatCID);
503 
504     if (!(pathID >= 0 && pathID < pmData->numberOfPaths)) {
505         error_logi(ERROR_MAJOR, "pm_heartbeatAck: invalid path ID %d", pathID);
506         return;
507     }
508 
509     /* this also resets error counters */
510     handleChunksAcked(pathID, roundtripTime);
511 
512     if (pmData->pathData[pathID].state == PM_INACTIVE || pmData->pathData[pathID].state == PM_PATH_UNCONFIRMED) {
513         /* Handling of acked heartbeats is the simular that that of acked data chunks. */
514         /* change to the active state */
515         pmData->pathData[pathID].state = PM_ACTIVE;
516         event_logi(INTERNAL_EVENT_0, "pathID %d changed to ACTIVE", pathID);
517         old_pmData = pmData;
518         mdi_networkStatusChangeNotif(pathID, PM_ACTIVE);
519         pmData = old_pmData;
520 
521         /* restart timer with new RTO */
522         sctp_stopTimer(pmData->pathData[pathID].hearbeatTimer);
523         pmData->pathData[pathID].hearbeatTimer =
524             adl_startTimer( (pmData->pathData[pathID].heartbeatIntervall + pmData->pathData[pathID].rto),
525                             &pm_heartbeatTimer,
526                             TIMER_TYPE_HEARTBEAT,
527                             (void *) &pmData->associationID,
528                             (void *) &pmData->pathData[pathID].pathID);
529     }
530     pmData->pathData[pathID].heartbeatAcked = TRUE;
531     pmData->pathData[pathID].timerBackoff = FALSE;
532 
533 }                               /* end: pm_heartbeatAck */
534 
535 
536 
537 /*------------------- Signals from SCTP internal modules -----------------------------------------*/
538 
539 /**
540  * pm_chunksAcked is called by reliable transfer whenever chunks have been acknowledged.
541  * @param pathID   last path-ID where chunks were sent to (and thus probably acked from)
542  * @param newRTT   the newly determined RTT in milliseconds, and 0 if retransmitted chunks had been acked
543  */
pm_chunksAcked(short pathID,unsigned int newRTT)544 void pm_chunksAcked(short pathID, unsigned int newRTT)
545 {
546     struct timeval now;
547 
548     pmData = (PathmanData *) mdi_readPathMan();
549 
550     if (pmData == NULL) {
551         error_log(ERROR_MAJOR, "pm_chunksAcked: mdi_readPathMan failed");
552         return;
553     }
554     if (!pmData->pathData) {
555         error_logii(ERROR_MAJOR, "pm_chunksAcked(%d, %u): Path Data Structures not initialized yet, returning !",
556                     pathID, newRTT);
557         return;
558     }
559 
560     if (!(pathID >= 0 && pathID < pmData->numberOfPaths)) {
561         error_logi(ERROR_MAJOR, "pm_chunksAcked: invalid path ID: %d", pathID);
562         return;
563     }
564 
565     if (newRTT > (unsigned int)pmData->rto_max)
566         error_logi(ERROR_MINOR, "pm_chunksAcked: Warning: RTO > RTO_MAX: %d", newRTT);
567 
568     newRTT = min(newRTT, (unsigned int)pmData->rto_max);
569 
570     if (pmData->pathData[pathID].state == PM_ACTIVE) {
571         /* Update RTO only if is the first data chunk acknowldged in this RTT intervall. */
572         adl_gettime(&now);
573         if (timercmp(&now, &(pmData->pathData[pathID].rto_update), < )) {
574             event_logiiii(VERBOSE, "pm_chunksAcked: now %lu sec, %lu usec - no update before %lu sec, %lu usec",
575                         now.tv_sec, now.tv_usec,
576                         pmData->pathData[pathID].rto_update.tv_sec,
577                         pmData->pathData[pathID].rto_update.tv_usec);
578             newRTT = 0;
579         } else {
580             if (newRTT != 0) {
581                 /* only if actually new valid RTT measurement is taking place, do update the time */
582                 pmData->pathData[pathID].rto_update = now;
583                 adl_add_msecs_totime(&(pmData->pathData[pathID].rto_update), pmData->pathData[pathID].srtt);
584             }
585         }
586         handleChunksAcked(pathID, newRTT);
587         pmData->pathData[pathID].chunksAcked = TRUE;
588     } else {
589         /* FIX :::::::
590             we got an ACK possibly from on an inactive path */
591         /* immediately send out a Heartbeat on that path, then when we get */
592         /* a HB-ACK, we can set the path back to ACTIVE */
593         /* when original newRTT is 0 then we got a RTX-SACK, else if we are */
594         /* inactive, get ACTIVE */
595         /* Nay, nay nay !   stale acknowledgement, silently discard */
596         return;
597     }
598 }                               /* end: pm_chunksAcked */
599 
600 
601 /**
602  * helper function, that simply sets the chunksSent flag of this path management instance to TRUE
603  * @param pathID  index of the address, where flag is set
604  */
pm_chunksSentOn(short pathID)605 void pm_chunksSentOn(short pathID)
606 {
607     pmData = (PathmanData *) mdi_readPathMan();
608 
609     if (pmData == NULL) {
610         error_log(ERROR_MAJOR, "pm_chunksSentOn: mdi_readPathMan failed");
611         return;
612     }
613     if (pmData->pathData == NULL) {
614         error_logi(ERROR_MAJOR, "pm_chunksSentOn(%d): Path Data Structures not initialized yet, returning !", pathID);
615         return;
616     }
617 
618     if (!(pathID >= 0 && pathID < pmData->numberOfPaths)) {
619         error_logi(ERROR_MAJOR, "pm_chunksSentOn: invalid path ID: %d", pathID);
620         return;
621     }
622     event_logi(VERBOSE, "Calling pm_chunksSentOn(%d)", pathID);
623     pmData->pathData[pathID].chunksSent = TRUE;
624 
625 }
626 
627 
628 /**
629   pm_chunksRetransmitted is called by reliable transfer whenever chunks have been retransmitted.
630   @param  pathID  address index, where timeout has occurred (i.e. which caused retransmission)
631 */
pm_chunksRetransmitted(short pathID)632 gboolean pm_chunksRetransmitted(short pathID)
633 {
634     gboolean removed_association = FALSE;
635     pmData = (PathmanData *) mdi_readPathMan();
636 
637     if (pmData == NULL) {
638         error_log(ERROR_MAJOR, "pm_chunksRetransmitted: mdi_readPathMan failed");
639         return removed_association;
640     }
641     if (pmData->pathData == NULL) {
642         error_logi(ERROR_MAJOR, "pm_chunksRetransmitted(%d): Path Data Structures not initialized yet, returning !", pathID);
643         return removed_association;
644     }
645 
646     if (!(pathID >= 0 && pathID < pmData->numberOfPaths)) {
647         error_logi(ERROR_MAJOR, "pm_chunksRetransmitted: invalid path ID %d", pathID);
648         return removed_association;
649     }
650 
651     if (pmData->pathData[pathID].state == PM_INACTIVE) {
652         /* stale acknowledgement, silently discard */
653         error_logi(ERROR_MINOR,
654                    "pm_chunksRetransmitted: retransmissions over inactive path %d", pathID);
655         return removed_association;
656     } else {
657         removed_association = handleChunksRetransmitted(pathID);
658     }
659     return removed_association;
660 }                               /* end: pm_chunksRetransmitted */
661 
662 
663 
664 /**
665   pm_rto_backoff is called by reliable transfer when the T3 retransmission timer expires.
666   Each call of this function may double the RTO (timer back off).
667   @param pathID  index of the address where the timeout occurred
668 */
pm_rto_backoff(short pathID)669 void pm_rto_backoff(short pathID)
670 {
671     pmData = (PathmanData *) mdi_readPathMan();
672 
673     if (pmData == NULL) {
674         error_log(ERROR_MAJOR, "pm_rto_backoff: mdi_readPathMan failed");
675         return;
676     }
677     if (pmData->pathData == NULL) {
678         error_logi(ERROR_MAJOR, "pm_rto_backoff(%d): Path Data Structures not initialized yet, returning !", pathID);
679         return;
680     }
681 
682     if (!(pathID >= 0 && pathID < pmData->numberOfPaths)) {
683         error_logi(ERROR_MAJOR, "pm_rto_backoff: invalid path ID %d", pathID);
684         return;
685     }
686 
687     if (pmData->pathData[pathID].state == PM_ACTIVE) {
688         /* Backoff timer anyway ! */
689         pmData->pathData[pathID].rto = min(2 * pmData->pathData[pathID].rto, (unsigned int)pmData->rto_max);
690 
691         event_logii(INTERNAL_EVENT_0,
692                         "pm_rto_backoff called for path %u: new RTO =%d",
693                         pathID, pmData->pathData[pathID].rto);
694         pmData->pathData[pathID].timerBackoff = TRUE;
695     } else {
696         /* stale acknowledgement, silently discard */
697         error_logi(ERROR_MINOR, "pm_rto_backoff: timer backoff for an inactive path %d", pathID);
698     }
699 }                               /* end pm_rto_backoff */
700 
701 
702 
703 /*------------------- Functions called by the ULP ------------------------------------------------*/
704 
705 /**
706   pm_enableHB is called when ULP wants to enable heartbeat.
707   @param  pathID index of address, where we sent the HBs to
708   @param  hearbeatIntervall time in msecs, that is to be added to the RTT, before sending HB
709   @return error code, 0 for success, 1 for error (i.e. address index too large)
710 */
pm_enableHB(short pathID,unsigned int hearbeatIntervall)711 int pm_enableHB(short pathID, unsigned int hearbeatIntervall)
712 {
713     pmData = (PathmanData *) mdi_readPathMan();
714 
715     if (pmData == NULL) {
716         error_log(ERROR_MAJOR, "pm_enableHB: mdi_readPathMan failed");
717         return SCTP_MODULE_NOT_FOUND;
718     }
719     if (pmData->pathData == NULL) {
720         error_logii(ERROR_MAJOR, "pm_enableHB(%d,%u): Path Data Structures not initialized yet, returning !",
721             pathID, hearbeatIntervall);
722         return SCTP_MODULE_NOT_FOUND;
723     }
724 
725     if (!(pathID >= 0 && pathID < pmData->numberOfPaths)) {
726         error_logi(ERROR_MAJOR, "pm_enableHB: invalid path ID %d", pathID);
727         return SCTP_PARAMETER_PROBLEM;
728     }
729 
730     pmData->pathData[pathID].heartbeatIntervall = hearbeatIntervall;
731 
732     event_logii(VERBOSE, "pm_enableHB(%d): chose interval %u msecs",pathID,hearbeatIntervall);
733 
734 
735     if (!pmData->pathData[pathID].heartbeatEnabled) {
736         pmData->pathData[pathID].heartbeatEnabled = TRUE;
737 
738         pmData->pathData[pathID].firstRTO = TRUE;
739         pmData->pathData[pathID].pathRetranscount = 0;
740         pmData->peerRetranscount = 0;
741 
742         pmData->pathData[pathID].heartbeatSent = FALSE;
743         pmData->pathData[pathID].heartbeatAcked = FALSE;
744         pmData->pathData[pathID].timerBackoff = FALSE;
745         pmData->pathData[pathID].chunksAcked = FALSE;
746         pmData->pathData[pathID].chunksSent = FALSE;
747 
748         pmData->pathData[pathID].rto = pmData->rto_initial;
749         pmData->pathData[pathID].srtt = pmData->rto_initial;
750         pmData->pathData[pathID].rttvar = 0;
751         pmData->pathData[pathID].hearbeatTimer =
752             adl_startTimer((pmData->pathData[pathID].heartbeatIntervall+pmData->pathData[pathID].rto),
753                             &pm_heartbeatTimer,
754                             TIMER_TYPE_HEARTBEAT,
755                             (void *) &pmData->associationID,
756                             (void *) &pmData->pathData[pathID].pathID);
757         event_logi(VERBOSE,
758                    "pm_enableHB: started timer - going off in %u msecs",
759                    pmData->pathData[pathID].heartbeatIntervall+pmData->pathData[pathID].rto);
760     } else {
761         pmData->pathData[pathID].hearbeatTimer =
762             adl_restartTimer(pmData->pathData[pathID].hearbeatTimer,
763                                      (pmData->pathData[pathID].heartbeatIntervall+pmData->pathData[pathID].rto));
764         pmData->pathData[pathID].chunksSent = FALSE;
765         event_logi(VERBOSE,
766                    "pm_enableHB: restarted timer - going off in %u msecs",
767                    pmData->pathData[pathID].heartbeatIntervall+pmData->pathData[pathID].rto);
768 
769     }
770     return SCTP_SUCCESS;
771 }                               /* end: pm_enableHB */
772 
773 
774 /**
775   pm_disableAllHB is usually called on shutdown to disable all heartbeats.
776 */
pm_disableAllHB(void)777 void pm_disableAllHB(void)
778 {
779     short pathID;
780 
781     pmData = (PathmanData *) mdi_readPathMan();
782 
783     if (pmData == NULL) {
784         error_log(ERROR_MAJOR, "pm_disableAllHB: mdi_readPathMan failed");
785         return;
786     }
787 
788     if (pmData->pathData == NULL) {
789         error_log(ERROR_MAJOR, "pm_disableAllHB: no paths set");
790         return;
791     }
792 
793     for (pathID = 0; pathID < pmData->numberOfPaths; pathID++) {
794         if (pmData->pathData[pathID].heartbeatEnabled) {
795             sctp_stopTimer(pmData->pathData[pathID].hearbeatTimer);
796             pmData->pathData[pathID].hearbeatTimer = 0;
797             pmData->pathData[pathID].heartbeatEnabled = FALSE;
798             event_logi(INTERNAL_EVENT_0, "pm_disableAllHB: path %d disabled", (unsigned int) pathID);
799         }
800     }
801 }                               /* end: pm_disableAllHB */
802 
803 
804 
805 /**
806   pm_disableHB is called to disable heartbeat for one specific path id.
807   @param  pathID index of  address, where HBs should not be sent anymore
808   @return error code: 0 for success, 1 for error (i.e. pathID too large)
809 */
pm_disableHB(short pathID)810 int pm_disableHB(short pathID)
811 {
812     pmData = (PathmanData *) mdi_readPathMan();
813 
814     if (pmData == NULL) {
815         error_log(ERROR_MAJOR, "pm_disableHB: mdi_readPathMan failed");
816         return SCTP_MODULE_NOT_FOUND;
817     }
818 
819     if (pmData->pathData == NULL) {
820         error_logi(ERROR_MAJOR, "pm_disableHB(%d): no paths set", pathID);
821         return SCTP_MODULE_NOT_FOUND;
822     }
823 
824     if (!(pathID >= 0 && pathID < pmData->numberOfPaths)) {
825         error_logi(ERROR_MAJOR, "pm_disableHB: invalid path ID %d", pathID);
826         return SCTP_PARAMETER_PROBLEM;
827     }
828 
829     if (pmData->pathData[pathID].heartbeatEnabled) {
830         sctp_stopTimer(pmData->pathData[pathID].hearbeatTimer);
831         pmData->pathData[pathID].hearbeatTimer = 0;
832         pmData->pathData[pathID].heartbeatEnabled = FALSE;
833         event_logi(INTERNAL_EVENT_0, "pm_disableHB: path %d disabled", (unsigned int) pathID);
834     }
835     return SCTP_SUCCESS;
836 }                               /* end: pm_disableHB */
837 
838 
839 
840 /**
841   pm_setPrimaryPath sets the primary path.
842   @param pathID     index of the address that is to become primary path
843   @return 0 if okay, else 1 if there was some error
844 */
pm_setPrimaryPath(short pathID)845 short pm_setPrimaryPath(short pathID)
846 {
847     pmData = (PathmanData *) mdi_readPathMan();
848 
849     if (pmData == NULL) {
850         error_log(ERROR_MAJOR, "pm_setPrimaryPath: mdi_readPathMan failed");
851         return SCTP_MODULE_NOT_FOUND;
852     }
853     if (pmData->pathData == NULL) {
854         error_logi(ERROR_MAJOR, "pm_setPrimaryPath(%d): no paths set", pathID);
855         return SCTP_UNSPECIFIED_ERROR;
856     }
857 
858     if (pathID >= 0 && pathID < pmData->numberOfPaths) {
859         if (pmData->pathData[pathID].state == PM_ACTIVE) {
860             pmData->primaryPath = pathID;
861             pmData->pathData[pathID].chunksSent = FALSE;
862             event_logi(INTERNAL_EVENT_0, "pm_setPrimaryPath: path %d is primary", pathID);
863             return SCTP_SUCCESS;
864         } else {
865             event_logi(INTERNAL_EVENT_0, "pm_setPrimaryPath: path %d not ACTIVE", pathID);
866             return SCTP_SPECIFIC_FUNCTION_ERROR;
867         }
868     } else {
869         error_logi(ERROR_MAJOR, "pm_setPrimaryPath: invalid path ID %d", pathID);
870         return SCTP_PARAMETER_PROBLEM;
871     }
872 }                               /* end: pm_setPrimaryPath */
873 
874 
875 
876 /*------------------- Functions called by ULP to read pathmanagement state info ------------------*/
877 
878 /**
879  * pm_readRTO returns the currently set RTO value in msecs for a certain path.
880  * @param pathID    index of the address/path
881  * @return  path's current RTO
882  */
pm_readRTO(short pathID)883 unsigned int pm_readRTO(short pathID)
884 {
885     pmData = (PathmanData *) mdi_readPathMan();
886 
887     if (pmData == NULL) {
888         error_logi(ERROR_MAJOR, "pm_readRTO(%d): mdi_readPathMan failed", pathID);
889         return 0;
890     }
891 
892     if (pathID >= 0 && pathID < pmData->numberOfPaths) {
893         if (pmData->pathData == NULL)
894             return pmData->rto_initial;
895         else
896             return pmData->pathData[pathID].rto;
897     } else {
898         error_logi(ERROR_MAJOR, "pm_readRTO(%d): invalid path ID", pathID);
899         return 0;
900     }
901 }                               /* end: pm_readRTO */
902 
903 /**
904  pm_readRttVar returns the currently measured value for Round-Trip
905  time variation of a certain path.
906  @param pathID    index of the address/path
907  @return  path's current RTTvar in msecs, 0 if it's not set, 0xffffffff on error
908 */
pm_readRttVar(short pathID)909 unsigned int pm_readRttVar(short pathID)
910 {
911     pmData = (PathmanData *) mdi_readPathMan();
912 
913     if (pmData == NULL) {
914         error_logi(ERROR_MAJOR, "pm_readRttVar(%d): mdi_readPathMan failed", pathID);
915         return 0xffffffff;
916     }
917     if (!pmData->pathData) {
918         error_logi(ERROR_MAJOR, "pm_readRttVAr(%d): Path Data Structures not initialized yet, returning !", pathID);
919         return 0;
920     }
921 
922     if (pathID >= 0 && pathID < pmData->numberOfPaths)
923             return pmData->pathData[pathID].rttvar;
924     else {
925         error_logi(ERROR_MAJOR, "pm_readRttVar(%d): invalid path ID", pathID);
926         return 0xffffffff;
927     }
928 }                               /* end: pm_readRttVar */
929 
930 
931 /**
932   pm_readSRTT returns the currently set SRTT value for a certain path.
933   @param pathID    index of the address/path
934   @return  path's current smoothed round trip time, or 0xffffffff on error
935 */
pm_readSRTT(short pathID)936 unsigned int pm_readSRTT(short pathID)
937 {
938     pmData = (PathmanData *) mdi_readPathMan();
939 
940     if (pmData == NULL) {
941         error_log(ERROR_MAJOR, "pm_readSRTT: mdi_readPathMan failed");
942         return 0xffffffff;
943     }
944 
945     if (!pmData->pathData) {
946         event_logi(VERBOSE, "pm_readSRTT(%d): Path Data Structures not initialized, return RTO_INITIAL !", pathID);
947         return pmData->rto_initial;
948     }
949 
950     if (pathID >= 0 && pathID < pmData->numberOfPaths)
951             return pmData->pathData[pathID].srtt;
952     else {
953         error_logi(ERROR_MAJOR, "pm_readSRTT: invalid path ID %d", pathID);
954         return 0xffffffff;
955     }
956 }                               /* end: pm_readSRTT */
957 
958 
959 /**
960   pm_readState returns the current state of the path.
961   @param pathID  index of the questioned address
962   @return state of path (active/inactive)
963 */
pm_readState(short pathID)964 short pm_readState(short pathID)
965 {
966     pmData = (PathmanData *) mdi_readPathMan();
967 
968     if (pmData == NULL) {
969         error_log(ERROR_MAJOR, "pm_readState: mdi_readPathMan failed");
970         return -1;
971     }
972     if (pmData->pathData == NULL) {
973         error_log(ERROR_MAJOR, "pm_readState: pathData==NULL failed");
974         return -1;
975     }
976 
977     if (pathID >= 0 && pathID < pmData->numberOfPaths) {
978         if (pmData->pathData == NULL)
979             return PM_INACTIVE;
980         else
981             return pmData->pathData[pathID].state;
982     } else {
983         error_logi(ERROR_MAJOR, "pm_readState: invalid path ID %d", pathID);
984         return -1;
985     }
986 }                               /* end: pm_readState */
987 
988 
989 
990 /**
991   pm_readPrimaryPath is used to determine the current primary path
992   @return index to the primary path
993 */
pm_readPrimaryPath(void)994 unsigned short pm_readPrimaryPath(void)
995 {
996     pmData = (PathmanData *) mdi_readPathMan();
997 
998     if (pmData == NULL) {
999         event_log(ERROR_MAJOR, "pm_readPrimaryPath: pathmanagement-instance does not exist");
1000         return 0xFFFF;
1001     } else {
1002         return pmData->primaryPath;
1003     }
1004 }                               /* end: pm_readPrimaryPath */
1005 
1006 /**
1007   pm_getMaxPathRetransmisions is used to get the current  maxPathRetransmissions
1008   parameter value
1009   @return   maxPathRetransmissions of the current instance
1010 */
pm_getMaxPathRetransmisions(void)1011 int  pm_getMaxPathRetransmisions(void)
1012 {
1013     pmData = (PathmanData *) mdi_readPathMan();
1014 
1015     if (pmData == NULL) {
1016         event_log(ERROR_MAJOR, "pm_getMaxPathRetransmisions(): pathmanagement-instance does not exist");
1017         return -1;
1018     } else {
1019         return pmData->maxPathRetransmissions;
1020     }
1021 }                               /* end: pm_getMaxPathRetransmisions(void) */
1022 
1023 /**
1024   pm_setMaxPathRetransmisions is used to get the current  maxPathRetransmissions
1025   parameter value
1026   @param   new_max  new value for  maxPathRetransmissions parameter
1027   @return   0 for success, -1 for error
1028 */
pm_setMaxPathRetransmisions(int new_max)1029 int  pm_setMaxPathRetransmisions(int new_max)
1030 {
1031     pmData = (PathmanData *) mdi_readPathMan();
1032 
1033     if (pmData == NULL) {
1034         event_log(ERROR_MAJOR, "pm_setMaxPathRetransmisions(): pathmanagement-instance does not exist");
1035         return -1;
1036     } else {
1037         pmData->maxPathRetransmissions = new_max;
1038     }
1039     return 0;
1040 }                               /* end: pm_setMaxPathRetransmisions(void) */
1041 
1042 /**
1043   pm_getRtoInitial is used to get the current  rto_initial parameter value
1044   @return   rto_initial on success, -1 for error
1045 */
pm_getRtoInitial(void)1046 int  pm_getRtoInitial(void)
1047 {
1048     pmData = (PathmanData *) mdi_readPathMan();
1049 
1050     if (pmData == NULL) {
1051         event_log(ERROR_MAJOR, "pm_getRtoInitial(): pathmanagement-instance does not exist");
1052         return -1;
1053     }
1054     return pmData->rto_initial;
1055 }                               /* end: pm_getRtoInitial() */
1056 
1057 /**
1058   pm_getRtoInitial is used to get the current  rto_initial parameter value
1059   @return   0 on success, -1 for error
1060 */
pm_setRtoInitial(int new_rto_initial)1061 int  pm_setRtoInitial(int new_rto_initial)
1062 {
1063     pmData = (PathmanData *) mdi_readPathMan();
1064 
1065     if (pmData == NULL) {
1066         event_log(ERROR_MAJOR, "pm_setRtoInitial(): pathmanagement-instance does not exist");
1067         return -1;
1068     }
1069     pmData->rto_initial = new_rto_initial;
1070     return 0;
1071 }                               /* end: pm_setRtoInitial() */
1072 
1073 
pm_setRtoMin(int new_rto_min)1074 int  pm_setRtoMin(int new_rto_min)
1075 {
1076     pmData = (PathmanData *) mdi_readPathMan();
1077 
1078     if (pmData == NULL) {
1079         event_log(ERROR_MAJOR, "pm_setRtoMin(): pathmanagement-instance does not exist");
1080         return -1;
1081     }
1082     pmData->rto_min = new_rto_min;
1083     return 0;
1084 }                               /* end: pm_setRtoMin() */
1085 
pm_getRtoMin(void)1086 int  pm_getRtoMin(void)
1087 {
1088     pmData = (PathmanData *) mdi_readPathMan();
1089 
1090     if (pmData == NULL) {
1091         event_log(ERROR_MAJOR, "pm_getRtoMin(): pathmanagement-instance does not exist");
1092         return -1;
1093     }
1094     return pmData->rto_min;
1095 }                               /* end: pm_getRtoMin() */
1096 
pm_setRtoMax(int new_rto_max)1097 int  pm_setRtoMax(int new_rto_max)
1098 {
1099     pmData = (PathmanData *) mdi_readPathMan();
1100 
1101     if (pmData == NULL) {
1102         event_log(ERROR_MAJOR, "pm_setRtoMax(): pathmanagement-instance does not exist");
1103         return -1;
1104     }
1105     pmData->rto_max = new_rto_max;
1106     return 0;
1107 }                               /* end: pm_setRtoMax() */
1108 
pm_getRtoMax(void)1109 int  pm_getRtoMax(void)
1110 {
1111     pmData = (PathmanData *) mdi_readPathMan();
1112 
1113     if (pmData == NULL) {
1114         event_log(ERROR_MAJOR, "pm_getRtoMax(): pathmanagement-instance does not exist");
1115         return 60000;
1116     }
1117     return pmData->rto_max;
1118 }                               /* end: pm_getRtoMax() */
1119 
1120 
pm_setHBInterval(unsigned int new_interval)1121 int  pm_setHBInterval(unsigned int new_interval)
1122 {
1123     int count;
1124 
1125     pmData = (PathmanData *) mdi_readPathMan();
1126 
1127     if (pmData == NULL) {
1128         event_log(ERROR_MAJOR, "pm_setHBInterval(): pathmanagement-instance does not exist");
1129         return -1;
1130     }
1131     if (pmData->pathData == NULL) {
1132         event_log(ERROR_MAJOR, "pm_setHBInterval(): path structures do not exist");
1133         return -1;
1134     }
1135     for (count = 0; count < pmData->numberOfPaths; count++) {
1136         pmData->pathData[count].heartbeatIntervall = new_interval;
1137     }
1138     return 0;
1139 }
1140 
pm_getHBInterval(short pathID,unsigned int * current_interval)1141 int pm_getHBInterval(short pathID, unsigned int* current_interval)
1142 {
1143     pmData = (PathmanData *) mdi_readPathMan();
1144 
1145     if (pmData == NULL) {
1146         event_log(ERROR_MAJOR, "pm_getHBInterval(): pathmanagement-instance does not exist");
1147         return -1;
1148     }
1149     if (pmData->pathData == NULL) {
1150         event_log(ERROR_MAJOR, "pm_getHBInterval(): path structures do not exist");
1151         return -1;
1152     }
1153     if (pathID >= 0 && pathID < pmData->numberOfPaths) {
1154         *current_interval = pmData->pathData[pathID].heartbeatIntervall;
1155     } else {
1156         error_logi(ERROR_MAJOR, "pm_getHBInterval: invalid path ID %d", pathID);
1157         *current_interval = 0;
1158         return -1;
1159     }
1160     return 0;
1161 }
1162 
1163 /*------------------- Functions called to create, init and delete pathman-instances --------------*/
1164 
1165 /**
1166    pm_setPaths modufies number of paths and sets the primary path.
1167    This is required for association setup, where the local ULP provides
1168    only one path and the peer may provide additional paths.
1169    This function also initializes the path structures and starts the heartbeat timer for each
1170    path. For this reason it is recommended to call this function when communication up is called.
1171    @params noOfPaths  number of paths to the destination endpoint
1172    @param  primaryPathID   index to the address that is to be used as primary address
1173 */
pm_setPaths(short noOfPaths,short primaryPathID)1174 short pm_setPaths(short noOfPaths, short primaryPathID)
1175 {
1176     PathmanData *pmData;
1177     int b,i,j = 0;
1178 
1179     pmData = (PathmanData *) mdi_readPathMan();
1180 
1181     if (pmData == NULL) {
1182         error_log(ERROR_MAJOR, "pm_setPrimaryPath: mdi_readPathMan failed");
1183         return 1;
1184     }
1185 
1186     pmData->pathData = (PathData *) malloc(noOfPaths * sizeof(PathData));
1187 
1188     if (!pmData->pathData)
1189         error_log(ERROR_FATAL, "pm_setPaths: out of memory");
1190 
1191     if (primaryPathID >= 0 && primaryPathID < noOfPaths) {
1192         pmData->primaryPath = primaryPathID;
1193         pmData->numberOfPaths = noOfPaths;
1194         pmData->peerRetranscount = 0;
1195 
1196 
1197         for (i = 0; i < noOfPaths; i++) {
1198             pmData->pathData[i].state = PM_PATH_UNCONFIRMED;
1199             if (i == primaryPathID) {
1200                 pmData->pathData[i].state = PM_ACTIVE;
1201             }
1202             pmData->pathData[i].heartbeatEnabled = TRUE;
1203             pmData->pathData[i].firstRTO = TRUE;
1204             pmData->pathData[i].pathRetranscount = 0;
1205             pmData->pathData[i].rto = pmData->rto_initial;
1206             pmData->pathData[i].srtt = pmData->rto_initial;
1207             pmData->pathData[i].rttvar = 0;
1208 
1209             pmData->pathData[i].heartbeatSent = FALSE;
1210             pmData->pathData[i].heartbeatAcked = FALSE;
1211             pmData->pathData[i].timerBackoff = FALSE;
1212             pmData->pathData[i].chunksAcked = FALSE;
1213             pmData->pathData[i].chunksSent = FALSE;
1214 
1215             pmData->pathData[i].heartbeatIntervall = PM_INITIAL_HB_INTERVAL;
1216             pmData->pathData[i].hearbeatTimer = 0;
1217             pmData->pathData[i].pathID = i;
1218 
1219             b = mdi_getDefaultMaxBurst();
1220 
1221             if (i != primaryPathID) {
1222                 j++;
1223                 if (j < b) {
1224                     pmData->pathData[i].hearbeatTimer =
1225                         adl_startTimer(j,    /* send HB quickly on first usually four unconfirmed paths */
1226                                        &pm_heartbeatTimer,
1227                                        TIMER_TYPE_HEARTBEAT,
1228                                         (void *) &pmData->associationID,
1229                                         (void *) &pmData->pathData[i].pathID);
1230                 } else {
1231                     pmData->pathData[i].hearbeatTimer =
1232                         adl_startTimer(pmData->pathData[i].rto * (j-b),    /* send HB more slowly on other paths */
1233                                        &pm_heartbeatTimer,
1234                                        TIMER_TYPE_HEARTBEAT,
1235                                        (void *) &pmData->associationID,
1236                                        (void *) &pmData->pathData[i].pathID);
1237                 }
1238             } else {
1239                 pmData->pathData[i].hearbeatTimer =
1240                     adl_startTimer(pmData->pathData[i].heartbeatIntervall+pmData->pathData[i].rto,
1241                                     &pm_heartbeatTimer,
1242                                     TIMER_TYPE_HEARTBEAT,
1243                                     (void *) &pmData->associationID,
1244                                     (void *) &pmData->pathData[i].pathID);
1245             }
1246             /* after RTO we can do next RTO update */
1247             adl_gettime(&(pmData->pathData[i].rto_update));
1248 
1249         }
1250 
1251         event_log(INTERNAL_EVENT_0, "pm_setPaths called ");
1252 
1253         return 0;
1254     } else {
1255         error_log(ERROR_MAJOR, "pm_setPaths: invalid path ID");
1256         return 1;
1257     }
1258 }                               /* end: pm_setPaths */
1259 
1260 
1261 
1262 /**
1263  * pm_newPathman creates a new instance of pathmanagement. There is one pathmanagement instance
1264  * per association. WATCH IT : this needs to be fixed ! pathData is NULL, but may accidentally be
1265  * referenced !
1266  * @param numberOfPaths    number of paths of the association
1267  * @param primaryPath      initial primary path
1268  * @param  sctpInstance pointer to the SCTP instance
1269  * @return pointer to the newly created path management instance !
1270  */
pm_newPathman(short numberOfPaths,short primaryPath,void * sctpInstance)1271 void *pm_newPathman(short numberOfPaths, short primaryPath, void* sctpInstance)
1272 {
1273     PathmanData *pmData;
1274 
1275     pmData = (PathmanData *) malloc(sizeof(PathmanData));
1276     if (!pmData)
1277         error_log(ERROR_FATAL, "pm_setPaths: out of memory");
1278     pmData->pathData = NULL;
1279 
1280     pmData->primaryPath = primaryPath;
1281     pmData->numberOfPaths = numberOfPaths;
1282     pmData->associationID = mdi_readAssociationID();
1283     pmData->maxPathRetransmissions = mdi_getDefaultPathMaxRetransmits(sctpInstance);
1284     pmData->rto_initial = mdi_getDefaultRtoInitial(sctpInstance);
1285     pmData->rto_min = mdi_getDefaultRtoMin(sctpInstance);
1286     pmData->rto_max = mdi_getDefaultRtoMax(sctpInstance);
1287     return pmData;
1288 }                               /* end: pm_newPathman */
1289 
1290 
1291 
1292 /**
1293  * Deletes the instance pointed to by pathmanPtr.
1294  * @param   pathmanPtr pointer to the instance that is to be deleted
1295  */
pm_deletePathman(void * pathmanPtr)1296 void pm_deletePathman(void *pathmanPtr)
1297 {
1298     int i;
1299     PathmanData *pmData;
1300 
1301     event_log(INTERNAL_EVENT_0, "deleting pathmanagement");
1302 
1303     pmData = (PathmanData *) pathmanPtr;
1304 
1305     if (pmData != NULL && pmData->pathData != NULL) {
1306         for (i = 0; i < pmData->numberOfPaths; i++) {
1307             if (pmData->pathData[i].hearbeatTimer != 0) {
1308                 adl_stopTimer(pmData->pathData[i].hearbeatTimer);
1309                 pmData->pathData[i].hearbeatTimer = 0;
1310             }
1311         }
1312     }
1313 
1314     event_log(VVERBOSE, "stopped timers");
1315 
1316     free(pmData->pathData);
1317     free(pmData);
1318 }                               /* end: pm_deletePathman */
1319