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

\section{二层交换机实现}
\label{sec:c:router-ob:s:switch}

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

本实验的主要目的是让学生掌握可编程网络平台的二层以太网数据收发方法，熟悉二层以太网帧的数据结构，掌握MAC地址的解析与使用。熟悉二层交换机的分组交换原理、设计完成二层交换的分组处理流程和转发表的老化功能与处理方法。

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

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

\begin{enumerate}
    \item \textbf{平台使用与验证：}掌握可编程网络平台及示例开发代码的修改、编译和运行操作，验证系统自带二层交换机的功能；
    \item \textbf{逻辑设计与实现：}设计满足二层交换功能的分组处理流程和逻辑，如源MAC学习功能、目的MAC查表功能、端口输出（单播与泛洪）功能和MAC转发表老化功能；
    \item \textbf{功能调试与验证：}调试自己开发的二层交换机逻辑，验证是否满足各端口间的交换通信功能。
\end{enumerate}

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

\subsubsection{平台基本使用}

请参考附录\ref{app:openbox}：《FAST平台介绍与操作说明手册》完成。

\subsubsection{二层交换工作原理}

二层交换机工作于OSI模型的第2层（数据链路层），可以识别数据帧中的源MAC地址与输入端口信息，并将这些MAC地址与对应的端口记录在自己内部的一个地址表（交换表）中。然后根据目的MAC地址进行交换表的查找，获得目的MAC的输出端口，然后进行转发。根据目的MAC地址的不同，转发方式还分为：单端口转发（单播）、多端口转发（多播）和泛洪转发（广播）。

\subsubsection{二层交换分组处理流程}

\begin{enumerate}
    \item \textbf{源MAC学习：}当交换机从某个端口收到一个数据帧，它先读取包头中的源MAC地址和输入端口号，这样就知道源MAC地址的机器是连在交换机的哪个端口上。以此做为MAC转发表的核心数据结构，学习或更新到转发表中；
    \item \textbf{目的MAC查表：}读取数据帧头中的目的MAC地址，并在MAC转发表中查找，找到相同MAC的匹配项后，获取该表项相应的端口信息（返回-1表示查表不命中）；
    \item \textbf{转发输出：}根据目的MAC的查表结果，判断输出端口号，若端口号为-1，表示查不到目的MAC表项，则进行泛洪转发；否则把数据帧直接复制到查表返回的端口上进行转发；
    \item \textbf{MAC表老化：}每次源MAC学习时都更新该MAC表项的使用时间；单独启动一个线程，逐项比较MAC表项的最新使用时间与系统当前时间的差值，如果超过指定老化时长（如3分钟）则将该表项删除。表项删除可将其有效位置无效，提高处理效率。
\end{enumerate}

\subsubsection{实验方法及手段}

可编程网络平台提供了完整软硬件开发环境和网络功能（交换或路由等）的示例应用程序及相应框架源代码。学生使用挖空了部分逻辑功能的示例框架源码版本完成网络功能的设计与调试工作。

\subsection{实验条件}
\label{subsec:c:router-ob:s:switch_requirement}

\begin{itemize}
    \item 可编程网络平台一个，交换测试主机两台，连接拓扑及网络配置
    如下图\ref{fig:c:router-ob_switch-topo}所示；
    \item 串口线一根，网线两根；
    \item 可编程网络平台使用手册和二层交换机框架源代码（开发环境与编译工具系统自带）。
\end{itemize}

\begin{figure}[!ht]
    \centering
    \includegraphics[width=8cm]{switch-topo}
    \caption{二层交换机实验拓扑图}
    \label{fig:c:router-ob_switch-topo}
  \end{figure}

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

\subsubsection{平台搭建与系统交换验证}

