%# -*- coding: utf-8-unix -*-

\section{三层路由器实现}
\label{sec:c:router-ob:s:router}

\subsection{实验目的}
\label{subsec:c:router-ob:s:router_object}

本实验的主要目的是让学生掌握二层以太网帧、ARP协议和三层IP协议等的数据结构，熟悉各协议的解析与封装过程，掌握三层路由器的工作原理、分组处理流程、FIB表和邻接表生成与使用。熟悉路由器数据平面与控制平面的切分及不同的工作处理流程。

\subsection{实验内容}
\label{subsec:c:router-ob:s:router_content}

使用可编程网络平台及三层路由器示例框架源码，设计完成一个三层路由器原型系统。主要完成以下内容：

\begin{enumerate}
    \item \textbf{系统路由验证：}搭建测试环境，验证系统自带三层路由原型系统的功能，实现不同网段主机的相互通信功能；
    \item \textbf{逻辑设计与实现：}基于路由器示例框架代码，分别设计完成路由器的分组解析、FIB同步与查表、二层与三层数据更新、ARP请求与响应等模块功能；
    \item \textbf{功能调试与验证：}根据路由器分组处理流程，分别调试每一类分组的处理逻辑。如ARP请求类分组、ARP响应类分组、控制平面分组和数据平面分组；组网测试路由器功能，实现不同网段内主机之间的相互通信功能。
\end{enumerate}

\subsection{实验原理、方法和手段}
\label{subsec:c:router-ob:s:router_principle}

\subsubsection{系统路由验证}

请参考附录\ref{app:openbox}：《FAST平台介绍与操作说明手册》完成。
\subsubsection{三层路由器工作原理}
三层路由器工作于OSI模型的第3层（网络层），根据网络层中的目的IP地址进行查表，获得分组转发的下一跳输出端口及下一跳的IP地址。更新IP层的ttl值与checksum值，并在输出端口对二层数据进行重新封装二层数据后转发。路由器还具备ARP响应与请求功能，支持响应请求自己IP的MAC信息与请求路由下一跳IP的MAC信息。

\subsubsection{路由器分组处理流程}

\begin{enumerate}
    \item \textbf{线程同步系统FIB表：}通过一个独立线程，读取内核FIB表信息，
    保存到路由器的FIB表结构中；循环不断的获取内核FIB表，一旦有更新，
    则刷新自己的FIB表内容；
    \item \textbf{输入分组解析：}接收各端口输入分组信息，并进行分组解析处理，根据目的MAC和目的IP地址信息判断分组为控制平面分组还是数据平面分组；
    \item \textbf{控制平面与数据平面分流：}将控制平面数据转发到协议栈进行处理，将数据平台数据转发到路由器数据转发逻辑中处理。若是本机MAC的ARP请求，则构造ARP响应进行回复；
    \item \textbf{提取目的IP并查表：}路由器数据转发逻辑进一步解析数据，解析提取目的IP地址，送入FIB查表引擎进行匹配，获取分组的下一跳IP地址及输出端口信息；
    \item \textbf{更新三层数据：}更新分组ttl值与checksum值，根据FIB表结果，将分组转发至对应端口输出队列；
    \item \textbf{更新二层数据：}更新FIB查表下一跳IP的MAC地址信息，查表无结果，则启动ARP请求模块；若查到对应MAC信息，则更新二层数据后直接转发；
    \item \textbf{ARP请求：}根据请求的IP地址与发送端口信息构造ARP协议的请求报文，发送到端口进行学习，并将学习结果保存到该端口的邻接表中；
    \item \textbf{端口队列输出：}抽取端口输出队列内的发送分组，更新二层数据后由端口直接发送。
\end{enumerate}

\subsection{实验条件}
\label{subsec:c:router-ob:s:router_requirement}
 
\begin{itemize}
  \item 可编程网络平台一个，路由测试主机两台，IP配置及连接拓扑如图\ref{fig:c:router-ob_router-topo}所示；
  \item 串口线一根，网线两根；
  \item 三层路由器框架源代码（开发环境与编译工具系统自带）。
\end{itemize}

\begin{figure}[!ht]
  \centering
  \includegraphics[width=8cm]{router-topo}
  \caption{三层路由器实验拓扑图}
  \label{fig:c:router-ob_router-topo}
