int dtn2_removeRule(char *nodeNm, char *demux) { Sdr sdr = getIonsdr(); char nodeName[SDRSTRING_BUFSZ]; Object elt; OBJ_POINTER(Dtn2Plan, plan); Object ruleAddr; OBJ_POINTER(Dtn2Rule, rule); CHKERR(nodeNm && demux); if (*demux == '\0') { writeMemo("[?] Zero-length DTN2 rule demux."); return 0; } if (filterNodeName(nodeName, nodeNm) < 0) { return 0; } CHKERR(sdr_begin_xn(sdr)); elt = locatePlan(nodeName, NULL); if (elt == 0) { sdr_exit_xn(sdr); writeMemoNote("[?] No plan defined for this node", nodeNm); return 0; } GET_OBJ_POINTER(sdr, Dtn2Plan, plan, sdr_list_data(sdr, elt)); dtn2_findRule(nodeName, demux, plan, &ruleAddr, &elt); if (elt == 0) { sdr_exit_xn(sdr); writeMemoNote("[?] Unknown rule", demux); return 0; } /* All parameters validated, okay to remove the rule. */ GET_OBJ_POINTER(sdr, Dtn2Rule, rule, ruleAddr); dtn2_destroyDirective(&(rule->directive)); sdr_free(sdr, ruleAddr); sdr_list_delete(sdr, elt, NULL, NULL); if (sdr_end_xn(sdr) < 0) { putErrmsg("Can't remove rule.", NULL); return -1; } return 1; }
static int scanInFdus(Sdr sdr, time_t currentTime) { CfdpDB *cfdpConstants; Object entityElt; OBJ_POINTER(Entity, entity); Object elt; Object nextElt; Object fduObj; OBJ_POINTER(InFdu, fdu); CfdpHandler handler; cfdpConstants = getCfdpConstants(); sdr_begin_xn(sdr); for (entityElt = sdr_list_first(sdr, cfdpConstants->entities); entityElt; entityElt = sdr_list_next(sdr, entityElt)) { GET_OBJ_POINTER(sdr, Entity, entity, sdr_list_data(sdr, entityElt)); for (elt = sdr_list_first(sdr, entity->inboundFdus); elt; elt = nextElt) { nextElt = sdr_list_next(sdr, elt); fduObj = sdr_list_data(sdr, elt); GET_OBJ_POINTER(sdr, InFdu, fdu, fduObj); if (fdu->eofReceived && fdu->checkTime < currentTime) { sdr_stage(sdr, NULL, fduObj, 0); fdu->checkTimeouts++; fdu->checkTime += cfdpConstants->checkTimerPeriod; sdr_write(sdr, fduObj, (char *) fdu, sizeof(InFdu)); } if (fdu->checkTimeouts > cfdpConstants->checkTimeoutLimit) { if (handleFault(&(fdu->transactionId), CfdpCheckLimitReached, &handler) < 0) { sdr_cancel_xn(sdr); putErrmsg("Can't handle check limit \ reached.", NULL); return -1; } } } }
int dtn2_updateRule(char *nodeNm, char *demux, FwdDirective *directive) { Sdr sdr = getIonsdr(); char nodeName[SDRSTRING_BUFSZ]; Object elt; OBJ_POINTER(Dtn2Plan, plan); Object ruleAddr; Dtn2Rule ruleBuf; CHKERR(nodeNm && demux && directive); if (*demux == '\0') { writeMemo("[?] Zero-length DTN2 rule demux."); return 0; } if (filterNodeName(nodeName, nodeNm) < 0) { return 0; } CHKERR(sdr_begin_xn(sdr)); elt = locatePlan(nodeName, NULL); if (elt == 0) { sdr_exit_xn(sdr); writeMemoNote("[?] No plan defined for this node", nodeNm); return 0; } GET_OBJ_POINTER(sdr, Dtn2Plan, plan, sdr_list_data(sdr, elt)); dtn2_findRule(nodeName, demux, plan, &ruleAddr, &elt); if (elt == 0) { sdr_exit_xn(sdr); writeMemoNote("[?] Unknown rule", demux); return 0; } /* All parameters validated, okay to update the rule. */ sdr_stage(sdr, (char *) &ruleBuf, ruleAddr, sizeof(Dtn2Rule)); dtn2_destroyDirective(&ruleBuf.directive); memcpy((char *) &ruleBuf.directive, (char *) directive, sizeof(FwdDirective)); sdr_write(sdr, ruleAddr, (char *) &ruleBuf, sizeof(Dtn2Rule)); if (sdr_end_xn(sdr) < 0) { putErrmsg("Can't update rule.", NULL); return -1; } return 1; }
void dtn2_findRule(char *nodeNm, char *demux, Dtn2Plan *plan, Object *ruleAddr, Object *eltp) { Sdr sdr = getIonsdr(); char nodeName[SDRSTRING_BUFSZ]; Object elt; OBJ_POINTER(Dtn2Plan, planPtr); /* This function finds the Dtn2Rule for the specified * demux token, for the specified destination node, if * any. */ CHKVOID(ionLocked()); CHKVOID(ruleAddr); CHKVOID(eltp); *eltp = 0; if (plan == NULL) { if (nodeNm == NULL) { return; } if (filterNodeName(nodeName, nodeNm) < 0) { return; } elt = locatePlan(nodeName, NULL); if (elt == 0) { return; } GET_OBJ_POINTER(sdr, Dtn2Plan, planPtr, sdr_list_data(sdr, elt)); plan = planPtr; } elt = locateRule(plan, demux, NULL); if (elt == 0) { return; } *ruleAddr = sdr_list_data(sdr, elt); *eltp = elt; }
int dtn2_removePlan(char *nodeNm) { Sdr sdr = getIonsdr(); char nodeName[SDRSTRING_BUFSZ]; Object elt; Object planObj; OBJ_POINTER(Dtn2Plan, plan); CHKERR(nodeNm); if (filterNodeName(nodeName, nodeNm) < 0) { return 0; } CHKERR(sdr_begin_xn(sdr)); elt = locatePlan(nodeName, NULL); if (elt == 0) { sdr_exit_xn(sdr); writeMemoNote("[?] Unknown plan", nodeNm); return 0; } planObj = sdr_list_data(sdr, elt); GET_OBJ_POINTER(sdr, Dtn2Plan, plan, planObj); if (sdr_list_length(sdr, plan->rules) > 0) { sdr_exit_xn(sdr); writeMemoNote("[?] Can't remove plan; still has rules", nodeNm); return 0; } /* Okay to remove this plan from the database. */ sdr_list_delete(sdr, elt, NULL, NULL); dtn2_destroyDirective(&(plan->defaultDirective)); sdr_list_destroy(sdr, plan->rules, NULL, NULL); sdr_free(sdr, plan->nodeName); sdr_free(sdr, planObj); if (sdr_end_xn(sdr) < 0) { putErrmsg("Can't remove plan.", nodeNm); return -1; } return 1; }
static void executeInfo() { Sdr sdr = getIonsdr(); OBJ_POINTER(CfdpDB, db); char buffer[256]; sdr_begin_xn(sdr); /* Just to lock memory. */ GET_OBJ_POINTER(sdr, CfdpDB, db, getCfdpDbObject()); isprintf(buffer, sizeof buffer, "xncount=%lu, maxtrnbr=%lu, \ fillchar=0x%x, discard=%hu, requirecrc=%hu, segsize=%hu, mtusize = %hu, \ inactivity=%u, ckperiod=%u, maxtimeouts=%u", db->transactionCounter, db->maxTransactionNbr, db->fillCharacter, db->discardIncompleteFile, db->crcRequired, db->maxFileDataLength, db->mtuSize, db->transactionInactivityLimit, db->checkTimerPeriod, db->checkTimeoutLimit); sdr_exit_xn(sdr); printText(buffer); }
static Object locateRule(Dtn2Plan *plan, char *demux, Object *nextRule) { Sdr sdr = getIonsdr(); Object elt; OBJ_POINTER(Dtn2Rule, rule); char nameBuffer[SDRSTRING_BUFSZ]; int result; /* This function locates the Dtn2Rule identified by the * specified demux token, for the specified destination * endpoint, if any; must be an exact match. If * none, notes the location within the rules list at * which such a rule should be inserted. */ if (nextRule) *nextRule = 0; /* Default. */ for (elt = sdr_list_first(sdr, plan->rules); elt; elt = sdr_list_next(sdr, elt)) { GET_OBJ_POINTER(sdr, Dtn2Rule, rule, sdr_list_data(sdr, elt)); sdr_string_read(sdr, nameBuffer, rule->demux); result = strcmp(nameBuffer, demux); if (result < 0) { continue; } if (result > 0) { if (nextRule) *nextRule = elt; break; /* Same as end of list. */ } return elt; } return 0; }
static Object locatePlan(char *nodeName, Object *nextPlan) { Sdr sdr = getIonsdr(); Object elt; OBJ_POINTER(Dtn2Plan, plan); char nameBuffer[SDRSTRING_BUFSZ]; int result; /* This function locates the Dtn2Plan identified by the * specified node name, if any; must be an exact match. * If none, notes the location within the plans list at * which such a plan should be inserted. */ if (nextPlan) *nextPlan = 0; /* Default. */ for (elt = sdr_list_first(sdr, (_dtn2Constants())->plans); elt; elt = sdr_list_next(sdr, elt)) { GET_OBJ_POINTER(sdr, Dtn2Plan, plan, sdr_list_data(sdr, elt)); sdr_string_read(sdr, nameBuffer, plan->nodeName); result = strcmp(nameBuffer, nodeName); if (result < 0) { continue; } if (result > 0) { if (nextPlan) *nextPlan = elt; break; /* Same as end of list. */ } return elt; } return 0; }
void bp_untrack(Object bundleObj, Object trackingElt) { Sdr sdr = getIonsdr(); OBJ_POINTER(Bundle, bundle); Object elt; CHKVOID(bundleObj && trackingElt); sdr_begin_xn(sdr); GET_OBJ_POINTER(sdr, Bundle, bundle, bundleObj); if (bundle->trackingElts == 0) { sdr_exit_xn(sdr); return; } for (elt = sdr_list_first(sdr, bundle->trackingElts); elt; elt = sdr_list_next(sdr, elt)) { if (sdr_list_data(sdr, elt) == trackingElt) { break; } } if (elt == 0) /* Not found. */ { sdr_exit_xn(sdr); return; } sdr_list_delete(sdr, elt, NULL, NULL); if (sdr_end_xn(sdr) < 0) { putErrmsg("Failed removing bundle tracking elt.", NULL); } }
int bp_track(Object bundleObj, Object trackingElt) { Sdr sdr = getIonsdr(); OBJ_POINTER(Bundle, bundle); CHKERR(bundleObj && trackingElt); sdr_begin_xn(sdr); GET_OBJ_POINTER(sdr, Bundle, bundle, bundleObj); if (bundle->trackingElts == 0) { sdr_exit_xn(sdr); putErrmsg("Corrupt bundle? Has no trackingElts list.", NULL); return -1; } sdr_list_insert_last(sdr, bundle->trackingElts, trackingElt); if (sdr_end_xn(sdr) < 0) { putErrmsg("Failed adding bundle tracking elt.", NULL); return -1; } return 0; }
static void executeList(int tokenCount, char **tokens) { Sdr sdr = getIonsdr(); ImcDB imcdb; Object elt; OBJ_POINTER(NodeId, node); if (tokenCount != 1) { SYNTAX_ERROR; return; } CHKVOID(sdr_begin_xn(sdr)); sdr_read(getIonsdr(), (char *) &imcdb, getImcDbObject(), sizeof(ImcDB)); for (elt = sdr_list_first(sdr, imcdb.kin); elt; elt = sdr_list_next(sdr, elt)) { GET_OBJ_POINTER(sdr, NodeId, node, sdr_list_data(sdr, elt)); printKin(node->nbr, imcdb.parent); } sdr_exit_xn(sdr); }
int dtn2_addRule(char *nodeNm, char *demux, FwdDirective *directive) { Sdr sdr = getIonsdr(); char nodeName[SDRSTRING_BUFSZ]; Object elt; OBJ_POINTER(Dtn2Plan, plan); Object nextRule; Dtn2Rule ruleBuf; Object addr; CHKERR(nodeNm && demux && directive); if (*demux == '\0') { writeMemo("[?] Zero-length DTN2 rule demux."); return 0; } if (filterNodeName(nodeName, nodeNm) < 0) { return 0; } CHKERR(sdr_begin_xn(sdr)); elt = locatePlan(nodeName, NULL); if (elt == 0) { sdr_exit_xn(sdr); writeMemoNote("[?] No plan defined for this node", nodeNm); return 0; } GET_OBJ_POINTER(sdr, Dtn2Plan, plan, sdr_list_data(sdr, elt)); if (locateRule(plan, demux, &nextRule) != 0) { sdr_exit_xn(sdr); writeMemoNote("[?] Duplicate rule", demux); return 0; } /* All parameters validated, okay to add the rule. */ memset((char *) &ruleBuf, 0, sizeof(Dtn2Rule)); ruleBuf.demux = sdr_string_create(sdr, demux); memcpy((char *) &ruleBuf.directive, (char *) directive, sizeof(FwdDirective)); addr = sdr_malloc(sdr, sizeof(Dtn2Rule)); if (addr) { if (nextRule) { elt = sdr_list_insert_before(sdr, nextRule, addr); } else { elt = sdr_list_insert_last(sdr, plan->rules, addr); } sdr_write(sdr, addr, (char *) &ruleBuf, sizeof(Dtn2Rule)); } if (sdr_end_xn(sdr) < 0) { putErrmsg("Can't add rule.", NULL); return -1; } return 1; }
int dtn2_lookupDirective(char *nodeName, char *demux, Bundle *bundle, FwdDirective *dirbuf) { Sdr sdr = getIonsdr(); int protClassReqd; Object elt; Object addr; OBJ_POINTER(Dtn2Plan, plan); int stringLen; char stringBuffer[SDRSTRING_BUFSZ]; int result; int last; OBJ_POINTER(Dtn2Rule, rule); /* This function determines the relevant FwdDirective for * the specified eid, if any. Wild card match is okay. */ CHKERR(ionLocked()); CHKERR(nodeName && demux && dirbuf); /* Determine constraints on directive usability. */ protClassReqd = bundle->extendedCOS.flags & BP_PROTOCOL_BOTH; if (protClassReqd == 0) /* Don't care. */ { protClassReqd = -1; /* Matches any. */ } else if (protClassReqd == 10) /* Need BSS. */ { protClassReqd = BP_PROTOCOL_STREAMING; } /* Find best matching plan. Universal wild-card match, * if any, is at the end of the list, so there's no way * to terminate the search early. */ for (elt = sdr_list_first(sdr, (_dtn2Constants())->plans); elt; elt = sdr_list_next(sdr, elt)) { addr = sdr_list_data(sdr, elt); GET_OBJ_POINTER(sdr, Dtn2Plan, plan, addr); stringLen = sdr_string_read(sdr, stringBuffer, plan->nodeName); result = strcmp(stringBuffer, nodeName); if (result < 0) { continue; } if (result == 0) /* Exact match. */ { break; /* Stop searching. */ } /* Node name in plan is greater than node name, * but it might still be a wild-card match. */ last = stringLen - 1; if (stringBuffer[last] == '~' /* "all nodes" */ && strncmp(stringBuffer, nodeName, stringLen - 1) == 0) { break; /* Stop searching. */ } } if (elt == 0) { return 0; /* No plan found. */ } /* Find best matching rule. */ for (elt = sdr_list_first(sdr, plan->rules); elt; elt = sdr_list_next(sdr, elt)) { addr = sdr_list_data(sdr, elt); GET_OBJ_POINTER(sdr, Dtn2Rule, rule, addr); if ((rule->directive.protocolClass & protClassReqd) == 0) { continue; /* Can't use this rule. */ } stringLen = sdr_string_read(sdr, stringBuffer, rule->demux); result = strcmp(stringBuffer, demux); if (result < 0) { continue; } if (result == 0) /* Exact match. */ { break; /* Stop searching. */ } /* Demux in rule is greater than demux, but it * might still be a wild-card match. */ last = stringLen - 1; if (stringBuffer[last] == '~' /* "all demuxes" */ && strncmp(stringBuffer, demux, stringLen - 1) == 0) { break; /* Stop searching. */ } } if (elt == 0) /* End of list. */ { if ((plan->defaultDirective.protocolClass & protClassReqd) == 0) { return 0; /* Matching plan unusable. */ } memcpy((char *) dirbuf, (char *) &plan->defaultDirective, sizeof(FwdDirective)); return 1; } /* Found a matching rule. */ memcpy((char *) dirbuf, (char *) &rule->directive, sizeof(FwdDirective)); return 1; }
int bp_receive(BpSAP sap, BpDelivery *dlvBuffer, int timeoutSeconds) { Sdr sdr = getIonsdr(); VEndpoint *vpoint; OBJ_POINTER(Endpoint, endpoint); Object dlvElt; Object bundleAddr; Bundle bundle; TimerParms timerParms; pthread_t timerThread; int result; char *dictionary; CHKERR(sap && dlvBuffer); if (timeoutSeconds < BP_BLOCKING) { putErrmsg("Illegal timeout interval.", itoa(timeoutSeconds)); return -1; } vpoint = sap->vpoint; sdr_begin_xn(sdr); if (vpoint->appPid != sm_TaskIdSelf()) { sdr_exit_xn(sdr); putErrmsg("Can't receive: not owner of endpoint.", itoa(vpoint->appPid)); return -1; } if (sm_SemEnded(vpoint->semaphore)) { sdr_exit_xn(sdr); writeMemo("[?] Endpoint has been stopped."); /* End task, but without error. */ return -1; } /* Get oldest bundle in delivery queue, if any; wait * for one if necessary. */ GET_OBJ_POINTER(sdr, Endpoint, endpoint, sdr_list_data(sdr, vpoint->endpointElt)); dlvElt = sdr_list_first(sdr, endpoint->deliveryQueue); if (dlvElt == 0) { sdr_exit_xn(sdr); if (timeoutSeconds == BP_POLL) { dlvBuffer->result = BpReceptionTimedOut; return 0; } /* Wait for semaphore to be given, either by the * deliverBundle() function or by timer thread. */ if (timeoutSeconds == BP_BLOCKING) { timerParms.interval = -1; } else /* This is a receive() with a deadline. */ { timerParms.interval = timeoutSeconds; timerParms.semaphore = vpoint->semaphore; if (pthread_create(&timerThread, NULL, timerMain, &timerParms) < 0) { putSysErrmsg("Can't enable interval timer", NULL); return -1; } } /* Take endpoint semaphore. */ if (sm_SemTake(vpoint->semaphore) < 0) { putErrmsg("Can't take endpoint semaphore.", NULL); return -1; } if (sm_SemEnded(vpoint->semaphore)) { writeMemo("[i] Endpoint has been stopped."); /* End task, but without error. */ return -1; } /* Have taken the semaphore, one way or another. */ sdr_begin_xn(sdr); dlvElt = sdr_list_first(sdr, endpoint->deliveryQueue); if (dlvElt == 0) /* Still nothing. */ { /* Either sm_SemTake() was interrupted * or else timer thread gave semaphore. */ sdr_exit_xn(sdr); if (timerParms.interval == 0) { /* Timer expired. */ dlvBuffer->result = BpReceptionTimedOut; pthread_join(timerThread, NULL); } else /* Interrupted. */ { dlvBuffer->result = BpReceptionInterrupted; if (timerParms.interval != -1) { pthread_cancel(timerThread); pthread_join(timerThread, NULL); } } return 0; } else /* Bundle was delivered. */ { if (timerParms.interval != -1) { pthread_cancel(timerThread); pthread_join(timerThread, NULL); } } } /* At this point, we have got a dlvElt and are in an SDR * transaction. */ bundleAddr = sdr_list_data(sdr, dlvElt); sdr_stage(sdr, (char *) &bundle, bundleAddr, sizeof(Bundle)); dictionary = retrieveDictionary(&bundle); if (dictionary == (char *) &bundle) { sdr_cancel_xn(sdr); putErrmsg("Can't retrieve dictionary.", NULL); return -1; } /* Now fill in the data indication structure. */ dlvBuffer->result = BpPayloadPresent; if (printEid(&bundle.id.source, dictionary, &dlvBuffer->bundleSourceEid) < 0) { sdr_cancel_xn(sdr); putErrmsg("Can't print source EID.", NULL); return -1; } dlvBuffer->bundleCreationTime.seconds = bundle.id.creationTime.seconds; dlvBuffer->bundleCreationTime.count = bundle.id.creationTime.count; dlvBuffer->adminRecord = bundle.bundleProcFlags & BDL_IS_ADMIN; dlvBuffer->adu = zco_add_reference(sdr, bundle.payload.content); dlvBuffer->ackRequested = bundle.bundleProcFlags & BDL_APP_ACK_REQUEST; /* Now before returning we send delivery status report * if it is requested. */ if (SRR_FLAGS(bundle.bundleProcFlags) & BP_DELIVERED_RPT) { bundle.statusRpt.flags |= BP_DELIVERED_RPT; getCurrentDtnTime(&bundle.statusRpt.deliveryTime); } if (bundle.statusRpt.flags) { result = sendStatusRpt(&bundle, dictionary); if (result < 0) { sdr_cancel_xn(sdr); putErrmsg("Can't send status report.", NULL); return -1; } } /* Finally delete the delivery list element and, if * possible, destroy the bundle itself. */ if (dictionary) { MRELEASE(dictionary); } sdr_list_delete(sdr, dlvElt, (SdrListDeleteFn) NULL, NULL); bundle.dlvQueueElt = 0; sdr_write(sdr, bundleAddr, (char *) &bundle, sizeof(Bundle)); if (bpDestroyBundle(bundleAddr, 0) < 0) { sdr_cancel_xn(sdr); putErrmsg("Can't destroy bundle.", NULL); return -1; } if (sdr_end_xn(sdr) < 0) { putErrmsg("Failure in bundle reception.", NULL); return -1; } return 0; }
static int dispatchEvents(Sdr sdr, Object events, time_t currentTime) { Object elt; Object eventObj; OBJ_POINTER(BpEvent, event); int result; while (1) { sdr_begin_xn(sdr); CHKERR(ionLocked()); /* In case of killm. */ elt = sdr_list_first(sdr, events); if (elt == 0) /* No more events to dispatch. */ { sdr_exit_xn(sdr); return 0; } eventObj = sdr_list_data(sdr, elt); GET_OBJ_POINTER(sdr, BpEvent, event, eventObj); if (event->time > currentTime) { /* This is the first future event. */ sdr_exit_xn(sdr); return 0; } switch (event->type) { case expiredTTL: result = bpDestroyBundle(event->ref, 1); /* Note that bpDestroyBundle() always * erases the bundle's timeline event, * so we must NOT do so here. */ break; /* Out of switch. */ case xmitOverdue: result = bpReforwardBundle(event->ref); /* Note that bpReforwardBundle() always * erases the bundle's xmitOverdue event, * so we must NOT do so here. */ break; /* Out of switch. */ case ctDue: result = bpReforwardBundle(event->ref); /* Note that bpReforwardBundle() always * erases the bundle's ctDue event, so * we must NOT do so here. */ break; /* Out of switch. */ default: /* Spurious event; erase. */ sdr_free(sdr, eventObj); sdr_list_delete(sdr, elt, NULL, NULL); result = 0; /* Event is ignored. */ } if (result != 0) /* Dispatching failed. */ { sdr_cancel_xn(sdr); putErrmsg("Failed handing BP event.", NULL); return result; } if (sdr_end_xn(sdr) < 0) { putErrmsg("Failed dispatching BP event.", NULL); return -1; } } }
static int reforwardStrandedBundles(int nodeNbr) { Sdr sdr = getIonsdr(); BpDB *bpConstants = getBpConstants(); Object elt; Object eventObj; OBJ_POINTER(BpEvent, event); OBJ_POINTER(Bundle, bundle); sdr_begin_xn(sdr); for (elt = sdr_list_first(sdr, bpConstants->timeline); elt; elt = sdr_list_next(sdr, elt)) { eventObj = sdr_list_data(sdr, elt); GET_OBJ_POINTER(sdr, BpEvent, event, eventObj); if (event->type != expiredTTL) { continue; } /* Have got a bundle that still exists. */ GET_OBJ_POINTER(sdr, Bundle, bundle, event->ref); if (bundle->dlvQueueElt || bundle->fragmentElt || bundle->fwdQueueElt || bundle->overdueElt || bundle->ctDueElt || bundle->xmitsNeeded > 0) { /* No need to reforward. */ continue; } /* A stranded bundle, awaiting custody acceptance. * Might be for a neighbor other than the one * that just reconnected, but in that case the * bundle will be reforwarded and requeued and * go into the bit bucket again; no harm. Note, * though, that this means that a BRS server * would be a bad candidate for gateway into a * space subnet: due to long OWLTs, there might * be a lot of bundles sent via LTP that are * awaiting custody acceptance, so reconnection * of a BRS client might trigger retransmission * of a lot of bundles on the space link in * addition to the ones to be issued via brss. */ if (bpReforwardBundle(event->ref) < 0) { sdr_cancel_xn(sdr); putErrmsg("brss reforward failed.", NULL); return -1; } } if (sdr_end_xn(sdr) < 0) { putErrmsg("brss reforwarding failed.", NULL); return -1; } return 0; }