\begin{enumerate}
  \item 请参考附录\ref{app:openbox}：《FAST平台介绍与操作说明手册》完成；
  \item 在串口登录界面输入如下命令，启动系统自带二层交换机命令。系统输出如下信息说明已正常启动且进入工作模式。
  \begin{code}[console]
    root@HNXS-FAST:/mnt/openbox# l2switch 
  \end{code}
  \begin{code}[text]
    fastU->REG Version:20180827,OpenBox HW Version:2030200722
    FAST UA REG->from pid:902,state:21,mid:129
    fastU->Register UA to FAST Kernel! Wait Reply......
    fastU->UA->pid:902,mid:129,Register OK!
    fastU->libua version:20180827
    Create nm08_mac_aging thread OK!
    aging[0]->invalid mac:0
    fastU->fast_ua_recv......
    ID           PORT0          PORT1          PORT2          PORT3
    0               .              .              .              .
    aging[1]->invalid mac:0
    ID           PORT0          PORT1          PORT2          PORT3
    0               .              .              .              .
    aging[2]->invalid mac:0
  \end{code}
\end{enumerate}

\subsubsection{设计开发二层交换机}

\begin{enumerate}
  \item 阅读二层交换机框架源代码文件，
  根据二层交换工作原理及分组处理流程设计完成框架源代码中的TODO部分内容；
  \begin{code}[console]
    root@HNXS-FAST:/# cd /home/hnxs/l2switch/
    root@HNXS-FAST:/home/hnxs/l2switch# vim main_l2switch.c
  \end{code}

  以下为main\_l2switch.c文件的具体内容
  \begin{code}[c]
    /*地址学习过程，将报文的源MAC学习到对应端口MAC表中*/
    void learn_smac(u8 inport,u8 *src_mac)
    {
      /*更新之前查找空白存储MAC位置*/
      int i = 0,j = -1;
      xprintf("learn_smac->\n");
      //TODO User add code
      xprintf("learn_smac->add new MAC,port:%d,index:%d\n",inport,j);
    }
  \end{code}

  上述框架代码需要实现的主要功能是源MAC地址的学习，参数输入为分组数据的输入端口号和源MAC地址的指针。通过对系统MAC转发表的查找，判断源MAC转发表中是否存在该表项，若存在，则更新该表项的使用时间；若不存在，则找一个空闲的表项位置存储该条表项，并更新表项使用时间。

  \item 编译自己的二层交换机源码，生成相应的交换机系统命令user\_l2switch。
  \begin{code}[console]
    root@HNXS-FAST:/home/hnxs/l2switch# make
    gcc –o user_l2switch main_l2switch –lreg –lrule –lua -lthread
    root@HNXS-FAST:/home/hnxs/l2switch# ls
    main_l2switch.c Makefile user_l2switch
  \end{code}
  user\_l2switch为用户代码编译输出的可执行文件。
\end{enumerate}

\subsubsection{调试与验证}

