接上文:https://blog.csdn.net/zhang1806618/article/details/107268598
1.3 网络层(续)
1.3.4 底板(队列)处理
在RoutePacketAndSendToMac函数对单播、广播、组播进行了分别处理。上文分析以单播为例,通过获取单播路由,然后调用NetworkIpSendPacketToMacLayer函数,再调用NetworkIpSendPacketOnInterface函数发送数据包。而广播、组播是直接调用NetworkIpSendPacketOnInterface函数发送数据包,跳过了路由阶段。上文已介绍了NetworkIpSendPacketOnInterface,主要调用NetworkIpSendOnBackplane函数发送数据。
NetworkIpSendOnBackplane函数主要代码如下,作用:一、判断队列是否无限制;二、队列有限时判断队列是否已满。已本地发送为例,无限制则直接调用QueueUpIpFragmentForMacLayer函数发送数据,有限制且队列未满时调用NetworkIpUseBackplaneIfPossible函数尝试发送数据。
void //inline//
NetworkIpSendOnBackplane(
Node *node,
Message *msg,
int incomingInterface,
int outgoingInterface,
NodeAddress hopAddr)
{
NetworkDataIp *ip = (NetworkDataIp *) node->networkData.networkVar;
NetworkDataIcmp *icmp = (NetworkDataIcmp*) ip->icmpStruct;
IpHeaderType *ipHeader = (IpHeaderType *) MESSAGE_ReturnPacket(msg);
//判断吞吐量是否无限制
//1.无限制情况
if (ip->backplaneThroughputCapacity ==
NETWORK_IP_UNLIMITED_BACKPLANE_THROUGHPUT)
{
//本地接收,将MAC层数据包上传至网络层、传输层
if (outgoingInterface == CPU_INTERFACE)
{
DeliverPacket(node, msg, incomingInterface, hopAddr);
}
//为MAC层处理数据包
else
{
QueueUpIpFragmentForMacLayer(node,
msg,
outgoingInterface,
hopAddr,
incomingInterface);
}
}
//2.有限制情况
else
{
BOOL queueIsFull = FALSE;
//本地发送情况
if (incomingInterface == CPU_INTERFACE ||
ip->backplaneType == BACKPLANE_TYPE_CENTRAL)
{
//插入队列
NetworkIpCpuQueueInsert(node,
msg,
hopAddr,
ipHeader->ip_dst,
outgoingInterface,
NETWORK_PROTOCOL_IP,
&queueIsFull,
incomingInterface);
}
//本地接收或转发情况
else
{
//插入队列
NetworkIpInputQueueInsert(node,
incomingInterface,
msg,
hopAddr,
ipHeader->ip_dst,
outgoingInterface,
NETWORK_PROTOCOL_IP,
&queueIsFull);
}
//队列已满,丢弃并发送ICMP消息
if (queueIsFull)
{
if (ip->isIcmpEnable && icmp->sourceQuenchEnable)
{
BOOL ICMPErrorMsgCreated = NetworkIcmpCreateErrorMessage(node,
msg,
ipHeader->ip_src,
incomingInterface,
ICMP_SOURCE_QUENCH,
ICMP_SOURCE_QUENCH_CODE,
0,
0);
}
MESSAGE_Free(node, msg);
}
//队列未满,发送消息
else
{
NetworkIpUseBackplaneIfPossible(node,
incomingInterface);
}
}
}
NetworkIpUseBackplaneIfPossible主要功能是判断接口是否繁忙,接口队列是否为空。只有在接口空闲,队列不为空时,才执行发送操作。通过调度网络层事件MSG_NETWORK_Backplane发送消息。
void NetworkIpUseBackplaneIfPossible(Node *node,
int incomingInterface)
{
NetworkDataIp *ip = (NetworkDataIp *) node->networkData.networkVar;
int outgoingInterface;
int networkType = 0;
NodeAddress hopAddr = 0;
MacHWAddress hopMacAddr;
TosType priority = 0;
Message *newMsg = NULL;
int packetSize;
BOOL isEmpty = TRUE;
NetworkIpBackplaneStatusType *backplaneStatus;
//背板统计状态,根据本地发送还是转发确定
if (incomingInterface == CPU_INTERFACE ||
ip->backplaneType == BACKPLANE_TYPE_CENTRAL)
{
//本地发送
backplaneStatus = &ip->backplaneStatus;
}
else
{
//转发或接收
backplaneStatus = &ip->interfaceInfo[incomingInterface]->backplaneStatus;
}
//判断接口是否繁忙
if (*backplaneStatus != NETWORK_IP_BACKPLANE_IDLE)
{
return;
}
//接口空闲,获取接口队列消息
//本地发送时,获取本地发送队列顶端消息
if (incomingInterface == CPU_INTERFACE ||
ip->backplaneType == BACKPLANE_TYPE_CENTRAL)
{
if (!NetworkIpCpuQueueIsEmpty(node))
{
NetworkIpCpuQueueTopPacket(node,
&newMsg,
&hopAddr,
&hopMacAddr,
&outgoingInterface,
&networkType,
&priority);
isEmpty = FALSE;
}
}
//转发或接收时,获取本地接收队列顶端消息
else
{
if (!NetworkIpInputQueueIsEmpty(node, incomingInterface))
{
NetworkIpInputQueueTopPacket(node,
incomingInterface,
&newMsg,
&hopAddr,
&hopMacAddr,
&outgoingInterface,
&networkType,
&priority);
isEmpty = FALSE;
}
}
//只有在队列非空时才执行发送.
if (!isEmpty)
{
Message *backplaneMsg;
clocktype backplaneDelay;
NetworkIpBackplaneInfo *backplaneInfo;
packetSize = MESSAGE_ReturnPacketSize(newMsg);
backplaneMsg = MESSAGE_Alloc(node,
NETWORK_LAYER,
NETWORK_PROTOCOL_IP,
MSG_NETWORK_Backplane);
MESSAGE_InfoAlloc(node,
backplaneMsg,
sizeof(NetworkIpBackplaneInfo));
backplaneInfo = (NetworkIpBackplaneInfo *)
MESSAGE_ReturnInfo(backplaneMsg);
backplaneInfo->incomingInterface = incomingInterface;
backplaneInfo->hopAddr = hopAddr;
MAC_CopyMacHWAddress(&backplaneInfo->hopMacAddr,&hopMacAddr);
*backplaneStatus = NETWORK_IP_BACKPLANE_BUSY;
MESSAGE_Send(node, backplaneMsg, backplaneDelay);
}
}
对网络层事件处理,第一步:network.cpp中函数NETWORK_ProcessEvent,根据不同网络层协议调用不同协议处理;第二步:由IP协议的事件处理函数,network_ip.cpp中的NetworkIpLayer,根据不同事件进行处理;第三步:根据事件类型MSG_NETWORK_Backplane,调用函数NetworkIpReceiveFromBackplane处理。
void NetworkIpReceiveFromBackplane(Node *node, Message *msg)
{
......
//本地发送,输出队列弹出最前数据包
if (info->incomingInterface == CPU_INTERFACE
|| ip->backplaneType == BACKPLANE_TYPE_CENTRAL)
{
NetworkIpCpuQueueDequeuePacket(node,
&queueMsg,
&hopAddr,
&nextHopMacAddr,
&outgoingInterface,
&networkType,
&priority);
}
//转发或接收,输入队列弹出最前数据包
else
{
NetworkIpInputQueueDequeuePacket(node,
info->incomingInterface,
&queueMsg,
&hopAddr,
&nextHopMacAddr,
&outgoingInterface,
&networkType,
&priority);
}
//本地转发或接收
if (outgoingInterface == CPU_INTERFACE)
{...
}
else
{
QueueUpIpFragmentForMacLayer(node,
queueMsg,
outgoingInterface,
info->hopAddr,
info->incomingInterface);
}
}
至此,与队列无限制时一样,调用QueueUpIpFragmentForMacLayer发送数据。
static void //inline//
QueueUpIpFragmentForMacLayer(
Node *node,
Message *msg,
int interfaceIndex,
NodeAddress nextHop,
int incomingInterface)
{
NetworkDataIp *ip = (NetworkDataIp *) node->networkData.networkVar;
Scheduler *scheduler = ip->interfaceInfo[interfaceIndex]->scheduler;
IpHeaderType* ipHeader = (IpHeaderType *) MESSAGE_ReturnPacket(msg);
NetworkDataIcmp *icmp = (NetworkDataIcmp*) ip->icmpStruct;
BOOL queueIsFull;
BOOL queueWasEmpty;
ActionData acnData;
NetworkType netType = NETWORK_IPV4;
//在接口输出队列中插入数据包
NetworkIpOutputQueueInsert(node,
interfaceIndex,
msg,
nextHop,
ipHeader->ip_dst,
NETWORK_PROTOCOL_IP,
&queueIsFull);
//队列已满,输出ICMP错误消息
if (queueIsFull)
{
if (ip->isIcmpEnable && icmp->sourceQuenchEnable)
BOOL ICMPErrorMsgCreated = NetworkIcmpCreateErrorMessage(node,
msg,
ipHeader->ip_src,
incomingInterface,
ICMP_SOURCE_QUENCH,
ICMP_SOURCE_QUENCH_CODE,
0,
0);
MESSAGE_Free(node, msg);
return;
}
//发送数据
if (queueWasEmpty)
{
if (!NetworkIpOutputQueueIsEmpty(node, interfaceIndex))
{
MAC_NetworkLayerHasPacketToSend(node, interfaceIndex);
}
}
}
1.4 MAC层
mac.cpp中函数MAC_NetworkLayerHasPacketToSend,主要区分MAC层协议类型进行分别调用。
void MAC_NetworkLayerHasPacketToSend(Node *node, int interfaceIndex)
{
//进入接口
node->enterInterface(interfaceIndex);
switch (node->macData[interfaceIndex]->macProtocol)
{
...
case MAC_PROTOCOL_DOT11:
{
MacDot11NetworkLayerHasPacketToSend(
node, (MacDataDot11 *) node->macData[interfaceIndex]->macVar);
break;
}
case MAC_PROTOCOL_CSMA:
{......}
case MAC_PROTOCOL_MACA:
{...... }
case MAC_PROTOCOL_TDMA:
{......}
...
}
//退出接口
node->exitInterface();
}
以mac802.11协议为例,调用mac_dot11.cpp中MacDot11NetworkLayerHasPacketToSend()函数,此函数主要作用:一是将数据包从网络层移至链路层,并添加帧首部,见函数MacDot11StationMoveAPacketFromTheNetworkLayerToTheLocalBuffer();二是在链路空闲时,尝试发送数据,见函数MacDot11StationAttemptToGoIntoWaitForDifsOrEifsState()。两个函数在mac_dot11_sta.h中定义
void MacDot11NetworkLayerHasPacketToSend(
Node* node,
MacDataDot11* dot11)
{
//将数据包由网络层移至缓存
MacDot11StationMoveAPacketFromTheNetworkLayerToTheLocalBuffer(
node,
dot11);
//链路状态为空闲
if (dot11->state == DOT11_S_IDLE)
{
MacDot11StationAttemptToGoIntoWaitForDifsOrEifsState(node, dot11);
}
}
MacDot11StationAttemptToGoIntoWaitForDifsOrEifsState()函数主要判断是否启用RTS\CTS机制,判断物理层状态是否为空闲,只有在空闲时才启动定时器发送消息,非空闲时取消定时器。
static //inline//
void MacDot11StationAttemptToGoIntoWaitForDifsOrEifsState(
Node* node,
MacDataDot11* dot11)
{
BOOL mustWaitForNAV = FALSE;
clocktype navTimeoutTime = 0;
if (!dot11->useDvcs)
{
if (MacDot11StationPhyStatus(node, dot11) != PHY_IDLE)
{
MacDot11StationSetState(node, dot11, DOT11_S_IDLE);
MacDot11StationCancelTimer(node, dot11);
return;
}
if (!dot11->IsInExtendedIfsMode)
{
if (MacDot11StationWaitForNAV(node, dot11))
{
// Set timer to wait for NAV to finish.
mustWaitForNAV = TRUE;
navTimeoutTime = dot11->NAV;
}
}
else
{
dot11->NAV = 0;
}
}
else
{
if (MacDot11StationPhyStatus(node, dot11) == PHY_RECEIVING)
{
MacDot11StationSetState(node, dot11, DOT11_S_IDLE);
MacDot11StationCancelTimer(node, dot11);
return;
}
if (MacDot11StationPhyStatus(node, dot11) == PHY_SENSING)
{
MacDot11StationSetState(node, dot11, DOT11_S_IDLE);
MacDot11StationCancelTimer(node, dot11);
return;
}
ERROR_Assert(MacDot11StationPhyStatus(node, dot11) == PHY_IDLE,
"Current state should be IDLE");
}
if (mustWaitForNAV)
{
MacDot11StationSetState(node, dot11, DOT11_S_WFNAV);
MacDot11StationStartTimer(node, dot11, (navTimeoutTime - getSimTime(node)));
}
else
{
clocktype extraWait = 0;
MacDot11StationSetState(node, dot11, DOT11_S_WF_DIFS_OR_EIFS);
if (getSimTime(node) < dot11->noOutgoingPacketsUntilTime)
{
extraWait = dot11->noOutgoingPacketsUntilTime - getSimTime(node);
}
if (dot11->IsInExtendedIfsMode)
{
MacDot11StationStartTimer(
node,dot11, (extraWait + dot11->extendedIfsDelay));
}
else
{
MacDot11StationStartTimer(
node, dot11, (extraWait + dot11->txDifs));
}
}
}
在定时器中,调度MAC层802.11协议事件MSG_MAC_TimerExpired。
static
void MacDot11StationStartTimer(
Node* node,
MacDataDot11* dot11,
clocktype timerDelay)
{
Message *newMsg;
dot11->timerSequenceNumber++;
newMsg = MESSAGE_Alloc(node, MAC_LAYER, MAC_PROTOCOL_DOT11,
MSG_MAC_TimerExpired);
MESSAGE_SetInstanceId(newMsg, (short) dot11->myMacData->interfaceIndex);
MESSAGE_InfoAlloc(node, newMsg, sizeof(dot11->timerSequenceNumber));
*((int*)(MESSAGE_ReturnInfo(newMsg))) = dot11->timerSequenceNumber;
newMsg->originatingNodeId = node->nodeId;
MESSAGE_Send(node, newMsg, timerDelay);
}
由mac.cpp函数MAC_ProcessEvent()调度MacDot11Layer()调度MSG_MAC_TimerExpired事件,调用MacDot11HandleTimeout()函数发送数据。MacDot11HandleTimeout()根据链路状态决定是传输帧还是修改链路状态,或重新发送。传输帧函数为MacDot11StationTransmitFrame()。
static //inline//
void MacDot11HandleTimeout(
Node* node,
MacDataDot11* dot11,
Message* msg)
{
...
switch (dot11->state)
{
//等待DIFS或EIFS状态
case DOT11_S_WF_DIFS_OR_EIFS:
{
if (dot11->BO == 0)
{
if (MacDot11StationHasFrameToSend(dot11))
{
//传输帧
MacDot11StationTransmitFrame(node, dot11);
}
else
{
MacDot11StationSetState(node, dot11, DOT11_S_IDLE);
}
}
else
{
MacDot11StationContinueBackoff(node, dot11);
MacDot11StationSetState(node, dot11, DOT11_S_BO);
MacDot11StationStartTimer(node, dot11, dot11->BO);
}
break;
}
//等待CTS
case DOT11_S_WFCTS:
{
//重新发送
MacDot11StationRetransmit(node, dot11);
break;
}
//等待数据
case DOT11_S_WFDATA:
{
MacDot11StationSetState(node, dot11, DOT11_S_IDLE);
MacDot11StationCheckForOutgoingPacket(node, dot11, FALSE);
break;
}
}
}
接下文。