\end{figure}

\subsection{实验步骤}
\label{subsec:c:router-ob:s:router_procedure}

\subsubsection{系统路由验证}
\begin{enumerate}
  \item 登录可编程网络平台，启动系统自带三层路由器命令，系统输出如下信息说明已正常启动且进入工作模式，已省略部分模块输出信息。

\begin{code}[console]
  root@HNXS-FAST:/mnt/openbox# l3router 
  \end{code}
  \begin{code}[text]  
  fastU->REG Version:20180827,OpenBox HW Version:2030200722
  FAST UA REG->from pid:907,state:21,mid:132
  fastU->Register UA to FAST Kernel! Wait Reply......
  fastU->UA->pid:907,mid:132,Register OK!
  fastU->libua version:20180827
  fastU->fast_ua_recv......
\end{code}
\end{enumerate}

\subsubsection{设计开发三层路由器}
\begin{enumerate}
  \item 阅读三层路由器框架源代码文件，根据三层路由工作原理及分组处理流程设计完成框架源代码中的TODO部分内容；
  \item 编译自己的三层路由器源码，生成相应的路由器系统命令user\_l3router。
\end{enumerate}

\subsubsection{调试与验证}
\begin{enumerate}
  \item 运行自己的三层路由器程序，验证路由器的功能；
  \item 路由器正常工作后，显示如下信息；
  \begin{itemize}
    \item 加载路由器接口地址信息
    \begin{code}[text]
      Port[0]->obx0,MAC->00:0A:35:01:00:5B,IPv4->192.168.0.1,
          IPv6->0000:0000:0000:0000:0000:0000:0000:0000
      Port[1]->obx1,MAC->00:0A:35:00:00:02,IPv4->192.168.1.1,
          IPv6->0000:0000:0000:0000:0000:0000:0000:0000
      Port[2]->obx2,MAC->00:0A:35:00:00:03,IPv4->192.168.2.1,
          IPv6->0000:0000:0000:0000:0000:0000:0000:0000
      Port[3]->obx3,MAC->00:0A:35:00:00:04,IPv4->192.168.3.1,
          IPv6->0000:0000:0000:0000:0000:0000:0000:0000
    \end{code}

    \item 同步内核FIB表
    \begin{code}[text]
      Iface   Destination     Gateway Flags   RefCnt  Use     Metric  Mask            MTU     Window  IRTT    Port                       
      obx0    0000A8C0        00000000        0001    0       0       0       00FFFFFF        0       0       0       0
      obx1    0001A8C0        00000000        0001    0       0       0       00FFFFFF        0       0       0       1
      obx2    0002A8C0        00000000        0001    0       0       0       00FFFFFF        0       0       0       2
      obx3    0003A8C0        00000000        0001    0       0       0       00FFFFFF        0       0       0       3
    \end{code}

    \item 启动端口输出队列线程
    \begin{code}[text]
      Create handle_queue_packet on Port[0] thread OK!
      handle_queue_packet on port[0]
      handle_queue_packet[0]->Wait pkt INQUEUE...
      Create handle_queue_packet on Port[1] thread OK!
      handle_queue_packet on port[1]
      handle_queue_packet[1]->Wait pkt INQUEUE...
      Create handle_queue_packet on Port[2] thread OK!
      handle_queue_packet on port[2]
      handle_queue_packet[2]->Wait pkt INQUEUE...
      Create handle_queue_packet on Port[3] thread OK!
      handle_queue_packet on port[3]
      handle_queue_packet[3]->Wait pkt INQUEUE...
    \end{code}

    \item 开始接收分组处理
    \begin{code}[text]
      fastU->REG Version:20180827,OpenBox HW Version:2030200722
      FAST UA REG->from pid:923,state:21,mid:132
      fastU->Register UA to FAST Kernel! Wait Reply......
      fastU->UA->pid:923,mid:132,Register OK!
      fastU->libua version:20180827
      fastU->fast_ua_recv......
    \end{code}

    \item 控制平面分组处理
    \begin{code}[text]
      UA Recv ptk 0xb2c00468,len:130,inport:0
      Recv IPv4 Packet!
      Recv Unicast Host Packet,Dispatch Packet!
      Control Plane->Send to Protocol Stack[128]!
    \end{code}

    \item 数据平面分组处理
    \begin{code}[text]
      UA Recv ptk 0xb2c00468,len:130,inport:2
      Recv IPv4 Packet!
      Recv Unicast Host Packet,Dispatch Packet!
      Data Plane->FIB(dst:192.168.0.111) tblidx:0
      Data Plane->Next Hop:192.168.0.111,outport:0
      //查不到MAC，入队后学习MAC
      Data Plane->port[0],NEIGH[1],IN QUEUE[1]
      Data Plane->Active Thread on Port[0],QUEUE[1] len:2
      //查询到MAC，直接封闭二层数据后发送
      Data Plane->port[2],NEIGH[0],DMAC->B8:27:EB:C1:D1:39
      Pack L2 on Port[2]
      pkt_send_normal->0xb2c00468,outport:2,len:130
    \end{code}

    \item ARP请求处理
    \begin{code}[text]
      send_arp_request 192.168.2.119 on Port[2]
      pkt_send_normal->0xb2b00468,outport:2,len:74
      pkt_send_normal->0xb2b00468,outport:2,len:74
      pkt_send_normal->0xb2b00468,outport:2,len:74
    \end{code}

    \item ARP响应处理
    \begin{code}[text]
      UA Recv ptk 0xb2c00468,len:92,inport:0
      Recv ARP Packet!
      ARP->Host IPv4
      ARP->Response,192.168.0.111[B8:27:EB:D8:83:20]--> 192.168.0.1[00:0A:35:01:00:5B]
      Learn SMAC Port[0],B8:27:EB:D8:83:20
      Learn SMAC Port[0],NEIGH[0],Update[B8:27:EB:D8:83:20]!
    \end{code}

    \item 路由不可达
    \begin{code}[text]
      ICMP Report and Drop!
    \end{code}

    \item FIB表刷新
    \begin{code}[text]
      FIB4 fresh...
    \end{code}

  \end{itemize}

  \item 路由器工作不正确时，使用\texttt{xprintf}函数打印调试，
  建议根据不同类别数据流进行打印分析，调试某一些报文时，其他无关内容均不打印；
  \item 在任意一台测试主机\texttt{ping 8.8.8.8}，
  观察路由器分组处理流程及FIB表查找结果变化；
  \item 路由器正常工作后，复制保留程序输出日志信息。
