From 4febba7f19740e2b748f011ee814033659f33bbe Mon Sep 17 00:00:00 2001 From: xphi Date: Tue, 23 Feb 2021 18:23:15 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=AC=AC=E5=9B=9B=E5=8D=95?= =?UTF-8?q?=E5=85=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- book/instructions.cls | 2 +- book/instructions.tex | 25 ++ data/appendix/fast.tex | 30 ++ data/appendix/openbox.tex | 29 ++ data/ch_ensp/preface.tex | 2 +- data/ch_router-openbox/preface.tex | 18 ++ data/ch_router-openbox/sec_networking.tex | 274 +++++++++++++++++ data/ch_router-openbox/sec_router.tex | 283 ++++++++++++++++++ data/ch_router-openbox/sec_switch.tex | 279 +++++++++++++++++ data/ch_sdn-openbox/preface.tex | 11 + data/ch_sdn-openbox/sec_ns.tex | 28 ++ data/ch_sdn-openbox/sec_sdn.tex | 28 ++ data/ch_sdn-openbox/sec_topo.tex | 28 ++ data/ch_socket/sec_smtp.tex | 18 +- data/ch_socket/sec_udp.tex | 2 +- data/preface.tex | 4 +- .../chapters/router-openbox/switch-topo.png | Bin 0 -> 23421 bytes 17 files changed, 1048 insertions(+), 13 deletions(-) create mode 100644 data/appendix/fast.tex create mode 100644 data/appendix/openbox.tex create mode 100644 data/ch_router-openbox/preface.tex create mode 100644 data/ch_router-openbox/sec_networking.tex create mode 100644 data/ch_router-openbox/sec_router.tex create mode 100644 data/ch_router-openbox/sec_switch.tex create mode 100644 data/ch_sdn-openbox/preface.tex create mode 100644 data/ch_sdn-openbox/sec_ns.tex create mode 100644 data/ch_sdn-openbox/sec_sdn.tex create mode 100644 data/ch_sdn-openbox/sec_topo.tex create mode 100644 figure/chapters/router-openbox/switch-topo.png diff --git a/book/instructions.cls b/book/instructions.cls index aa142bc..ce7454d 100644 --- a/book/instructions.cls +++ b/book/instructions.cls @@ -402,7 +402,7 @@ %定制代码框 \newtcblisting{code}[1][]{ center, - width=0.9\linewidth, + width=0.95\linewidth, listing only, breakable=true, minted language=#1, diff --git a/book/instructions.tex b/book/instructions.tex index c39aa3e..cf4180a 100644 --- a/book/instructions.tex +++ b/book/instructions.tex @@ -48,24 +48,43 @@ \pagestyle{fancy} \setcounter{page}{1} +% “网络抓包与协议分析” \graphicspath{{../figure/chapters/wireshark/}} \input{../data/ch_wireshark/preface} \input{../data/ch_wireshark/sec_arp} \input{../data/ch_wireshark/sec_ip} \input{../data/ch_wireshark/sec_tcp} +% “基于套接字的网络程序设计” \graphicspath{{../figure/chapters/socket/}} \input{../data/ch_socket/preface} \input{../data/ch_socket/sec_udp} \input{../data/ch_socket/sec_web} \input{../data/ch_socket/sec_smtp} + +% “组网基础” \graphicspath{{../figure/chapters/ensp/}} \input{../data/ch_ensp/preface} \input{../data/ch_ensp/sec_static} \input{../data/ch_ensp/sec_rip} \input{../data/ch_ensp/sec_ospf} +% “路由器实现” +\graphicspath{{../figure/chapters/router-openbox/}} +\input{../data/ch_router-openbox/preface} +\input{../data/ch_router-openbox/sec_switch} +\input{../data/ch_router-openbox/sec_router} +\input{../data/ch_router-openbox/sec_networking} + +% “软件定义网络与网络测量” +\graphicspath{{../figure/chapters/sdn-openbox/}} +\input{../data/ch_sdn-openbox/preface} +\input{../data/ch_sdn-openbox/sec_sdn} +\input{../data/ch_sdn-openbox/sec_topo} +\input{../data/ch_sdn-openbox/sec_ns} + + %========================================================================= % 附录 \appendix @@ -85,6 +104,12 @@ \graphicspath{{../figure/appendixes/ensp/}} \input{../data/appendix/ensp} +\graphicspath{{../figure/appendixes/openbox/}} +\input{../data/appendix/openbox} + +\graphicspath{{../figure/appendixes/fast/}} +\input{../data/appendix/fast} + \backmatter %====================================================================== % 参考文献 diff --git a/data/appendix/fast.tex b/data/appendix/fast.tex new file mode 100644 index 0000000..45d7641 --- /dev/null +++ b/data/appendix/fast.tex @@ -0,0 +1,30 @@ +%# -*- coding: utf-8-unix -*- + +\chapter{FAST软件编程入门指南} +\label{app:fast} + +\subsection{前言} + +本文档的主要阅读对象是首次使用FAST平台进行软件开发的工作人员。 +文档主要描述了FAST架构软件的主要功能、如何获取软件代码、 +介绍软件架构与对应代码结构、编译运行FAST代码。 +详细介绍了FAST开发的主要使用函数, +描述了用户如何使用开发函数进行自己代码的开发流程, +最后提供一个完整的开发示例说明,供初学者学习使用。 + +\subsection{FAST能做什么} + +FAST平台的主要功能是软硬件配合的网络分组处理功能, +硬件的主要作用是解析报文,对报文进行查表匹配, +进行指定的动作处理,然后分发报文。 +报文分发可以直接转发到硬件端口,也可以送到CPU端的UA进程再处理, +UA处理完成后可以再通过硬件转发从端口输出。 +软件的主要功能有两点: +一是对硬件的流表与对应动作进行配置,指定报文从硬件端口转发或送到CPU端的UA; +二是处理硬件无法处理的报文,处理完成后再转发到硬件输出。 + +FAST软件的编程主要包括UA编程、规则管理编程和寄存器访问控制编程。 +UA编程主要处理硬件送到指定软件的分组数据; +规则管理编程主要是对硬件流表的读写管理; +寄存器访问控制编程主要是对硬件各寄存器资源的访问控制。 +对硬件规则的读写与对硬件其他寄存器的读写需要了解FAST架构的虚拟地址空间定义。 \ No newline at end of file diff --git a/data/appendix/openbox.tex b/data/appendix/openbox.tex new file mode 100644 index 0000000..be601e1 --- /dev/null +++ b/data/appendix/openbox.tex @@ -0,0 +1,29 @@ +%# -*- coding: utf-8-unix -*- + +\chapter{可编程网络平台-操作手册} +\label{app:openbox} + +\subsection{FAST简介} + +针对近年来关于工业控制网络创新技术、创新网络结构、 +时间敏感网络、SDN网络、FPGA硬件加速技术及下一代网络技术的研究需求, +由国防科大与湖南新实共同发起的开源项目 +——FAST(FPGA Accelerated Switcing plaTform)于2016年创建。 + +FAST平台的架构是一种通用多核CPU加FPGA形态的网络处理架构, +该平台架构主要由硬件逻辑和软件逻辑两部分组成, +实现软硬件耦合通信与支持软硬件可编程的网络创新实验系统。 +其中硬件包括平台相关的数据IO和FAST硬件逻辑。 +软件部分包括通用操作系统与FAST软件逻辑。 +FAST软件逻辑包括FAST软件开发环境和FAST UA程序。 +在FAST平台,不仅支持FAST的UA编程开发,同时也支持通用APP的运行。 + +\subsection{设备简介} + +OpenBox-S4是一款软硬件全可编程网络实验平台, +基于多核CPU加FPGA异构模型的FAST处理架构实现, +软件在CPU上使用C语言编程,硬件在FPGA芯片上使用Verilog语言编程。 +全系统根据FAST架构搭建其软硬件基础运行环境, +内部软硬件用户逻辑支持所有FAST架构开发的应用、 +标准的网络协议栈与通用网络应用。 +平台软硬件逻辑代码开源免费,支持用户二次开发。 diff --git a/data/ch_ensp/preface.tex b/data/ch_ensp/preface.tex index dd6ae7d..1ae90af 100644 --- a/data/ch_ensp/preface.tex +++ b/data/ch_ensp/preface.tex @@ -6,6 +6,6 @@ 掌握静态路由、动态路由RIP和OSPF协议的配置,并理解分析静态与动态路由的区别, 两种动态路由协议各自的适用环境,理解网络收敛的概念。 推荐使用华为ENSP模拟器, -在附录\ref{app:ENSP}中有该软件的安装、操作和使用, +在附录\ref{app:ENSP}:《华为网络仿真平台 ENSP 简介》中有该软件的安装、操作和使用, 以熟悉华为模拟设备的基本配置及其配置命令, 掌握软件安装常见问题的解决方法。 diff --git a/data/ch_router-openbox/preface.tex b/data/ch_router-openbox/preface.tex new file mode 100644 index 0000000..853cb0f --- /dev/null +++ b/data/ch_router-openbox/preface.tex @@ -0,0 +1,18 @@ +%# -*- coding: utf-8-unix -*- + +\chapter{路由器实现} +\label{chap:router-ob} + +本单元实验使学生能初步使用可编程网络设备开发平台进行路由器基本功能的设计与实现, +通过程序来实现路由器中最基本的三个功能: +包括二层交换功能、三层路由功能及组网功能。 +深入理解路由器的基本工作原理与分组处理流程, +从而培养学生对网络设备使用和研发的能力。 + +可编程网络设备开发平台是一款基于FAST架构的软硬件 +(软件在CPU上使用C语言编程,硬件在FPGA芯片上使用Verilog语言编程) +全可编程的网络实验教学平台,平台的软硬件逻辑代码开源免费,支持用户二次开发。 +其基本使用方法与编程开发方法见附录\ref{app:openbox}:《可编程网络平台-操作手册》 +和附录\ref{app:fast}:《FAST编程-入门指南》。 +可编程网络设备开发平台在后文简称为“可编程网络平台”,图标LOGO为FAST,节点名称为OpenBox-S4。 + diff --git a/data/ch_router-openbox/sec_networking.tex b/data/ch_router-openbox/sec_networking.tex new file mode 100644 index 0000000..3ed9b55 --- /dev/null +++ b/data/ch_router-openbox/sec_networking.tex @@ -0,0 +1,274 @@ +%# -*- coding: utf-8-unix -*- + +\section{三层路由器组网} +\label{sec:c:router-ob:s:networking} + +\subsection{实验目的} +\label{subsec:c:router-ob:s:networking_object} + +本实验的主要目的是让学生将自己开发的路由器进行组网实验, +引入路由协议的配置与学习,实现多网段主机间的相互通信。 +熟悉Quagga软件的安装与使用和两种基本路由协议(RIP与OSPF)的配置与使用。 + +\subsection{实验内容} +\label{subsec:c:router-ob:s:networking_content} + +使用学生开发的路由器平台,搭建至少跨两跳路由器的复杂路由组网环境, +验证多网段主机的相互连通性。主要完成以下内容: + +\begin{enumerate} + \item \textbf{搭建自研路由器环境:}使用学生自己研发的路由器组建网络, + 搭建不少于3台路由器和2台测试主机的网络环境进行验证; + \item \textbf{安装并配置路由协议学习环境:}运行Quagga环境, + 分配配置RIP路由协议与OSPF路由协议,并将两种协议启动运行; + \item \textbf{验证组网功能:}验证不同路由协议下组网运行功能, + 验证不同网段内主机的相互通信功能 + \item \textbf{观察分析路由器工作流程:}观察各路由节点学习非互联网段的路由转发表, + 打印输出各路由节点的FIB表 + \item \textbf{观察分析内核FIB的变化:}在网络正常运行状态下, + 断开中间某个路由器间的链路,观察断开链路两端路由器的内核FIB表变化情况, + 同时验证路由器内FIB表同步情况 +\end{enumerate} + +\subsection{实验原理、方法和手段} +\label{subsec:c:router-ob:s:networking_principle} + +\subsubsection{路由协议及基本工作原理} + +路由协议工作在路由器的控制平面,创建了路由表,描述了网络拓扑结构。 +通过在路由器之间共享路由信息来相互学习总个网络可达区域内可到达的路由网段信息。 +路由信息在相邻路由器之间传递,确保所有路由器知道到其它路由器的路径。 + +路由协议主要包括:RIP、OSPF、IS-IS和BGP等。 +RIP、OSPF、IS-IS是内部网关协议(IGP), +适用于单个ISP的统一路由协议的运行, +一般由一个ISP运营的网络位于一个AS(自治系统)内, +有统一的AS number(自治系统号)。 +BGP是自治系统间的路由协议,是一种外部网关协议, +多用于不同ISP之间交换路由信息,以及大型企业、政府等具有较大规模的私有网络。 + +\subsubsection{路由组网环境} + +本实验建议采用学生自研的路由器进行组网测试, +组网测试通过的组可以在中间穿插商用路由器一起验证。 + +\subsection{实验条件} +\label{subsec:c:router-ob:s:networking_requirement} + +\begin{itemize} + \item 可编程网络平台两个,交换测试主机两台, + 网络配置与连接拓扑如图\ref{fig:c:router-ob_networking-topo}所示; + \item 串口线一根,网线三根; + \item 学生自研路由器程序。 +\end{itemize} + +\begin{figure}[!ht] + \centering + \includegraphics[width=8cm]{switch-topo} + \caption{三层路由组网实验拓扑图} + \label{fig:c:router-ob_networking-topo} +\end{figure} + +\subsection{实验步骤} +\label{subsec:c:router-ob:s:networking_procedure} + +\begin{enumerate} + \item 在每个路由器节点安装quagga软件,配置其正常工作。 + 路由协议选择RIP和OSPF两种,每次测试只启动一种协议即可; + \begin{itemize} + \item Quagga安装 + \begin{code}[console] + root@HNXS-FAST:/home/hnxs# apt-get install quagga + 正在读取软件包列表... 完成 + 正在分析软件包的依赖关系树 + 正在读取状态信息... 完成 + 建议安装: + snmpd + 下列【新】软件包将被安装: + quagga + 升级了 0 个软件包,新安装了 1 个软件包,要卸载 0 个软件包,有 1 个软件包未被升级。 + 有 1 个软件包没有被完全安装或卸载。 + 需要下载 1,154 kB 的归档。 + 解压缩后会消耗 4,605 kB 的额外空间。 + 获取:1 https://mirrors.ustc.edu.cn/ubuntu-ports xenial-updates/main armhf quagga armhf 0.99.24.1-2ubuntu1.4 [1,154 kB] + 已下载 1,154 kB,耗时 1秒 (997 kB/s) + 正在预设定软件包 ... + 正在选中未选择的软件包 quagga。 + (正在读取数据库 ... 系统当前共安装有 21814 个文件和目录。) + 正准备解包 .../quagga_0.99.24.1-2ubuntu1.4_armhf.deb ... + 正在解包 quagga (0.99.24.1-2ubuntu1.4) ... + 正在处理用于 libc-bin (2.23-0ubuntu3) 的触发器 ... + 正在处理用于 ureadahead (0.100.0-19) 的触发器 ... + 正在处理用于 systemd (229-4ubuntu4) 的触发器 ... + 正在设置 quagga (0.99.24.1-2ubuntu1.4) ... + 正在处理用于 libc-bin (2.23-0ubuntu3) 的触发器 ... + 正在处理用于 ureadahead (0.100.0-19) 的触发器 ... + 正在处理用于 systemd (229-4ubuntu4) 的触发器 ... + \end{code} + + \item Quagga启动配置 + \begin{code}[console] + root@HNXS-FAST:/home/hnxs# vim /etc/quagga/daemons + zebra=yes + bgpd=no + ospfd= yes + ospf6d=no + ripd= yes + ripngd=no + isisd=no + babeld=no + \end{code} + + 将上述文件内容中的3个no修改为yes,表示启动该功能模块。 + + \item RIP协议启动配置 + \begin{code}[console] + root@HNXS:/home/hnxs# vim /etc/quagga/ripd.conf + \end{code} + + ripd.conf配置文件示例如下: + + \begin{code}[text] + ! -*- rip -*- + ! + ! RIPd sample configuration file + ! + ! $Id: ripd.conf.sample,v 1.1 2002/12/13 20:15:30 paul Exp $ + ! + hostname ripd + password zebra + ! + ! debug rip events + ! debug rip packet + ! + router rip + ! network 11.0.0.0/8 + ! network eth0 + ! route 10.0.0.0/8 + ! distribute-list private-only in eth0 + network obx0 + network obx1 + network obx2 + network obx3 + neighbor 192.168.3.1 + ! + !access-list private-only permit 10.0.0.0/8 + !access-list private-only deny any + ! + !log file /var/log/quagga/ripd.log + ! + log stdout + \end{code} + 上述文件是S4-A路由器的rip配置文件,另一台配置文件与上述基本相同, + 只是其neighbor的地址是192.168.3.2。 + 这两台路由器是通过S4-A的一个端口(IP为192.168.3.1, + 故其neighbor为192.168.3.2)和S4-B的一个端口(IP为192.168.3.2)连接起来的。 + + \item OSPF协议启动配置 + \begin{code}[console] + root@HNXS:/home/hnxs# vim /etc/quagga/ospfd.conf + \end{code} + + ospfd.conf配置文件示例如下: + + \begin{code}[text] + ! -*- ospf -*- + ! + ! OSPFd sample configuration file + ! + ! + hostname ospfd + password zebra + !enable password please-set-at-here + ! + !router ospf + ! network 192.168.1.0/24 area 0 + ! + ospf router-id 192.168.3.1 + network 192.168.0.0/24 area 0 + network 192.168.1.0/24 area 0 + network 192.168.2.0/24 area 0 + network 192.168.3.0/24 area 0 + log stdout + \end{code} + + 上述为S4-A路由器的配置文件,另一台路由器配置文件为: + \begin{code}[text] + ! -*- ospf -*- + ! + ! OSPFd sample configuration file + ! + ! + hostname ospfd + password zebra + !enable password please-set-at-here + ! + !router ospf + ! network 192.168.1.0/24 area 0 + ! + ospf router-id 192.168.3.2 + network 192.168.4.0/24 area 0 + network 192.168.5.0/24 area 0 + network 192.168.6.0/24 area 0 + network 192.168.3.0/24 area 0 + log stdout + \end{code} + \end{itemize} + + \item 启动路由协议 + \begin{code}[console] + root@HNXS:/home/hnxs# systemctl start quagga + \end{code} + + \item 将自研路由器和测试主机组网,路由节点不少于2个,连线可以组成环状; + \item 运行自研路由器,并在两个测试主机上分别ping对方主机IP地址; + 请参考实验\ref{sec:c:router-ob:s:router}内容。 + \item 观察某个节点路由器分组处理流程, + 从打印消息上观察控制平面与数据平面的分组处理情况; + \item 观察每个节点上内核FIB表内容; + + \begin{code}[console] + root@HNXS-FAST:/mnt/openbox# route -n + Kernel IP routing table + Destination Gateway Genmask Flags Metric Ref Use Iface + 192.168.0.0 0.0.0.0 255.255.255.0 U 0 0 0 obx0 + 192.168.1.0 0.0.0.0 255.255.255.0 U 0 0 0 obx1 + 192.168.2.0 0.0.0.0 255.255.255.0 U 0 0 0 obx2 + 192.168.3.0 0.0.0.0 255.255.255.0 U 0 0 0 obx3 + 192.168.4.0 192.168.3.2 255.255.255.0 UG 2 0 0 obx3 + 192.168.5.0 192.168.3.2 255.255.255.0 UG 2 0 0 obx3 + 192.168.6.0 192.168.3.2 255.255.255.0 UG 2 0 0 obx3 + \end{code} + 观察新增加的表项内容,最后三条是非本地路由可直达的网段, + 其均需要通过192.168.3.2网送才可达。 + \item 观察路由器FIB表的更新情况,判断是否检测到内核FIB变化,是否已经同步更新; +\end{enumerate} + +\subsection{思考题} +\label{subsec:c:router-ob:s:networking_rethink} + +\begin{enumerate} + \item 路由协议的邻接信息学习后如何去重?如何收敛? + \item 默认网关的作用是什么? +\end{enumerate} + +\subsection{注意事项及有关说明} +\label{subsec:c:router-ob:s:networking_notice} + +\begin{enumerate} + \item RIP协议与OSPF协议一定要根据网络连接接口及相应IP地址配置网段信息, + 修改文件时注意不要使用中文符号; + \item 每次只启动一个协议,以更好验证其效果。 +\end{enumerate} + +\subsection{考核方法} +\label{subsec:c:router-ob:s:networking_criterion} + +完成本次实验,需要提交一份实验报告和一分程序输出日志。 +\begin{enumerate} + \item (20分)在规定时间内完成实验,并提交实验成果; + \item (30分)实验报告内容详细,有关键步骤截图与分析说明; + \item (20分)所有节点正常运行,不同网段的主机可以相互ping通; + \item (20分)Quagga安装配置正确,RIP和OSPF配置正确,工作正常; + \item (10分)实验报告与源代码内容完整、格式整洁。 +\end{enumerate} diff --git a/data/ch_router-openbox/sec_router.tex b/data/ch_router-openbox/sec_router.tex new file mode 100644 index 0000000..2deeef2 --- /dev/null +++ b/data/ch_router-openbox/sec_router.tex @@ -0,0 +1,283 @@ +%# -*- 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{系统路由验证} + +请参考附件《可编程网络平台-操作手册》完成。 +\subsubsection{三层路由器工作原理} +三层路由器工作于OSI模型的第3层(网络层),根据网络层中的目的IP地址进行查表, +获得分组转发的下一跳输出端口及下一跳的IP地址。更新网络层的TTL值与CSUM值, +并在输出端口对二层数据进行重新封装二层数据后转发。 +路由器还具备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值与CSUM值,根据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]{switch-topo} + \caption{三层路由器实验拓扑图} + \label{fig:c:router-ob_router-topo} +\end{figure} + +\subsection{实验步骤} +\label{subsec:c:router-ob:s:router_procedure} + +\subsubsection{系统路由验证} +1. 登录可编程网络平台,启动系统自带三层路由器命令, +系统输出如下信息说明已正常启动且进入工作模式; + +\begin{code}[console] + root@HNXS-FAST:/mnt/openbox# l3router + 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 + 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 + Create update_fib4_thread OK! + 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... + 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} + +\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} diff --git a/data/ch_router-openbox/sec_switch.tex b/data/ch_router-openbox/sec_switch.tex new file mode 100644 index 0000000..4e9a3cf --- /dev/null +++ b/data/ch_router-openbox/sec_switch.tex @@ -0,0 +1,279 @@ +%# -*- 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}:《可编程网络平台-操作手册》完成。 + +\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 参考附件《可编程网络平台-操作手册》搭建可编程网络平台的运行环境; + \item 在串口登录界面输入如下命令,启动系统自带二层交换机命令。 + 系统输出如下信息说明已正常启动且进入工作模式; + \begin{code}[console] + 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...... + 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] + /** + * @brief + * + * @param inport + * @param src_mac + */ + /*地址学习过程,将报文的源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}[console] + 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}[console] + 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函数打印调试; + 重返步骤3,仔细阅读二层交换框架代码、交换机的工作原理及分组处理流程; + + \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} diff --git a/data/ch_sdn-openbox/preface.tex b/data/ch_sdn-openbox/preface.tex new file mode 100644 index 0000000..a923b63 --- /dev/null +++ b/data/ch_sdn-openbox/preface.tex @@ -0,0 +1,11 @@ +%# -*- coding: utf-8-unix -*- + +\chapter{软件定义网络与网络测量} +\label{chap:sdn-ob} + +本单元实验使学生能初步使用SDN平台进行网络功能的设计与实现, +通过实验来掌握OpenFlow协议的基本功能与应用, +以及网络拓扑构建的基本实现。初步掌握网络测试的常用原理及方法, +了解在测试过程中影响测量精度的原因及如何提高测量精度。 +能通过实验理解软硬件协同测量的方法与特点。 + diff --git a/data/ch_sdn-openbox/sec_ns.tex b/data/ch_sdn-openbox/sec_ns.tex new file mode 100644 index 0000000..3c44e3a --- /dev/null +++ b/data/ch_sdn-openbox/sec_ns.tex @@ -0,0 +1,28 @@ +%# -*- coding: utf-8-unix -*- + +\section{纳秒级高精度硬件测量} +\label{sec:c:sdn-ob:s:ns} + +\subsection{实验目的} +\label{subsec:c:sdn-ob:s:ns_object} + +\subsection{实验内容} +\label{subsec:c:sdn-ob:s:ns_content} + +\subsection{实验原理、方法和手段} +\label{subsec:c:sdn-ob:s:ns_principle} + +\subsection{实验条件} +\label{subsec:c:sdn-ob:s:ns_requirement} + +\subsection{实验步骤} +\label{subsec:c:sdn-ob:s:ns_procedure} + +\subsection{思考题} +\label{subsec:c:sdn-ob:s:ns_rethink} + +\subsection{注意事项及有关说明} +\label{subsec:c:sdn-ob:s:ns_notice} + +\subsection{考核方法} +\label{subsec:c:sdn-ob:s:ns_criterion} diff --git a/data/ch_sdn-openbox/sec_sdn.tex b/data/ch_sdn-openbox/sec_sdn.tex new file mode 100644 index 0000000..adab807 --- /dev/null +++ b/data/ch_sdn-openbox/sec_sdn.tex @@ -0,0 +1,28 @@ +%# -*- coding: utf-8-unix -*- + +\section{基于OpenFlow的SDN交换机} +\label{sec:c:sdn-ob:s:sdn} + +\subsection{实验目的} +\label{subsec:c:sdn-ob:s:sdn_object} + +\subsection{实验内容} +\label{subsec:c:sdn-ob:s:sdn_content} + +\subsection{实验原理、方法和手段} +\label{subsec:c:sdn-ob:s:sdn_principle} + +\subsection{实验条件} +\label{subsec:c:sdn-ob:s:sdn_requirement} + +\subsection{实验步骤} +\label{subsec:c:sdn-ob:s:sdn_procedure} + +\subsection{思考题} +\label{subsec:c:sdn-ob:s:sdn_rethink} + +\subsection{注意事项及有关说明} +\label{subsec:c:sdn-ob:s:sdn_notice} + +\subsection{考核方法} +\label{subsec:c:sdn-ob:s:sdn_criterion} diff --git a/data/ch_sdn-openbox/sec_topo.tex b/data/ch_sdn-openbox/sec_topo.tex new file mode 100644 index 0000000..c61cacc --- /dev/null +++ b/data/ch_sdn-openbox/sec_topo.tex @@ -0,0 +1,28 @@ +%# -*- coding: utf-8-unix -*- + +\section{基于OpenFlow的拓扑测量} +\label{sec:c:sdn-ob:s:topo} + +\subsection{实验目的} +\label{subsec:c:sdn-ob:s:topo_object} + +\subsection{实验内容} +\label{subsec:c:sdn-ob:s:topo_content} + +\subsection{实验原理、方法和手段} +\label{subsec:c:sdn-ob:s:topo_principle} + +\subsection{实验条件} +\label{subsec:c:sdn-ob:s:topo_requirement} + +\subsection{实验步骤} +\label{subsec:c:sdn-ob:s:topo_procedure} + +\subsection{思考题} +\label{subsec:c:sdn-ob:s:topo_rethink} + +\subsection{注意事项及有关说明} +\label{subsec:c:sdn-ob:s:topo_notice} + +\subsection{考核方法} +\label{subsec:c:sdn-ob:s:topo_criterion} diff --git a/data/ch_socket/sec_smtp.tex b/data/ch_socket/sec_smtp.tex index c2d3bfc..acfe608 100644 --- a/data/ch_socket/sec_smtp.tex +++ b/data/ch_socket/sec_smtp.tex @@ -1,23 +1,23 @@ %# -*- coding: utf-8-unix -*- \section{SMTP客户端实现} -\label{sec:c:socket_s2} +\label{sec:c:socket:s:smtp} \subsection{实验目的} -\label{subsec:c:socket_s3_object} +\label{subsec:c:socket:s:smtp_object} 进一步理解和掌握Python中TCP套接字编程的基础知识, 理解SMTP报文格式,了解开发一个简单应用程序的流程。 \subsection{实验内容} -\label{subsec:c:socket_s3_content} +\label{subsec:c:socket:s:smtp_content} 创建一个可以向任何接收方发送电子邮件的简单邮件客户端。 通过Python编写代码与邮件服务器创建一个TCP连接, 使用SMTP协议与邮件服务器交谈并发送邮件报文,最后关闭连接。 \subsection{实验原理、方法和手段} -\label{subsec:c:socket_s3_principle} +\label{subsec:c:socket:s:smtp_principle} SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议, 是因特网电子邮件中主要的应用层协议,它使用TCP可靠数据传输服务, @@ -68,7 +68,7 @@ RFC5321给出了SMTP的定义。SMTP有众多出色的性质, 用一个独立的句点指示该邮件的结束,并且仅当所有邮件发送完后才发送QUIT。 \subsection{实验条件} -\label{subsec:c:socket_s3_requirement} +\label{subsec:c:socket:s:smtp_requirement} \begin{itemize} \item 下列装有python环境的电脑一台; @@ -77,7 +77,7 @@ RFC5321给出了SMTP的定义。SMTP有众多出色的性质, \end{itemize} \subsection{实验步骤} -\label{subsec:c:socket_s3_procedure} +\label{subsec:c:socket:s:smtp_procedure} 通过Python开发一个简单的SMTP客户端发送邮件。 补充完善下面的代码并通过向不同的账号发送电子邮件测试程序。 @@ -111,16 +111,16 @@ RFC5321给出了SMTP的定义。SMTP有众多出色的性质, \end{code} \subsection{思考题} -\label{subsec:c:socket_s3_rethink} +\label{subsec:c:socket:s:smtp_rethink} 修改代码使程序发送的邮件中不仅包含文本还能够包含图片。 \subsection{注意事项及有关说明} -\label{subsec:c:socket_s3_notice} +\label{subsec:c:socket:s:smtp_notice} 注意部分邮件服务器默认关闭SMTP,需进入设置手动开启SMTP协议。 \subsection{考核方法} -\label{subsec:c:socket_s3_criterion} +\label{subsec:c:socket:s:smtp_criterion} 同实验\ref{sec:c:socket:s:udp}。 diff --git a/data/ch_socket/sec_udp.tex b/data/ch_socket/sec_udp.tex index aac2375..1b0e3f0 100644 --- a/data/ch_socket/sec_udp.tex +++ b/data/ch_socket/sec_udp.tex @@ -44,7 +44,7 @@ UDP报文没有可靠性保证、顺序保证和流量控制字段等,可靠 一旦数据传输结束,服务器和客户通过调用close( )来关闭套接字。 具体流程如图\ref{fig:c:socket_udp-flow}所示。 -\begin{figure}[!htp] +\begin{figure}[!ht] \centering \includegraphics[width=8cm]{udp-flow} \caption{无连接客户/服务器流程图} diff --git a/data/preface.tex b/data/preface.tex index 988fbe7..e25cf20 100644 --- a/data/preface.tex +++ b/data/preface.tex @@ -22,8 +22,10 @@ \begin{itemize} \item 第一单元:谢怡、陈建发、洪劼超、雷蕴奇,厦门大学 - \item 第二单元:吴荻,国防科技大学 + \item 第二单元:吴荻、周丽涛,国防科技大学 \item 第三单元:张晓丽,昆明理工大学 + \item 第四单元:胡罡、徐东来、徐明、夏竟,国防科技大学 + \item 第五单元:胡罡、徐东来、蔡志平、徐明、夏竟,国防科技大学 \item 统筹规划:徐明、夏竟,国防科技大学 \end{itemize} diff --git a/figure/chapters/router-openbox/switch-topo.png b/figure/chapters/router-openbox/switch-topo.png new file mode 100644 index 0000000000000000000000000000000000000000..5e9a6ad677fbf96189911eea5ef9211928709a50 GIT binary patch literal 23421 zcmZ^LWmHvL*zO_(q*1y>3F(q<2?az-Kw7sb-6bVTr-u>|kP-oDk#3L@Dd`632I;;p z-#6~KzwQ}F7;ZOf&$Z^9&-=tX2~k&5BEY4_MG%DG;RAV1_h&p8VSV=99y=`k1ee4&|P{h*3D8h|4r1@t+|5 zPk}x7JI*f_M)t()!gtvUKgn8jQ-gy!q_ySMv){cFl#xwMh<$Qk%P1beV$;thS}z@a zy-AUpJ0&evf_9C=|LCv%O|8H9KXEinmHUkb_6!Aro4C0fmz~&Lng5@-5~lB|t>(0KB7I1%aL(gyk8wL3Unj!v zN*p_4v}&b_2=Ssl=Ov4aqPVE*<)c;1JkcMtjw91gt`E;>GvV6N*kkXqA{ouaS-CS} zbR4ouPC@6Uqh$=fRB`qMgiB=&e8U*N@6obtz6+bR1z{YKHI$T{V?X0+okoQjw7E|3 zwfXcN7$6OpGu-1BWJn9Yc%-N4mKjA`k60ylTPIts4U&av()h^=4_R!FJiNwBDNK9s zx{M8CW8=_qfH(Tu2F>fbDJv4U@sVnXwyeyC2KkDn$5cIJKq!_vYXcRcoIcUMKs9;1)Z}K#S+(23GyZ*rJFwCxnge9`3gR*;Y761=0^_1 z8&?g9(6e8S7Z5St9O`@KhgO4Fskq-9cTsE@`wt7zLiXO3nyVpGT+Q8Ig=ml&Dugy=L?;CQi0M%4Os4%ykq>Sa{7+Zv^Q0@;&95dFYP`y zCN_3_a7tWU!2JA+vz;~uk=OrTT&E&&8Ljj7{Fh&xq%~;YRuIVBI)%0U=?4P^MNoG3 zzn*4BrkedBgWeVAKXqoO2OG*qig*%BEHjZtUFx}mFJ*~Ik?oE0X(oRD@0xootgNW1 zn%949^K^0_*;AyzEF-c^w(N| zx3N4>J5SUJGet}=vg=<9(0gq+I<8RNvm`|qvX;3o>@eniA*xeoOn~s1mySJVH|Gy( zcECpcZlcVXm@piJg?3IrjHZpL-t$@VuCQ>q;Vmw%h_9p0B6yXRl|L*8*`J4#psZhw zk~?}4d`6v$SeANxV#GW2`uZp_a9}_i6BE<_;&>^AEV;UC)_$J1)m5OCjfbblqP?RdjrE174PFCxwc`S= zBvvo&DBo*ybMaTVWh6cJQnSoF=6AYNq#tYE^VRa+4xqYEJ1o)r<>AeExd6Ghaj##S zzZjRkK9eUh*mxcC1*L z(~{a}D_sHi$c>Eog@qPj>k;C2jP9hGT3X3MAH(Uy$=@-izUuibVo*r`6@!xBofpQu zrQP%D_&77tWz(7==T}O+CHiHC7pW{Ui)xZcUq%J%HYE#t3}-lqq2a;pmYI*DQ#ATg}^GAw+@>eZi7n{H2Tn2x+;&2lZ63ogfQbZik5yME+xg+|C zV(*X1;%@2pg9tNe35tB2t@aP6QD!m-|FiR?#SuP~{cO`Ue}DhjP-%*WFCjNIw6*;^ zVpwibG>jvbW!51cXlS-{r*)^ByWIpc+LTv!XXG{+kaj=lUHksXe|Hfd>>|%`p*+JC zFMB;3#6dwpugsbXVU&`h$9COl>Om~gDsUOQrx`3PEHb+vsi`R{EC1?Z32+LVjog^~ zohVF%_MLW+>JY>IaMI0av4SEjOoeW3td!$99xiY1*Et&`DqsKEzxj>>k?}H{=H9gX z4wvVL;`CCj~O7=QZv?WWu#bph)hWgArs9c+vTwYNVY z6;HVDT>_AT%M}(@Y~IHdPA@rT;hm`EJ)^K=DQ|AhhKq}vRjOd z$fHk!jUTuS*q;b54K>qMQ#&62GouxA#$k!Mp08gj_u|E!8#ixCe!k9vgZ}%az~|SC zR7Y@sfZhla)8XIfM<*TN?N?J%V-P3b{irGI?;vHSW=L}okza6;&$)-7#UIR9-AQld z(tH&&WUE~_h^eST($kr~WM$pHcaQAN<+(6odUmv<_rsiooM+KV%vPxaHO(S%vYbK5 z{{kntot~2%FI@5V+4(sMDe1i6(&EHt`?k7>sK8W5?7H0<7i+>LA%Ah9ASaq8y78cn zo*p+23VD*Zva;K6E?#2}6`IJA|Ecp1IWJO-NIg4#g}J{)gy2Mm%d6Y-qMsZp$!tye z_UTo&I@RF1Su-f+I8S5BmGM}Ua~Xx6?JlXblM-U6;$}To+CjZ<{P7F}VdUY!Lrm$V z=>y)JKY7aH$Et@{r*j7<~$8;N`X)ss#@7^XsMx*KET~5w; zuHD71=HxZQ0V;FHtAPBuB&MfnS5Ux_lmc z=JSh-?}ERh85Vx|;_!`9>P`-I17!vp2A1hi-qVBGbVM1S!DS*I+hRT;_xdfz1g~nl z={f|-*TF;1k+atv{araC5^V)o#2ST<5`~@|^-GY+I<5xbQJ^=l*k={!Y|~bM>dnr+CIOie1_C7u_vrDTk$p65@@7Bgz{Gv_ z+TO-^`GlyekB^kG&#~E;uV2Spu(7b7!z{EQb=S7{^3`av9AU2#JPVH+wf0&48FDiW zc!7Zd_gY^D_Ga~#)DOF9dID;}BJr}07f=@|{!2;0hkwN^{5sd)3o!@0Ph6;*TLwk6r299T}JS3?loue zmnBrFw#a}zg3a0HAl67KftTk{Ajkz;ab!N~pM`_1^ z=2>mR1?Ms~tD!t9?3oPd%d_3bh%|DPnwo0L>&vh7A7ASy{?=c@2eHkwu|%yO`B+$5 z_v6vOArXotpOz+Q)yNttX?{|w9Q-NG${w`rqZwYbx3#KCw&HK)HW#EW6}Fj)C6bb9 zX?qr~ku2t-J@rF2cW~%76PG&=Zje(yPB>p^SlDhte4&26kU;C|>v2NDtg+`DKBtC+ zD;t(*Z312_!ZQ=Td{(QQ1i}HkG*A*R?6J7t$7_L>WD>*&rVJv z3#nBi;ffir{rOs@(>F4<(<_J?DaCK$H2F=HHQxkO6eiZVg|hZ9x0$u~B9Bn)*U#56 z*(;-42;fs}4kXy+5N8Q;3QFrUPyePuGC?&s>PncUY#P4*Dm>GDJTfsryxgvFZDi_I zj85*xvdwMV5AWW+Gd1Qsq^m%Ak{EDP%-e{w`_*4wjNid&3h#{PId#^h3)0HW9$XzB zwA>e-D$Dr(EZjvacTl%8vSW7Q<ydlt@&eEe%8{*5|chl4Ugv5vknekRfzkBZk+M#R@gKi{>o+@_LDkMo00 zc(Iq=c;l=#6_Mng`lNUo&L$4Zx>i5`BBgv|#-5#S{ClHrjZILwX}DxJrPD>eF=1Af zBkSToBn+}YoG+$)qe5%OWt}~c@1;5$X~flAuSmTU@`krnYNw2gw)@d+mzke8Su5o} z6t~F#xhg)-JMp0D_-{cJEx9%4D@0hb*xr3k%EUzcQ|Jv}ZN*%{$kR{PZe$TaC@anO~1bt`hbX+o8AWMUGnA z&e(|Cvi{KD@I=O1e*ScZyB?&P;?j~GuylFLh;vszzbp|RH(ha@|5wY>vCJplToH=9 zcS-iq5oziA**E9=!y(`5UOs7dTE}gmq+!Kra@`zw5-RP>9A3=M@IghsANhgZJ7q1z z@G3kach$<#3%^wVyh<_Scxc>p;x2D%LGIvc_Y*Gye(H#UN~c$2Mb9w!T7&r%F|Y}f z4C03KmE9waq%dWPr<7GyY3FS2Z<7Adw;_-vK6ny$0b72wzA5_S$Kano<()StsNOL$ z^6=PQ+a#+iH9IKTBaGCcGH+BsHGf zxaNU^f&!+mPQJbZD52O-pOlg1ls6Z}(pGWa8{fW+EsTI#l0aU(cwyS)f4xYLljMna zzgZ{N8=bh}*$Svx1?a?0lr-8;#i&sC5JnLZO6ANO4^Om+iIRUF?aVn08@b&Q7N+3i z<2#y+v#zwn)0>v*TSpuOoY8NQ@Vir#YSNiIOSh4zeRP|7QG% zhVn37Zf8dDp;%?o5K`RKQOU02dlu507E|b~YDo=arB)8W*#vFINa60*hVt=}Bwq8A zMD>)FYb&*3A^7wXl-?(MjWDwRQc~WZ?62L=)I@7o7#SG}VxOZ9FLpvU`z=d7+3z!C zX)*#Nu*Ln#`=u^q-ph#d&i7{9KhO&_9T$bO|aJU(3Y7jIlcNIb^i8cbbY<_In=D!#6%UhX>Z|`eGktl zi`9V~9_>B-aK8Jhs;bSuexzu@?qTERE@(=K+2bT3$v^TSpnFui&X0+8tuH~~6`Qnl zN+hkQ-2Uod#;X+X50t1MFFI~8rR&EflqF{BFO0k_Iqu{(s%n^?W~i#Ip2<=--D*_b z+YlyB6t-*KnQPktL5(`vo;9DYlO$x2I@`hHsY&J7auKBNYDUyxLN=z^A7Eug_ga?X z=IdA$v{zm!@stz>B(WpOnB-$q2Q4#l*-4U={eqWowT>MB`^<*Zlpb=E6&<;v7dY>%vOY8s zNnZvUd1?{U(&E2)^Jac&3H2-X?v;vh_&5L1T=0`VV5XGO;laasg)Hx&KP$aCD$Gsq z>#ftkEQs!S!4%UWkHCZQ{z;hu`om-3d)NPPu7 zQ{?M-N|SIEw&RV$d`2V+mTYy>^iBlqISp%MC=$FdGrPsgiVX-RZ){9&IruFACYS@* za`x?X9`<`_3#nrwuCT7$EIcQUvTotL>rd6p*?zoghR+~M4LhV6n9=rf#{0h!!cz!R zv(riw0}hVSH*Qi2iY#Kx*$7~C(%yJ6Ejf37=;xdAC;US_fBwmxc+yx~Cr8_TZBl{R z-;Asy)1K*^=|pOPAtWS3#C@9)h*1!bo}1vRG_(Pc2k4W2B%jM6p8r|hhIagUvDawU zV+PQINs=*W0Z935HG@yf#eLT8-vT|RFdKT+u3UhIseR_#VB`#c5_c=8HK!*dTPPf~ z?DC>@-&Z8|CnOJJuFZ3|EEVHsW5Yqz)zy8uCp}{S^ZaDRfr*w_zWI}f#;1+OuI4S! zwwcwzT)iSww9lZ0t{^8vF~Z|r@YJ_x%<8l;5h33pt*@$Yl{1@m1&j}tBgBp?SKOU{ ziu|ygBo8C!l1mh_*|F`_DWHpKtuo*)MgQ+ zr0~#kw6dCddWZyGbvrX1N(}3;Vb;(e3T>{9Tth=cLwuW>P@KxfuTdq8cYPYT7 zLX&zQ6{XxKiL{}7iZo!S>FH5MW}3XM|3icuFr$I~Z+Ew^!0i4<)gjMZ%^Z7B*QyEp z*PsRo2_0lSEqPh1U3zzBf*ou{30YzfsGgLs99N#=Ucdep_U*;PTKvoubY0khCqz|~_?t5giJ_XOt*2J(NW4s{j~ozqibF)y))Ge1SM=<@d8t09 zQ57jq?Vjd*7vZ;^Sj*WFEWc+CKq7D9pkJKq9%?$u-f|}ml-{}1ZOqRIsvQi#3fB$l zDX-l}HQVo8d_MtC?X(uEcgXE;%kcuX;e&L97a#wdgW7g5+nVv2`u)cR`toznV za&q?_mXGS;K8P{V04dxzN{4x0{-*2@a$=5fakFuI!d}RUDf^KZ)rz03&Hi(5#YB0N z6=wl=iHAm`+}6mj%K3UgKmetLJFj9mB^t6kS$$X&9v-foAo+8LyDa@^LdyF@{y`fj zd1t&I*3(x$n*+81avEiLu}|7u0~v8dKDl0FkHeMsXLxf)GCDeHK2gC30zXZgy#z>6 z&##9$HxqM}9n1hLX^u`$QB&0^vE;a)m&P6~i&j{BKpjaZ9!SomZ@Tlh1qG&C$=ruK(OQ{oq@iCC%`e{IhJX=Jb1@|M zzT554`n6oN`;9)LgCONhIanxB2?-Vqy3Yiu&wnM-Ki+=b#=_)Td=%2RIZV8_dsD(v zHKyLeJX0e^q#sv{3S22E5aU+hJ%t13f{*3)3yI!mfOOEJJY?kO#?g*;!s*enzIb9Jo3&@*j1#;(IR7q{8Dv8!|?becOm}AInF-2T`$pp zeVmK?A|4s0C_sHrGc7e0fi>UW-rjI=e#+;*Jrk3y=;tAli9^Tt98>Q8{hdW|HR@-Q zGQ{eO0`sBCWBE9Qgo>~}2cF0kRHiH!^&=#-#L;r z87~!xF@JlwT-jDWm*;MrdPNP!&Nmbso>bz`9^LW7(oNm7qA}7Txi~x51$u==pzPD^ zuszcl!TzNP-)pS+nymRfi)a^|L*ePz^240@yuyDsE;~Ho5dg0K#2}LJy~&z4Z4nj{ zVxenmMqAjQtIKmb@!M_cb(WkMr+g9d+w8OM^NVrF9(R}GDS7-}eSZCQ&vak8A&ZWt zC~{4QjWl?5qISKVa6%QO@=bOV z#ND|6Xg>a65Y*y$J9fCdq|gKd*<0zy1~)I7KWSP^i^DV+`=|zmA!BN48n7z=$5EJU zHZ$HBo#YuXAS7=tGzgyx9W;Kf9ImMG{fs_CGR(B$Pxq{F!A|2LvZ{OfZd&+ay*ToD zNv8JpKqXI-bP%|tC|IS4FSyrfP$iClK<;*?Q1@&HW5e z${`}IUs?O3try+79~C2oiym!;tb>sYj4?elI9LX#M%~FO2PPvC-S_~HR#1Q~X)~Q4 zYmNBUlbVAut1AFR5MU<2lc64t*=vP3`Spki`I3QkeGQQi^3o0a;^5ZcQLT=)gNML`ke3U{!SLB01^+Gi!9^_un4XUQ(zTy(uV^Mxt-5% z#W%*ZSp~V`k>Xza_wV0&8>Xxb2!UMdk?%a>t{Z%)Rz_al-l<{}?(r@udsw25n`EMi<)+i-O6@T$g|kph+rkXsytjxLU&s1aJyZk)Z30cNZ$qeW!lG z6eIuVpdLZ)-W{54nf5-U15)h|#*yyB``B6kKwsMtM-`|gi#>Soph#R0@ed3f^Lz+V z%zKE>GK{KSLFmE%%cJ`~Ngco0(%y4wzhny|>x_RF%xm_G^bRX42BK47SXt?)Wqc!r zCN*6rHy-aDZ{_RV#cYiXEN;UJ-%BnpnrF|R;o##ob$ct3(9h(=A*D3{a3Z*P-c}6rXWvn@bEH@ zj*b%lY!I6KD!vw>7{h|oG!In&Z_Hz5ZnAmX>T?ar_i6gVi7RCZ6&pa;cq`PltGt z$@>#Y9X0sm0UrWrZZ64W(q)pG9SJX11Yu5c@BC=){SI7aEKF4?lC}{B1fWBGwei{_ z=^KA7sBE8qD@4u?IBe&7-X5hyMdt2B0 z&rfLnC+_^=`DCw`)a^hTKB^_}`VzD>q551|qyl6> zBNUVhg?y@0Sb4gtqO5Lx1HXMUu{`MmtCKIq%-P_v6o8_^NA*z{%vU?*9-0~drAl1HdIXvA@uHSf^2j?^NAXbpDt=U501VOgPDNKK6dWaH!2@w8@twqL9;Gj=RY zYTUYY>nbf_dGyI2Y7wZ3FXx_Jw@-ufdUT^k-vySU_m)KkG7vd_?iaX>JjxoV54bG z4&cGK>mf!3pUkzQNU_`Lha7yzNw`gv0*DAH71hc>4s()d=?`hwb-k;Mwz091dH&x9 zB;NoG&*$5t+IxC}My(Y~0`F;93&kf}jZ;s!jtps!L;8uBf)o>R_=|~;<+sEkp-o&U zQkHM5?FqQ=J|CsDuEu4z{$G;*6fx5d+`<*TGVe`A!^OjkO7^BBIvh4N`DaXOz)6q& zit}v7h?s;$^`mbLtID1&vxwwdr_Lg>+Gas&0x*xvc;v*%3m0PE%=0T%4y3Xu`D{Ww zQfxmf2O@srbBMF;G_F>bs{N>pm0dP-Q%xn0UvO`siL@t)BnQMj-@KS74D?A~g?C{V z;S>?lf|Hp!m91HeUDUlC?e+v=4pZ?R+;MVrM35e}94)94jPr_#dMM1?J$Jqwe{i^5H0$P&J3!=mqE)MP~EXH zG51+wLSPA-|E>s$J=nVUzGVn+( z!}-J<=Y!98+u=v&!56BIC5bXfFr;T-*gBX1CZPpYy2F%L9X|R|5`S`Qc#2%mVv1nLGqVpgfNVG)J0~aS}6S*|jp$5ysqffm&)eNMJTLwq+x`>7K+uzA{j$P;yGe&5VtWa}qh+4BN`T8g8yftS%oFiDvbge_3C* z{-?#h@?vq2zjK*CEZz8z@$TbZTwLs}<`zc7 zD9_Hk&}G{C3uCDO9@xai6CqHx=!j+>D0%f5XxWfH+^#rXE2U9|{oBu;o0Xc~T%zfy zmop*16AZbX0u7~}|HRR+a=r^bCliy$jI5%=7AMnr@h+w>J{AU4*~mzGN#(LY>32lo zbUDPe9{ZLogIVwCCG}s{wkGG*>@!zdLu>>S3^ zYsoMqu$I&zjl15EGF6x5CKk^3tGoNF@U&M9zktA`SUHM&W4Y#Y(%Op3EpVhh@R+DU z1-R|kCn%)hOTz_5 zw*%y4vDFMYNwnX-g>kY83J&xYiyF)8^U69}!&{1ri=8*d?!8YM>w9QK7f(AvqRWG+ zv;^ovN=~kr^*H4#EY7PZ2&!kC&J#?XA1-S{HdA#eSDz0KJu{+~T%Fg7|LXfM5emf2 z%xtup9r^EKS=1ASZQXs@!J0BcBi=wa&bmBa&coNb9%&;;eOE#v2{Jd1`H4ubX-%(J z-CV;fYAgY^lU03HE^Ajy3+V-~4bq13aywQg_UUx=I@sT!m=$#k4Aa0kd18qQDiQuB zp5>NzbFk1P~B8>hjEKb+TF*T6DZtHcmT= znHD^vQ81tAGLRE+Cy$TrGP#+$wYa`=tWyp2hlx$#V!dDB>W(Vn7>gvR3 zdb9AEm1wANEPP5+H*46}{aKH*AgEV=_AIS`=2vA%dya&ZRNc?x&%bCF7KsBoYcb{28^8`}Qs+sz0D>CaO>r#7D657@;XMiQ(s zkmxHqikAOZQ6Umg$|;YVDLp^&DQ?y5M%K1hKX1W30rQUC@J00Po|VZC+J(sPuw+jF4YEO6 zxwb_{Mt;Sk;NCtdC2G#=4JNt09+?bgJ*=o7;N-J&bAN@7jjYB&CzkhfYKv^~&aF^K zKvE@iy}ZgfK$YBxB=EgB+wEaLZaC*sfEY9$i|m0*dJu!p$L?hDC7~r(*SSd-rFOG| z`R=lXrOy(tqgRLWRiJt5OaIC;%+pWG+p@&no~KfdN$^lpAolZysNS`$yQ?d+FGEQS zaG-NxB3jFX$?Z^+k*NyUA{38Nx>Ja8;9WgEZ$`!s^EILKPwC4C)tr%m3I~+O@j|>2 zd4l0!$i$!(ZO`GJMCHT_ehtuN(3BKu?6so+?N78_5H3`fZ{2FU2cGH&K1B%J;wOuD z|NUErVC9Q8dj~vS2Z!Q*M!TS>NRlX(N`Zt72}#Lh*R3fVw{^JE|3+9iGNI*Lx|vMO zs>Tc_@FXmNt}M9Pc977SZFa{DhZ)xLevxM3;_5PXR(|jx6_O;!wgU%Blr(SaGQ=mY zZ6A85{uBQqu=GK^l(uprK^6C9Ndiph3Mg$D#R?FX*bg5RAqHk`rF7YvN|x8Ssy4&9 zb~?J0C_$?s&T7=s{CqDU%p(q+yo$R^uGYWpV@0iS9AaUfU=quQgYLAED?~LlHCets zopSi~)oQo^e%2X0s~eFv{0VF#A|F9n83D8sf*5eDyd6COH?N~ZPABFZY&n$o5JYI~ z=g(>|L#!(a*fLPOz_dU3_=YRXZEGqY%>388FJTyqi+>>4vY2fO;9isaEa+Mh6E{fw zyjYKom9@>-+0ohBZdy2U37?Api!UGa#+=&ajQZSGiu|&a^5!#8dl+t(>_t*S*W!)w zNCmrx7hCn0(K@-A(50ja93=DWS50ecPQKP`NGI=vGI2fq{=#Q;NY{xHwFhiI)_ncH zP}P?uqxpU+YV7x3w9^glv{rkHKH!Qc4-0^@a+ixM-yfx7beBEOGOT=yBFpOsJcxk3 z%ZFNb7iMP~{SIDr8QBLKx-R~v5_4L_M@#+vTNP--m#JFMo2c;5?tibN%bXr=T_yy- z`^9^F0Ih-aIMKE>6IY9djh{an!jN?Zlv^W7j>tlu+Pq#0(|7U?0e*gXz9X9>(&u|I zlR1Z*lPb{bCk~L(a#tGAKY76N-a`eaV|H;(^FLp%!>Vk{m@x1txRcclAAOaJ*juht zK2%m_(jdJHl!V!CI>ryu z;prcjq6ix^b4yZ6%5=~8VW^hF6~ofp?@4f4+xIv{$0U@wc-0$@JzrmhMVmJKgvT{?V{S&E^l)iQKgsndLciG`i6pVd_^&)k+l1&Lgu=>i z-@dgq5TIL=cU&sdEKAUzuXRuAm%0*{ z{#1fQFb5#R^j+*uw%V!#1fa;j=c?t_U z?&GaRPH0BboNJA+fTiXPg6~F81lQT>g5a^lJLmo@0L<#X0(wxCd%rje-L(aKUK!_@^L5;#pk zts^8tDSNK^-w2>bqMWxi`*DgCcbGD47FOhk^(cRQ1RX%gRTqNgpD!V`B7+czfkJ^E zL$p+A%AJb`72_Xkc5~?fBIqE1p6 zykp}Q^GBGCU}FA)iAa3v_z%(;Emv2e+TCvP$Vx#p_rY;2T}bywo%E}U;ce_CQg^&9 zp1}(TQ7^z3WNxLL^gf&f1lpLYO?)%`Nh=ii%<)~)&?amKuinky;e3`;HKJ&#AQ_DI z4iDe|D@!iFSqMMh&osFl!}_?&X4%)KK~`Bg^t@-Z=$YTZ>g4oz$n22>9j0t5Hobep z)B|!Vs$+5AKhw(#6SVU4CXl^UXZ)gIiVXq?zKQ5VUI;J<-J)OA*+7`-S2`}NO{rmL zom`QwYJ?Qp_(I&?PN94F9L)x`MEgBX6w3dpZ+1mY08C+bwMNo$42EzbFWpCN34`X) z#v$hny@Ne$F6cbpPU7)5gcYb8fDf|<9x{V6=N2DAOX!HcIs7@$mhb=+Qkc1WngV zXsZ8h5EY*#ZUVDyqSa3^@N*y6vq zg~WFj)&I$HHMuxl$x8W+paVst`Tbea(+T$UPUX_)`yc|ro;_=ZcEh<^o7+nt`sV?O z3=TjP@r*z~vU+)OSl?Oj>(HM4lQ`-uK^M$`8e2U*y-zoj^P!(xN`tgz z7%``P(XuvNsEeqh9qE#`NWPakG5^LEioMcmdAC$vLzPT{k4$Ex(rM}9NytjAQ|h?G zF;yh1Jcf+AdT%hiTsHz!aXFCp)aix2{dVQ{wtW~|sP24}i`XR9=q8&6>9Ck6p$I4B z#GxPz%A;N1(~90e5H9^6M;$}MlCpoUSw+(2^sh+8{dI%hUq80cQ(Li8`o9sho+Ztt z3;VY$1V5IX5I4GbFQ+r&6nqeH)M4-#!Vni7#F)H1pT67>Oi*oMiRtK>o~{R1p_K%Y zkta7uaznz=dzr+VttmyyRGlzS0UYw-=l58?-Yw$jMI81oPkRiMP;1N{$?G>=Ox3Ysj^AMF%qbptYi;udgozT0=a^5P6Lx6OaPM zY)|K*51-7|tHh=SqPutuk-K3fl*q}!Mztns3m3>xFdA+$kNZq)E3h-x0&EC;fl{ zr-4C69J#oHF{|wDma8Z8rDhJ>_D!3SAk>P%)!XV$N+SQ{_cQA z20K7>T&)~!E>c{DXRs-XQFP+W%$XQ@mzN~S4>;|h+o^gJ0h_Uv2=0vYL&sFq>IgO= zt@)kMzqcw1QC9Y-<1YqH`Q62*7$b@C@rMgpDL!L*IohADxeNumRnP#Kqs#5SokNGZe>;b@0lJ_L-e{{ji` zLRYl=5f5%alXaNXqpbc~+_iD1d{qK8-;D@MSmH91%8vxIlq-P-qtHy@5Y2nwJy_`O z)QC3wlQhmYa)c6!d96m8I_bt}$r8i3gUW*k?R>13D)S?bFgG<^ZIRgPlKcYJJ=&)7 z@;|L7mYanzC^1J;3dcXxl`*;KK{K-+_*g9BVh1pC>J?|Phz-Su!l>eAQw-63w9^#E zLLy1b89EJ8<~ASI!r}UWKat5qjFkWux;Ff;-Uzn2C6JMl{ZP zm1Ft>13&K}-(sjWP7$=7sBpV4C-Ax;ucVD1Fmr$I8VxfXt^~4awHZ z>ZU}jOuawfp$*%y0i1S%J-mYiZjF`ZT0mC!Oeit)8t*j?Qt6wRv$w)p{)zf-LwC5i zrFDP;Y0DPc#5A;@8zWG>S_1d$GOR(|jN!HVf;(E9 zdS8Yby~UC@rI5#K&Q*&*;2d9l9}YrDk>FCga)dZ}IhLYDg_w@6R>_zlTIc6~tM`%L z>18Jo&?GDX!#DO${|jr-#?}Dd4WxkgHe=N@0&k*cNA7*-9a*pS9Fqjy-g3 z|KxhNK8M{QxoY=Dgs`Kv0J7{{6_k;lUWFhewQQl!lh{I;1JU(4Kd8xTk3iFr2h7hd znkjUaUf56B>7i?oE}Rdzz_J&US5O$0p1zIKlqIypm z1W8WXeWCR5t22z{Xqrz}#FV)$O~0kmBM(Y8Orq&HzSH$r_c>^kN$R}}JEMNlZ}Ne_ ztOHBTu;+Q$ZUzms3zxo?*N`BXsZz^QAyL@*!4{fzetyo?(a~|}ZhvtfaYX(zE__2f zL>~?Pc2hOSuangd;gBsdMXFGoubCq#p0LZaQjy)i;uF1&?hC=}>*ig^IwXCqd|i5< zxVu+$q{SW+=b;4d(9HXbL`4*@Kq>x9uBRLET934aO_takIDwPo1L2^#{s)s^hv~+& z7Pls=hcgVtMMaN|6Fw^-jHSEs^72XRaMWO9y1qV+Ez}O({RwG{ml7Q;pfvD%_2o5c zaMnoYTmJCoPT$B*RAzGmWFdsK0NH~ zmh`@EAWo+=6Z1b}JUT65*y2>hPQ}x^- zgFYG~7qvE1HN`YMOSJaLQ;Yoaj_Kc7$=^W9M&;`hs;hrb50PunFs-<*|Mf z5RDT_glbgo0<`zoK3SmChD}B2x2xnkmzry6?o>m?^seXSKdMzoTU^Ra^0}PRdmjU~ z?#wv9L($PrZG^l{$`Jha+1UA*A3~5UR zGNusxmA%Nz-p55q)Zn-#=TtJrF zutb7kld_TGLR{XN{?goh*&dOFGh-cPbmO>I7-M=&&&`tF;10c2D-7`Wmv92R^b>Nz zAmvqI2M1utpvdY~Ia|D@Ih>GnaB#3Aq!tWDqr6Muj=F*4d*l44y!;BA<vqVF3^eYhfm#yLov)RJI==%r=;X~xoj=DZbkqA$HKf75M z7tDymgNBfho&D?N3Gt|vDh8nznY&VP`-bmpIE*w6_D2*Utw?H#9_J`xc~irr*7z}f zxXlq&gFMoxp`npwnrxdlu(nT*6hi`U+62aFv=*R_8}unwqIWpY?^IM&l!H$P3S)kG z|Af;(tY&FMsI_sGk>2~0HnIPRQx^KRGJ?=hT>V~vrS6Tuu4lrw6Gw0a`b{2MFCP~d zK{}f)$P^h$Uq&YCtp1g=%zJs5vcb&hH#$cE+|Y)&F)*>P0=@SKb-SS7)0r6AfP^va zk#Q5&)8t=q-l}3cKzkOE3;P?BM#-UCYLM~HKdFjXaJEF+(e3X6X(sn&MRT?VPKLHT z^D}3IaNhUYkb-y_`Vn=5AG88^f=Lwwt>T!-5^S@!*7}Q+T#_LjcU@PJ{*CbaNZt2Q zMdTCG?)pY~p`=@flr_i~baZ@H_ivw$IsH2v*moBCaM`rkNF4}oYRwxuYm&x~vr5~2RLi1SByNLo&rucVx1d>xtGwQO zaM|))FC|&nre#^P=bLk+AK~#Xqh$OWj3LVen`=hs+abnIC}a9cfS~Z!&2sUx~6yfxg5ae z^}lD5Xl1T9k$%`}xTfZ?RFYs&GV*te2>h_rNui;aXl=skHMb9itzfq8kNm9R8@>u@ z>Vo|%&faKV+&FS7Ok;oNy($Ubv4w*7y9H#^`;#Aj(Lwv!3l;J25Lk^Qoh8Clf?``X zkRl!Xj2d5*Jfu)4egjpHQoZ0Ig=XZrkS~q~X%9Fg5fF@T^V#@!(Lj$$ix z#!StemWV+XE@*Kg*E8VDtgF)`f%!Z0m<-0y)C1tyZD2(`wf|j`(AEK!f*EllpZ%!NlE!y9?g1o+XR%n?vHHq zNS$ZPXVPOOy$&Z!aAZ5~&0sg3tgt>#5^Fo)_^a5{s;ReR+9^G9^dSZnDfSthh0{sG^M=KC*`+5g@u)%g$YqDKdp9(CdOK+}JXw zkgRJ+6)WmvU*O~!1z{y{%8@Gc114^A&yC5;<7$wGVahU&`=`likmAXYQtu**qU-U! zw?dyAj7}`O(YiRV4Q=$mxt~ltI!*m~A@!$10AjgiyQk>Ei&s&0s&U)nZiy#J*~Je4MPcb-W%T+=F+S?$t%t+OGQHu?|hEA)}XKLAi><&U zZV`eR#=PmFZnty{I1`|2v_g!T7UxHhmd^GZdb3IqQe`0^vv$cz3_0p-UtL!R9S(r^&j0)ed=8}-f2R=% zQR|T+352C&l=eDuMNxy(gBLpEF=T#1L)%k06nF3tT2%JIJE0*W+OpBs9(bm=mYw#F zu?BCgBn2%9a&badUl(5V^8fBt%&mg_M;YZjwDysV-57jO@LBr$2x9uR9(O5BKwV z-}n2R*X#K@?Vc~D-(S1&UpV{0;ezd5f~i*h;kv$Ki-6NnR{xG%ih8A(nT!zPTMEWp ze%@iBsH00XKG#^2ZI{ch7ET68hIiSn|B|g$yLR2XQmC8ZR4Uq^x(7QwC#ZIu8y6V6 zLiet!!(-=QaP;Cdmj2Po<(FCZpNr&g)i*ER&X!jm9UC4KkgmJ|j(B>T&>$>;6v5Cg z_j%&`V-ZmYkD~yb`Fmk@H!tz9#V2F69B0b)enFKA>Dx{MRDAa{?MGqEyn7m z-Ov_K$8~BGxoHUsIl43by@VCF5KO&&#;TAukB0%K_z2q@l2bzNVtQJd<%=s#2N}yz z)=e=zJw0B?zy5^+`El+j2lXyWThdx6Zn zo0{Qg_;Y(f+3BQ(e@55r;M&ED(RK%vYQNt4uWrcSlYSHlF$J!_ljYEvRc5S~ob($> zIL+!Yn$ltwPL#~zWNN?*5HDg>Hgb@Wz#rrm3kufq_jn+ zbzy1g;y?c-5SiPAJk5PcUm>S?JtPZC&CAe6bGq3K)h0z5YP$28n3(KRrd_^z^{KtN zdFss-xqEwC6Hg-Zjx5fdN8(pL?>J!7ygq6&ZwTkU`Dy+6Fb`~kz1aTgUGY5ky|iU@ zcqk%r5aDG;I=Z_#5rjo)iPU0YALMg{6%R*y4K0bM%ISf#RlK@ba`;az80HX~R4zgI z(HbI*8hNc$J_}0q1_kkU^H?+Ee?pr*K)MTrsErego9ino{)_9MZV+N%`MJ5Pd|`wT zN?2i+bi~~sTkQ7=X@8V>x|582qNWJ3N^F{JFs*Y1*YhFV&$F6>gr;Q(ls?Vw_Ad~Blz`f zvnFnl(C0Otp~`|lEUv`N*(j;YW7vf<1r)e_sdMM%%#nV??&A3^uOkMv>NT>8=^ zC!S-J1C%HnWS!P8P9P_9BVRyXJ7 z<`S@N7>(nWsh=5%FY@yRJ13Ukgx+3k1^?^L9dHcT$kmzs@;qkt_Q8VIoCEpuwQI06 z6`6~Q4!;SAi7nXodEnL%C(n|JiluWumd$`v(>gOdLtiyS%WvRTD=4c_{7O3qAl0!sg77fjANy6cM8QBG{)_vB_mdfAVG-Kq&|)XeSF*ByJ;7!_V;epDUL zrcq&m9IF{Z#FMR0s@msJ^AH2tEJNETqSYe&7xLWfEwVxX(WG>mSMHcef zonlI#!A_cgcGvaSMqh>xWr&){EJ!1P3(!tyd9N7b-IAl(oveRo!aiK3f16j2GpA3L z#u%8M!Sq`{L_JFw*;M7$uDq*Wu)>-CeT&ih!iu{sB_QC05cQGqj4?;DZ^)x+vCUz+ zaE0;^PKG3nVub-Lw~ zPV)vGh4K5StuS;y?6=g21KC%cXc$-iZ90}*a$HQIj7zjTMwo+oHLi~fF07v`QSbMvJX)%O0I3mZffw#u&wKiQ-1nM6OgSEqg=>>1~q zfm3}ivalEzb9!MpyTL_H9d%}QKmb0TY0c-!?c>Pzj%jo6xUE?KJ$laPspGQfaka|HRWFh69L1v4BwLV8-~H6gDL=!&5_Cw*TVpzb z9H-~|`n&fjqfP>*5o^tveAhL0LBL&+g%qFj>(_4kShO1>aRR`hA^?8$?Z8bqt;-SQPGn7Hh)jw>Z=K8{9(Dq#Y7@&L^Z3wmNP#(NLScXEb(@! zciR2-A3m4tRr?0FE|NoabaG*4$m6 z^~tZ=pyv};ws(D-Se^$f;CwATNwy37=VXWF(hDRIk)@;#eW!?`!+)i6P-abkpxxpR z+~r@|B?eyn**DzbcyhCh=h?p-0gZQ>A#v23Iv6L3@A@Vhf5T%2d8f zSqIgoR?F88C;{Ls4Izv{UHVooE zUam1J@=oL!qHt=uoC`(USeG%t{A-Btn#;-Y#P?L+FZ#`^A-^^YybTop)ST=dnUv%( z*q0=W96xj;IP;SXb#>VpicG)LIOHn?A_Q>Nf>W}x4mc4g;>JExBuszuRW)hPy1Ur8 zNjIq&`Yx2Vx#(PgQq~8=j?(y1N=nM`zyM(wCU_Xwae9(BSTBp9=~^?g!3>?SN49K& zf;zEJRm8#^Qk@rAs3^=57cJ8t{irBWqD|OP$@qdoxBT#vp3jiel6N6*NSI~{fS;|iR);4~?I^Nx!&tsg z7$MCqWWKkr?~8xIuBP>?a6U?&PcVUDsNUP__0^icPCgh~UPX2$3}5c5yHsf?LqyJG zI!;&E_X||FH00{iP#G0}B`#*+whtc)ikfg!*T@@V$n9{OrgE*=lXw7$e*_eY3Z6J| zLOD>y9!@^8;><7y9y|Cfr6QZZ39eYM)Y0hqFjE(9-dtNaf6qX^r^K$zL7jnEfMnf; zBeV2I{KM)3nB~X0@#F5|C=~)71aQiRKv$3ykVE0HT09ua*%WD6nVCnBOU)2n3)A3R zFt?$UFQ;-X&(C_PmMv5^h)EVbr0Lk_J?CB{}u=0dsJZHZ{)D z;`tRUt*xJ+qj8SCamP;d>GNhHC7-1pI$MdVs2t2gYO6U+-{P*T99N%9+yc$OBgxbw zXS!|Pv3Db7p_0DxPM#GmwLn;Lt7I=?I@|>Bz$7Ie!Cbyn=TYIXG(%h|g&zSzOY$Ir zj!#tM$2jPy?)mI4)XZ7EVjyKt{QA{&g?{$xdt67EX1ZS1HaBswE6O>*(6BFcZEdX> zDtl{NTQTUpR$oL!M6e8w7`yTlW$j8g&$oTmMGg_{<68#`#9~mhnXStmwWL@_P3-Lt zZGa{}c*nl`_?;8~WHpm1r~Z$kSIPT=g98HtKPHx$?my}LSJG=47#Ms^61p@Sj^@o( zMbxylwOfUp$q|&pU+||D*hpTmaoK9(g3Cl1p)9#T{EP60 zvCk~~(NH4Ofyrn`9nSbLW~i15?9Toh%@WHi#%AQ*SHr;9#3kc4AYWNc>4YLJZn9DM zy{{R?=J(=k;f|?M8qe?kSYmvf0$L-#Lr7j`1G_s>+D(k9eIj zJCogHhH|v72&R2)-|XmGGX?aR$|wB+pw)7B|3moP?6&+oPpc}fVE#qKgT#IUV}8FH zdL=z`GDFpC=LFQU9)x=woJcF65>FfNWUTsth-v-hK%(~!jCqroo|^g-5PU{LqWOyZ zn=#ycVgB#!m(=;SPtIR!2@^LUe9{18WG_?B{UmP;%`xA=C@Ni7ER4?2?;h3n~U6GTh zD3f88^L`)vl`yFBqvYxf)k3_K*U$IpA#8q1S?^(;mF#R(S%3UfCa*#F)BR@%mo$y9 z0Z0-m`Ds#UgWV(w57yg-1 zVR*8U&dbs+3{C#4rc+<>3z`#)W>69`=*0=~voc2>Tk8@q$CSQZiTUP>VWN=;n0Xd? zK!uzYU6BuZkW^L0;kBylEB;}@SmbsJ`j=1#DVRRd&Wawn;nJ5;S65sd1Cc`OOE>}1 z?(YjZw4FOy-B7h9>>2x8A2LE=@UneayVLj@$FFWl$(xB4)*ZqfK|2~73E@cccJQptgI~Y zlVpbVTR(4Fa&_KV!>9 zsmv-LLOC=vWP~1U1p8zGngB+QJQlI#@eHFF9DhwAL2p_s2PkuiIjYgF0%Im_1ye}D zW%yM}(x5{9n3R-cja(=Uhz5&qkwmxaNK;~>bU>c$mCr6x z@NYtVPPO+0>#QdP9lomhaW=WaP-5|Lh^`3}ADLAiDQ_W!Gu6fUOifMgztB%Vinm(m z`^uc0l43jBmNMKd*7B3k0+dp-S?XsH(Yrf-8o`kh5!8@c#q{$l*Kn$saNJbFEo{wc zr@X5*H?t8bIR#`Zu;~u3fKeFgts(9qyI*LOBg3<>=OGIyf+8{!xY~OcDFeTQu|ux6 z66TkzF3HhwMD8K13*k1Bo<46pJRu}{8a?R59B{{>VQW(ZUcT9b(&1DAR@yn&9xlh) zBe;;&nsZXs@@l;f4ELk-IGn7XOS!%^i!iF{yi4yGDet8E;`?&pPGtNS6}diHmd3}* zms6Sx$ldHiYr$782@5yM`&~VE>*p^;olmWsz-e8CF{#Rw>5h}kZC_RRS{?GbbYSYn zLS|y(l99{>g6oa8^N+7uYh&$i(iGJ?0!RN?P3yU7+9+(Yn|LyYn~qAqUMG6Spu57+ z>VZ9l8cW7D?lueYu<+%}yK%Sf`3N3O?_|H4(>2HLRJngIIB%@_ohHk?fuLsV1`56G zr}UUyt;|f)YqR9x!#$jBxY!?eUS_r3H@S|m{9dEGT9q2N`uOI#j1@HE78C|FF9eoB hHTnN1Tdd=UsC4au^K7nrL;nV9U(_