\begin{enumerate}
  \item 运行自己的二层交换机程序，验证交换机的功能；

  \begin{code}[text]
    root@HNXS-FAST:/home/hnxs/l2switch# ./user_l2switch
    fastU->REG Version:20180827,OpenBox HW Version:2030200722
    FAST UA REG->from pid:902,state:21,mid:129
    fastU->Register UA to FAST Kernel! Wait Reply......
    fastU->UA->pid:902,mid:129,Register OK!
    fastU->libua version:20180827
    Create nm08_mac_aging thread OK!
    aging[0]->invalid mac:0
    fastU->fast_ua_recv......
    // 后面打印以学生代码为准……
  \end{code}
  运行当前目录下用户的可执行文件，必须在前面加上”./”

  \item 二层交换机正常工作后，显示如下信息；
  \begin{code}[text]
    root@HNXS-FAST:/mnt/openbox# l2switch 
    fastU->REG Version:20180827,OpenBox HW Version:2030200722
    FAST UA REG->from pid:902,state:21,mid:129
    fastU->Register UA to FAST Kernel! Wait Reply......
    fastU->UA->pid:902,mid:129,Register OK!
    fastU->libua version:20180827
    Create nm08_mac_aging thread OK!
    aging[0]->invalid mac:0
    fastU->fast_ua_recv......
    inport:0,dstmid:129,len:102,dmac:33:33:00:00:00:02,
      smac:B8:27:EB:04:FC:F0
    learn_smac->
    update_mac_time->port:0,index:0
    learn_smac->add new MAC,port:0,index:0
    find_dmac->ret = -1
    ------pkt_send_flood------
    pkt_send_normal->0xb4c00468,outport:1,len:102
    pkt_send_normal->0xb4c00468,outport:2,len:102
    pkt_send_normal->0xb4c00468,outport:3,len:102
    ID             PORT0              PORT1      PORT2      PORT3
    0  B8:27:EB:04:FC:F0  B8:27:EB:76:8F:DA          .          .
    1                  .                  .          .          .
  \end{code}
  \begin{itemize}
    \item 其中，端口接收到报文时，终端会显示输入端口及源目的MAC地址内容；
    \begin{code}[text]
      inport:0,dstmid:129,len:102,dmac:33:33:00:00:00:02,
        smac:B8:27:EB:04:FC:F0
    \end{code}      
    \item 然后进入源MAC学习阶段，并更新MAC时间；
    \begin{code}[text]
      learn_smac->
      update_mac_time->port:0,index:0
      learn_smac->add new MAC,port:0,index:0
    \end{code}      
    \item 然后进入目的MAC查表阶段，输出查表结果；
    \begin{code}[text]
      find_dmac->ret = -1
    \end{code}      
    \item 然后进行报文转发；
    \begin{code}[text]
      //泛洪发送
      ------pkt_send_flood------
      pkt_send_normal->0xb4c00468,outport:1,len:102
      pkt_send_normal->0xb4c00468,outport:2,len:102
      pkt_send_normal->0xb4c00468,outport:3,len:102
      //单播发送
      pkt_send_normal->0xb4c00468,outport:3,len:102
    \end{code}      
    \item 最后实时输出端口MAC地址信息，终端输出如下信息表示在端口0和端口1上分别学习到了两个MAC地址信息。
    \begin{code}[text]
      ID              PORT0              PORT1     PORT2     PORT3
      0   B8:27:EB:04:FC:F0  B8:27:EB:76:8F:DA         .         .
      1                   .                  .         .         .
    \end{code}      
  \end{itemize}

  \item 交换机工作不正确时，开启xprintf函数打印调试。仔细阅读二层交换框架代码、交换机的工作原理及分组处理流程；

  \item 交换机正常工作后，尝试切换测试主机连接在交换机的端口位置，观察测试主机的ping丢包，观察交换机工作界面的输出显示。
\end{enumerate}

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

\begin{enumerate}
  \item 交换机区分IPv4和IPv6协议吗？区分TCP和UDP吗？
  \item 广播风暴（broadcast storm）是如何产生的？如何避免？在不同的应用环境中分别采取的什么方法？
  \item 广播与组播报文如何判断？组播报文如何转发？
  \item MAC转发表的老化时间与哪些因素相关？网络规模、MAC表项大小？
\end{enumerate}

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

\begin{enumerate}
  \item 可编程网络平台的使用手册需要仔细阅读，严格参照文档说明搭建和运行该平台系统；
  \item 代码修改若不习惯vim命令，可将其文件通过网络方式下载到自己电脑，编辑修改完成后，再上传到实验平台进行验证。Windows平台的上传下载工具为pscp.exe；
  \item 二层交换机框架代码只完成了基本的二层数据收发功能，交换机的其他功能逻辑需要自己完成，根据二层交换机的实验原理与分组处理流程完成；
  \item 系统命令运行直接输入命令名称，当前目录的可执行文件运行需要在前面加上“./”。
\end{enumerate}

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

完成本次实验，需要提交一份实验报告、一份程序源代码和一份程序输出日志。程序源代码中用户添加的代码需要有详细的注释说明。
\begin{enumerate}
  \item （20分）在规定时间内完成实验，并提交实验成果；
  \item （40分）实验报告中有详细的交换逻辑设计分析与实现说明；
  \item （10分）程序正常运行，测试主机可以ping通或抓包接收到对端报文；
  \item （10分）MAC转发表在设定时间后可以老化；
  \item （10分）测试主机切换端口连接后仍能ping通；
  \item （10分）实验报告与源代码内容完整、格式规范。
\end{enumerate}
