NS3中的路由模型-5 OLSR路由协议
目录
- 1. OLSR工作原理
- **1.1. 关键机制**
- **1.2. 消息类型**
- **1.3. 工作流程**
- 1.3.1 路由建立过程
- 1.3.2 数据包转发过程
- 1.4. NS3中的关键函数和流程
- 1.5. 示例:数据包转发的代码路径
- **2. OLSR 在 NS-3 中的实现**
- **2.1. 核心类**
- **2.2. 路由建立过程**
- **1. 邻居发现(HELLO 消息处理)**
- **2. MPR 选择(多点中继计算)**
- **3. 拓扑扩散(TC 消息洪泛)**
- **4. 路由表计算**
- **2.3. 数据包转发过程**
- **1. 数据包进入网络层**
- **2. 路由查询**
- **3. 数据包转发**
- **2.4. 动态维护机制**
- **1. 链路失效处理**
- **2. 定时器驱动事件**
- **2.5. 示例:NS-3 中查看 OLSR 路由表**
- **四、与其它协议的对比**
NS3中的路由模型-5 OLSR路由协议
1. OLSR工作原理
核心思想:通过选择多点中继(MPR, MultiPoint Relays) 来优化控制消息(尤其是拓扑信息)的传播范围,减少冗余广播。
1.1. 关键机制
-
邻居发现(Neighbor Detection):
- 每个节点定期广播 HELLO 消息(小范围广播,TTL=1)。
- HELLO 消息包含:邻居列表、链路状态(对称/非对称)、MPR 选择器状态等。
- 用于维护一跳邻居和两跳邻居信息。
-
多点中继(MPR)选择:
- 每个节点独立选择自己的 MPR 集合。
- 目标:选出一组最小的一跳邻居,使得通过这些邻居可以覆盖所有两跳邻居。
- 作用:只有 MPR 节点有权转发 TC(Topology Control)消息,减少洪泛开销。
-
拓扑扩散(Topology Dissemination):
- MPR 节点定期广播 TC 消息(Topology Control)。
- TC 消息包含:该 MPR 的邻居信息(即 MPR Selector 列表)。
- 非 MPR 节点不转发 TC 消息,极大减少控制开销。
-
路由表计算:
- 节点利用收集到的 TC 消息构建全网拓扑图。
- 使用 Dijkstra 算法计算到所有节点的最短路径。
- 路由表包含:目的地址、下一跳地址、跳数等。
1.2. 消息类型
消息类型 | 作用 | 发送范围 |
---|---|---|
HELLO | 邻居发现与维护 | 一跳 (TTL=1) |
TC | 扩散拓扑信息 | 全网 (由 MPR 转发) |
MID | 多接口声明(可选) | 全网 |
HNA | 主机与网络关联(连接外部网络) | 全网 |
1.3. 工作流程
NS3中OLSR的具体实现细节,包括路由建立(邻居发现、MPR选择、拓扑扩散和路由计算)以及数据包如何根据路由表进行转发。
1.3.1 路由建立过程
在NS3中,OLSR的路由建立过程遵循RFC 3626,并通过几个周期性的事件来驱动:
- Hello消息的发送与邻居发现:
- 每个节点会周期性地(默认2秒)广播Hello消息(通过
SendHello()
函数实现)。Hello消息只在一跳范围内传播(TTL=1)。 - Hello消息包含以下信息:
- 邻居接口地址列表(包括链路类型:对称链路、非对称链路或丢失链路)
- 该节点选择的MPR集合(MPR Set)
- MPR选择器集合(即哪些邻居选择本节点作为MPR)
- 当节点收到邻居的Hello消息,它会更新邻居表(Neighbor Set)和两跳邻居表(Two-hop Neighbor Set)。这些表存储在
OlsrState
对象中。
- 每个节点会周期性地(默认2秒)广播Hello消息(通过
- MPR选择:
- 在收到Hello消息后(或者当邻居信息发生变化时),节点会触发MPR计算(
MprComputation()
函数)。 - MPR选择的目标是选择最小数量的一跳邻居,使得这些邻居可以覆盖所有的两跳邻居(即每个两跳邻居至少有一个一跳邻居是所选MPR)。
- 在NS3中,MPR选择算法在
RoutingProtocol::MprComputation()
中实现,它使用贪心算法来求解。
- 在收到Hello消息后(或者当邻居信息发生变化时),节点会触发MPR计算(
- TC消息的发送与拓扑扩散:
- 被选为MPR的节点会周期性地(默认5秒)广播TC消息(通过
SendTc()
函数)。 - TC消息包含该节点的MPR选择器集合(即哪些邻居选择本节点作为MPR)。这样,其他节点就可以通过这些信息构建全网拓扑。
- TC消息会被MPR节点转发(但每个节点只转发一次),从而在整个网络内扩散。在NS3中,TC消息的转发通过
RoutingProtocol::ForwardDefault()
处理。
- 被选为MPR的节点会周期性地(默认5秒)广播TC消息(通过
- 路由表计算:
- 节点在以下情况下会触发路由表计算(
RoutingProtocol::RoutingTableComputation()
):- 收到TC消息(拓扑信息更新)
- 邻居表发生变化(如链路断开)
- 路由表过期(默认周期为5秒)
- 路由计算使用Dijkstra算法,基于收集到的拓扑信息(存储在拓扑表中)计算到所有节点的最短路径。
- 计算出的路由存储在节点的路由表中(
m_table
)。
- 节点在以下情况下会触发路由表计算(
1.3.2 数据包转发过程
一旦路由表建立,数据包的转发过程如下:
- 数据包到达网络层:
- 当节点需要发送数据包时,它会查询路由表(通过
RouteOutput()
函数)。 - 对于转发的数据包(即目的地址不是本节点),节点会调用
RouteInput()
函数来决定如何处理。
- 当节点需要发送数据包时,它会查询路由表(通过
- 路由查询:
- 在
RouteInput()
中,节点首先检查数据包的目的地址是否为本节点或广播地址。如果不是,则查询路由表(Lookup()
函数)以确定下一跳。 - 路由表查询返回一个
Ipv4Route
对象,其中包含下一跳地址和输出接口。
- 在
- 数据包转发:
- 如果找到有效路由,节点会将数据包发送到下一跳节点(通过
SendPacket()
函数)。 - 如果没有找到路由,数据包会被丢弃,并可能触发ICMP目的不可达消息。
- 如果找到有效路由,节点会将数据包发送到下一跳节点(通过
- 路由维护:
- OLSR会持续维护路由信息。当链路断开时(通过Hello消息检测到邻居丢失),节点会更新邻居表,重新计算MPR,并触发新的TC消息广播,从而更新全网路由。
1.4. NS3中的关键函数和流程
- 周期性事件:
HelloTimerExpire()
:触发Hello消息的发送。TcTimerExpire()
:触发TC消息的发送(如果是MPR)。MidTimerExpire()
:处理多接口声明(可选)。HnaTimerExpire()
:处理主机和网络关联(连接外部网络)。RoutingTableCalculationTimerExpire()
:周期性触发路由表计算。
- 消息处理函数:
RecvOlsr()
:接收OLSR消息的总入口,根据消息类型分发给ProcessHello()
、ProcessTc()
等函数。ProcessHello()
:处理收到的Hello消息,更新邻居信息。ProcessTc()
:处理收到的TC消息,更新拓扑表。
- 路由计算:
RoutingTableComputation()
:执行Dijkstra算法,计算最短路径。
1.5. 示例:数据包转发的代码路径
假设节点A要发送一个数据包给节点D:
- 节点A的应用层产生数据包,传递给网络层。
- 网络层调用
Ipv4L3Protocol::Send()
,进而调用路由协议的RouteOutput()
。 OlsrRoutingProtocol::RouteOutput()
查询路由表(m_table
),找到到D的下一跳(比如节点B)。- 节点A将数据包封装成IP包,目的IP为D,下一跳MAC地址为节点B的MAC地址。
- 节点B收到数据包后,同样调用
RouteInput()
,查询路由表,找到下一跳(比如节点C)。 - 如此继续,直到数据包到达节点D。
2. OLSR 在 NS-3 中的实现
NS-3 通过 olsr
模块实现了完整的 OLSR 协议(RFC 3626),位于 src/olsr
目录。
2.1. 核心类
类名 | 功能 |
---|---|
ns3::OlsrHelper | 集成 OLSR 到仿真环境的辅助类 |
ns3::OlsrRouting | OLSR 主逻辑实现(路由表计算、消息处理) |
ns3::OlsrState | 维护 OLSR 状态(邻居表、拓扑表等) |
ns3::OlsrPacketHeader | OLSR 消息包头定义 |
2.2. 路由建立过程
路由建立通过周期性消息交换完成,分为四个关键阶段:
1. 邻居发现(HELLO 消息处理)
源码函数: ProcessHello()
工作流程:
NS-3 实现细节:
- HELLO 消息包含邻居地址和链路状态 (
LinkType
):// 消息结构 (olsr-packet-header.cc) struct HelloMessage {uint8_t linkCode; // 链路类型: ASYMMETRIC/SYMMETRIC/MPR_LINKIpv4Address neighbor; };
- 邻居表更新逻辑:
// olsr-routing-protocol.cc void RoutingProtocol::ProcessHello(...) {if (linkType == SYMMETRIC) { m_neighborSet.insert(neighborAddr); // 添加对称邻居} else if (linkType == LOST_LINK) {m_neighborSet.erase(neighborAddr); // 删除失效邻居} }
2. MPR 选择(多点中继计算)
源码函数: MprComputation()
算法逻辑:
graph TDA[获取两跳邻居集合] --> B[筛选未覆盖的邻居]B --> C{是否有未覆盖邻居?}C -->|是| D[选择覆盖最多邻居的一跳节点]D --> E[标记为 MPR]E --> BC -->|否| F[结束]
NS-3 实现:
- 使用贪心算法选择最小 MPR 集合:
void RoutingProtocol::MprComputation() {while (!twoHopNeighbors.empty()) {// 选择覆盖最多两跳邻居的一跳节点auto bestNeighbor = SelectMaxCoverageNode();m_mprSet.insert(bestNeighbor); RemoveCoveredNeighbors(bestNeighbor);} }
3. 拓扑扩散(TC 消息洪泛)
源码函数: SendTc()
, ProcessTc()
关键流程:
NS-3 特性:
- TC 消息包含 MPR Selector 列表:
struct TcMessage {uint16_t ansn; // 序列号std::vector<Ipv4Address> mprSelectors; // 选择本节点的MPR列表 };
- 转发规则 (
olsr-routing-protocol.cc
):bool RoutingProtocol::ForwardDefault(Ptr<Packet> p) {if (!IsMpr(originator)) return false; // 非MPR不转发if (m_dupSet.find(p->GetUid()) != m_dupSet.end()) return false; // 重复检测SendPacket(p); // 广播转发 }
4. 路由表计算
源码函数: RoutingTableComputation()
算法:
- 基于 Dijkstra 最短路径算法
- 输入:拓扑表 (
m_topologySet
) - 输出:路由表 (
m_table
)
NS-3 实现:
void RoutingProtocol::RoutingTableComputation() {// 1. 初始化距离向量std::map<Ipv4Address, uint32_t> dist;for (auto& node : allNodes) dist[node] = INFINITY;// 2. Dijkstra 核心while (!unvisited.empty()) {Ipv4Address u = ExtractMin(unvisited);for (auto& v : GetNeighbors(u)) {uint32_t alt = dist[u] + 1; // OLSR 跳数=1if (alt < dist[v]) {dist[v] = alt;m_table[v] = u; // 记录下一跳}}}
}
2.3. 数据包转发过程
当路由表建立后,数据包转发流程如下:
1. 数据包进入网络层
源码入口: Ipv4L3Protocol::Receive()
→ RouteInput()
2. 路由查询
源码函数: RouteOutput()
逻辑:
// olsr-routing-protocol.cc
Ptr<Ipv4Route> RoutingProtocol::RouteOutput(...) {auto it = m_table.find(dest);if (it != m_table.end()) {// 找到路由:构建 Ipv4Route 对象rt->SetDestination(dest);rt->SetGateway(it->second); // 下一跳地址rt->SetOutputInterface(interface);return rt;}return nullptr; // 无路由则丢弃
}
3. 数据包转发
转发路径:
flowchart LRA[源节点] -->|查询路由表| B{路由存在?}B -->|是| C[发送到下一跳]B -->|否| D[丢弃并发送ICMP错误]C --> E[下一跳节点]E --> F[重复路由查询]F --> G[目标节点]
关键约束:
- TTL 递减: 每跳 TTL 减 1,为 0 时丢弃
- 链路层广播: 无线网络中使用 MAC 广播地址
FF:FF:FF:FF:FF:FF
- MPR 无关性: 数据转发不依赖 MPR,仅使用路由表
2.4. 动态维护机制
OLSR 在 NS-3 中持续维护路由状态:
1. 链路失效处理
- 检测机制: 3 次未收到 HELLO 则判定邻居失效
- 响应动作:
void RoutingProtocol::NeighborLoss(Ipv4Address neighbor) {m_neighborSet.erase(neighbor);RecomputeMpr(); // 重新计算MPRRebuildRoutingTable(); // 重建路由表 }
2. 定时器驱动事件
定时器类型 | 默认间隔 | 触发动作 |
---|---|---|
HelloTimer | 2s | 广播 HELLO |
TcTimer | 5s | MPR 节点广播 TC |
RoutingTimer | 5s | 重新计算路由表 |
NeighborTimer | 6s | 检查邻居超时 |
2.5. 示例:NS-3 中查看 OLSR 路由表
在仿真脚本中添加以下代码打印路由表:
// 仿真结束后打印节点0的路由表
Ptr<OutputStreamWrapper> stream = Create<OutputStreamWrapper>("olsr_routes.txt", std::ios::out);
olsrProto->PrintRoutingTable(stream);
输出示例:
节点 0 路由表:
目的网络 下一跳 接口 跳数
10.1.1.3 10.1.1.3 1 1
10.1.1.12 10.1.1.4 1 2
10.1.1.25 10.1.1.7 1 3
...
四、与其它协议的对比
特性 | OLSR | AODV | DSDV |
---|---|---|---|
路由类型 | 主动式 | 按需式 | 主动式 |
控制开销 | 中等(周期TC) | 低(按需) | 高(全洪泛) |
时延 | 低(路由已存) | 较高(需发现) | 低 |
适用场景 | 中型稳定网络 | 动态小型网络 | 小型静态网络 |