\end{enumerate}

\subsection{思考题}
\label{subsec:c:router-ob:s:router_rethink}

\begin{enumerate}
  \item 系统内核的FIB表是如何生成的？生成FIB表的路由协议有哪些，各适应哪些环境？
  \item 什么是静态路由表？什么是动态路由表？
  \item FIB表查表不命中如何处理？
  FIB表指示的下一跳IP的MAC地址不存在如何处理当前分组数据？
  \item 本程序采用单核单线程处理多个端口的数据转发，如何改成多核多线程方式？
\end{enumerate}

\subsection{注意事项及有关说明}
\label{subsec:c:router-ob:s:router_notice}

\begin{enumerate}
  \item 可编程网络平台的路由器只是一个基础的路由器原型，分组处理方法与流程设计并不是必须严格遵守的规范，学生可根据自己理解创新实现；
  \item 控制平面的数据是通过平台开发环境重新导入到了系统内核处理，
  目的是为了支持该系统上的其他正常SOCKET应用，如Quagga路由软件。
\end{enumerate}

\subsection{考核方法}
\label{subsec:c:router-ob:s:router_criterion}

完成本次实验，需要提交一份实验报告、一份程序源代码和一份程序输出日志。程序源代码中用户添加的代码需要有详细的注释说明。

\begin{enumerate}
  \item （20分）在规定时间内完成实验，并提交实验成果；
  \item （40分）实验报告中有详细的路由逻辑设计分析与实现说明；
  \item （10分）程序正常运行，测试主机可以ping通或抓包接收到对端报文；
  \item （10分）ARP请求与响应功能正常工作；
  \item （10分）测试主机可以ping通路由器端口的IP地址；
  \item （10分）实验报告与源代码内容完整、格式规范。
\end{enumerate}
