/* * Copyright (c) 2017-2019 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include struct wldr_buffer { Message *message; uint8_t rtx_counter; }; typedef struct wldr_buffer WldrBuffer; struct wldr_state { uint16_t expected_label; uint16_t next_label; WldrBuffer *buffer[BUFFER_SIZE]; }; Wldr *wldr_Init() { Wldr *wldr = parcMemory_AllocateAndClear(sizeof(Wldr)); parcAssertNotNull(wldr, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(Wldr)); wldr->expected_label = 1; wldr->next_label = 1; for (int i = 0; i < BUFFER_SIZE; i++) { WldrBuffer *entry = parcMemory_AllocateAndClear(sizeof(WldrBuffer)); parcAssertNotNull( entry, "WldrBuffer init: parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(WldrBuffer)); entry->message = NULL; entry->rtx_counter = 0; wldr->buffer[i] = entry; } return wldr; } void wldr_ResetState(Wldr *wldr) { wldr->expected_label = 1; wldr->next_label = 1; for (int i = 0; i < BUFFER_SIZE; i++) { wldr->buffer[i]->message = NULL; wldr->buffer[i]->rtx_counter = 0; } } void wldr_Destroy(Wldr **wldrPtr) { Wldr *wldr = *wldrPtr; for (unsigned i = 0; i < BUFFER_SIZE; i++) { if (wldr->buffer[i]->message != NULL) { message_Release(&(wldr->buffer[i]->message)); parcMemory_Deallocate((void **)&(wldr->buffer[i])); } } parcMemory_Deallocate((void **)&wldr); *wldrPtr = NULL; } static void _wldr_RetransmitPacket(Wldr *wldr, const Connection *conn, uint16_t label) { if (wldr->buffer[label % BUFFER_SIZE]->message == NULL) { // the required message for retransmission is not in the buffer return; } if (wldr->buffer[label % BUFFER_SIZE]->rtx_counter < MAX_RTX) { Message *msg = wldr->buffer[label % BUFFER_SIZE]->message; message_SetWldrLabel(msg, wldr->next_label); if (wldr->buffer[wldr->next_label % BUFFER_SIZE]->message != NULL) { message_Release(&(wldr->buffer[wldr->next_label % BUFFER_SIZE]->message)); } wldr->buffer[wldr->next_label % BUFFER_SIZE]->message = msg; wldr->buffer[wldr->next_label % BUFFER_SIZE]->rtx_counter = wldr->buffer[label % BUFFER_SIZE]->rtx_counter + 1; message_Acquire(wldr->buffer[wldr->next_label % BUFFER_SIZE]->message); wldr->next_label++; connection_ReSend(conn, msg, false); } } static void _wldr_SendWldrNotificaiton(Wldr *wldr, const Connection *conn, Message *message, uint16_t expected_lbl, uint16_t received_lbl) { // here we need to create a new packet that is used to send the wldr // notification to the prevoius hop. the destionation address of the // notification is the source address of the message for which we want to // create a notification. in fact, if message is an interest the prevoius hop // is identified by the src. if message is a data, we need to send the // notification message with the content name has a source address in this way // the message will be trapped by the pounting rules in the next hop We define // the notification as an interest message so that the NAT in the send // function will set the src address of the local connection. Notice that in // this way the notification packet will be dispaced to the right connection // at the next hop. Message *notification = message_CreateWldrNotification(message, expected_lbl, received_lbl); parcAssertNotNull(notification, "Got null from CreateWldrNotification"); connection_ReSend(conn, notification, true); } void wldr_SetLabel(Wldr *wldr, Message *message) { // in this function we send the packet for the first time // 1) we set the wldr label message_SetWldrLabel(message, wldr->next_label); // 2) we store the pointer to packet in the buffer if (wldr->buffer[wldr->next_label % BUFFER_SIZE]->message != NULL) { // release an old message if necessary message_Release(&(wldr->buffer[wldr->next_label % BUFFER_SIZE]->message)); } // we need to acquire the message to avoid that it gets destroyed message_Acquire(message); wldr->buffer[wldr->next_label % BUFFER_SIZE]->message = message; wldr->buffer[wldr->next_label % BUFFER_SIZE]->rtx_counter = 0; wldr->next_label++; if (wldr->next_label == 0) // we alwasy skip label 0 beacause it means that wldr is not active wldr->next_label++; } void wldr_DetectLosses(Wldr *wldr, const Connection *conn, Message *message) { if (message_HasWldr(message)) { // this is a normal wldr packet uint16_t pkt_lbl = (uint16_t)message_GetWldrLabel(message); if (pkt_lbl != wldr->expected_label) { // if the received packet label is 1 and the expected packet label > // pkt_lbl usually we are in the case where a remote note disconnected for // a while and reconnected on this same connection, so the two nodes are // out of synch for this reason we do not send any notification, we just // synch the labels if ((pkt_lbl != 1) || (wldr->expected_label < pkt_lbl)) { _wldr_SendWldrNotificaiton(wldr, conn, message, wldr->expected_label, pkt_lbl); } // here we always synch wldr->expected_label = (uint16_t)(pkt_lbl + 1); } else { wldr->expected_label++; if (wldr->expected_label == 0) wldr->expected_label++; // for the next_label we want to skip 0 } } } void wldr_HandleWldrNotification(Wldr *wldr, const Connection *conn, Message *message) { uint16_t expected_lbl = (uint16_t)message_GetWldrExpectedLabel(message); uint16_t received_lbl = (uint16_t)message_GetWldrLastReceived(message); if ((wldr->next_label - expected_lbl) > BUFFER_SIZE) { // the packets are not in the buffer anymore return; } while (expected_lbl < received_lbl) { _wldr_RetransmitPacket(wldr, conn, expected_lbl); expected_lbl++; } }