├── .github └── workflows │ └── cpp.yml ├── .gitignore ├── .gitlab-ci.yml ├── .gitlab-ci.yml.asc ├── .gitmodules ├── CMakeLists.txt ├── Docker ├── Dockerfile └── build.sh ├── Example ├── CMakeLists.txt ├── broadcaster.cpp ├── capture.cpp └── shell.cpp ├── HAL ├── CMakeLists.txt ├── include │ ├── common.h │ ├── router_hal.h │ └── router_hal_common.h └── src │ ├── common.cpp │ ├── linux │ ├── platform │ │ └── standard.h │ └── router_hal.cpp │ ├── macOS │ └── router_hal.cpp │ └── stdio │ └── router_hal.cpp ├── Homework ├── dhcpv6 │ ├── .gitignore │ ├── HONOR-CODE.md │ ├── common │ │ └── Makefrag │ ├── custom │ │ └── Makefile │ ├── dhcpv6.h │ ├── main.cpp │ └── r1 │ │ └── Makefile ├── eui64 │ ├── .gitignore │ ├── HONOR-CODE.md │ ├── Makefile │ ├── README.md │ ├── README_en.md │ ├── data │ │ ├── eui64_answer1.txt │ │ ├── eui64_answer2.txt │ │ ├── eui64_answer3.txt │ │ ├── eui64_answer4.txt │ │ ├── eui64_input1.txt │ │ ├── eui64_input2.txt │ │ ├── eui64_input3.txt │ │ └── eui64_input4.txt │ ├── eui64.cpp │ ├── eui64.h │ ├── grade.py │ └── main.cpp ├── internet-checksum │ ├── .gitignore │ ├── HONOR-CODE.md │ ├── Makefile │ ├── README.md │ ├── README_en.md │ ├── checksum.cpp │ ├── checksum.h │ ├── data │ │ ├── checksum_answer1.txt │ │ ├── checksum_answer2.txt │ │ ├── checksum_answer3.txt │ │ ├── checksum_answer4.txt │ │ ├── checksum_input1.pcap │ │ ├── checksum_input2.pcap │ │ ├── checksum_input3.pcap │ │ └── checksum_input4.pcap │ ├── grade.py │ └── main.cpp ├── lookup │ ├── .gitignore │ ├── HONOR-CODE.md │ ├── Makefile │ ├── README.md │ ├── README_en.md │ ├── data │ │ ├── lookup_answer1.txt │ │ ├── lookup_answer2.txt │ │ ├── lookup_answer3.txt │ │ ├── lookup_answer4.txt │ │ ├── lookup_answer5.txt │ │ ├── lookup_input1.txt │ │ ├── lookup_input2.txt │ │ ├── lookup_input3.txt │ │ ├── lookup_input4.txt │ │ └── lookup_input5.txt │ ├── grade.py │ ├── lookup.cpp │ ├── lookup.h │ └── main.cpp ├── ospf │ ├── .gitignore │ ├── HONOR-CODE.md │ ├── README.md │ ├── common │ │ └── Makefrag │ ├── custom │ │ └── Makefile │ ├── interconnect-r2 │ │ └── Makefile │ ├── main.cpp │ ├── r1 │ │ └── Makefile │ ├── r2 │ │ └── Makefile │ └── r3 │ │ └── Makefile ├── protocol-ospf │ ├── .gitignore │ ├── HONOR-CODE.md │ ├── Makefile │ ├── README.md │ ├── README_en.md │ ├── data │ │ ├── protocol_ospf_answer1.txt │ │ ├── protocol_ospf_answer2.txt │ │ ├── protocol_ospf_input1.pcap │ │ └── protocol_ospf_input2.pcap │ ├── grade.py │ ├── main.cpp │ ├── ospf_lsa_csum.cpp │ ├── protocol_ospf.cpp │ └── protocol_ospf.h ├── protocol │ ├── .gitignore │ ├── HONOR-CODE.md │ ├── Makefile │ ├── README.md │ ├── README_en.md │ ├── data │ │ ├── protocol_answer1.txt │ │ ├── protocol_answer10.txt │ │ ├── protocol_answer11.txt │ │ ├── protocol_answer12.txt │ │ ├── protocol_answer2.txt │ │ ├── protocol_answer3.txt │ │ ├── protocol_answer4.txt │ │ ├── protocol_answer5.txt │ │ ├── protocol_answer6.txt │ │ ├── protocol_answer7.txt │ │ ├── protocol_answer8.txt │ │ ├── protocol_answer9.txt │ │ ├── protocol_input1.pcap │ │ ├── protocol_input10.pcap │ │ ├── protocol_input11.pcap │ │ ├── protocol_input12.pcap │ │ ├── protocol_input2.pcap │ │ ├── protocol_input3.pcap │ │ ├── protocol_input4.pcap │ │ ├── protocol_input5.pcap │ │ ├── protocol_input6.pcap │ │ ├── protocol_input7.pcap │ │ ├── protocol_input8.pcap │ │ └── protocol_input9.pcap │ ├── grade.py │ ├── main.cpp │ ├── protocol.cpp │ └── protocol.h ├── router │ ├── .gitignore │ ├── HONOR-CODE.md │ ├── README.md │ ├── common │ │ └── Makefrag │ ├── custom │ │ └── Makefile │ ├── interconnect-r2 │ │ └── Makefile │ ├── main.cpp │ ├── r1 │ │ └── Makefile │ ├── r2 │ │ └── Makefile │ └── r3 │ │ └── Makefile └── tftp │ ├── .gitignore │ ├── HONOR-CODE.md │ ├── client.cpp │ ├── common │ └── Makefrag │ ├── pc1 │ └── Makefile │ ├── pc2 │ ├── Makefile │ └── router │ ├── r2 │ ├── Makefile │ └── router │ ├── server.cpp │ └── tftp.h ├── README.md └── Setup ├── dhcpv6 ├── setup │ ├── setup-netns.sh │ └── start-r1.sh └── test │ ├── test2.sh │ ├── test3.sh │ ├── test4.sh │ └── test5.sh ├── interconnect ├── client │ └── .gitignore ├── server │ └── .gitignore ├── setup │ ├── .gitignore │ ├── bird-v2 │ │ ├── bird-r2-v2-ospf.conf │ │ ├── bird-r2-v2-ospf.sh │ │ ├── bird-r2-v2-ripng.conf │ │ ├── bird-r2-v2-ripng.sh │ │ ├── bird-r3-v2-ospf.conf │ │ ├── bird-r3-v2-ospf.sh │ │ ├── bird-r3-v2-ripng.conf │ │ └── bird-r3-v2-ripng.sh │ ├── dhcpd.conf │ ├── radvd.conf │ ├── setup-netns.sh │ ├── start-custom-pc2.sh │ ├── start-custom-r1.sh │ ├── start-custom-r2-ospf.sh │ ├── start-custom-r2-ripng.sh │ ├── start-custom-r3-ospf.sh │ ├── start-custom-r3-ripng.sh │ ├── start-standard-pc2.sh │ ├── start-standard-r1-dhcpd.sh │ └── start-standard-r1-radvd.sh └── test │ ├── test2.sh │ ├── test4-bird-r2-v2.sh │ ├── test4-bird-r3-v2.sh │ ├── test5.sh │ ├── test6.sh │ ├── test7-standard.sh │ ├── test7.sh │ ├── test8-standard.sh │ ├── test8.sh │ ├── test9-standard.sh │ └── test9.sh ├── ospf ├── setup │ ├── bird-v2 │ │ ├── bird-r1-v2.conf │ │ ├── bird-r1-v2.sh │ │ ├── bird-r3-v2.conf │ │ └── bird-r3-v2.sh │ ├── setup-netns.sh │ └── start-r2.sh └── test │ ├── test3-bird-v2.sh │ ├── test4.sh │ ├── test5.sh │ ├── test6.sh │ └── test7.sh ├── ripng ├── setup │ ├── bird-v2 │ │ ├── bird-r1-v2.conf │ │ ├── bird-r1-v2.sh │ │ ├── bird-r3-v2.conf │ │ └── bird-r3-v2.sh │ ├── setup-netns.sh │ └── start-r2.sh └── test │ ├── test3.sh │ ├── test4.sh │ ├── test5.sh │ ├── test6.sh │ ├── test7.sh │ └── test8.sh └── tftp ├── client └── .gitignore ├── server └── .gitignore ├── setup ├── setup-netns.sh ├── start-custom-tftpd-r2.sh └── start-standard-tftpd-r2.sh └── test ├── test2.sh ├── test3.sh ├── test5.sh ├── test6.sh ├── test7.sh ├── test8.sh └── test9.sh /.github/workflows/cpp.yml: -------------------------------------------------------------------------------- 1 | name: C/C++ CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ${{ matrix.os }} 13 | strategy: 14 | matrix: 15 | os: [ubuntu-latest, macos-latest] 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Install dependencies 20 | if: runner.os == 'Linux' 21 | run: | 22 | sudo apt install -y libpcap-dev libreadline-dev libncurses-dev 23 | - name: CMake 24 | run: | 25 | mkdir -p build 26 | cd build 27 | cmake .. -DBACKEND=${{ runner.os }} 28 | make 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | .vscode/ 3 | .idea/ 4 | cmake-build-*/ 5 | *.log 6 | .DS_Store 7 | 8 | # Created by https://www.gitignore.io/api/c++,cmake 9 | # Edit at https://www.gitignore.io/?templates=c++,cmake 10 | 11 | ### C++ ### 12 | # Prerequisites 13 | *.d 14 | 15 | # Compiled Object files 16 | *.slo 17 | *.lo 18 | *.o 19 | *.obj 20 | 21 | # Precompiled Headers 22 | *.gch 23 | *.pch 24 | 25 | # Compiled Dynamic libraries 26 | *.so 27 | *.dylib 28 | *.dll 29 | 30 | # Fortran module files 31 | *.mod 32 | *.smod 33 | 34 | # Compiled Static libraries 35 | *.lai 36 | *.la 37 | *.a 38 | *.lib 39 | 40 | # Executables 41 | *.exe 42 | *.out 43 | *.app 44 | 45 | ### CMake ### 46 | CMakeLists.txt.user 47 | CMakeCache.txt 48 | CMakeFiles 49 | CMakeScripts 50 | Testing 51 | Makefile 52 | cmake_install.cmake 53 | install_manifest.txt 54 | compile_commands.json 55 | CTestTestfile.cmake 56 | _deps 57 | 58 | ### CMake Patch ### 59 | # External projects 60 | *-prefix/ 61 | 62 | # End of https://www.gitignore.io/api/c++,cmake 63 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | homework: 2 | image: jiegec/router-lab-x86_64 3 | script: 4 | # DO NOT EDIT! 5 | - gpg --verify .gitlab-ci.yml.asc .gitlab-ci.yml 6 | - mkdir logs 7 | - make -C Homework/eui64 8 | - (cd Homework/eui64 && python3 grade.py | tee ../../logs/eui64.log) 9 | - make -C Homework/internet-checksum 10 | - (cd Homework/internet-checksum && python3 grade.py | tee ../../logs/internet-checksum.log) 11 | - make -C Homework/lookup 12 | - (cd Homework/lookup && python3 grade.py | tee ../../logs/lookup.log) 13 | - make -C Homework/protocol 14 | - (cd Homework/protocol && python3 grade.py | tee ../../logs/protocol.log) 15 | - make -C Homework/protocol-ospf 16 | - (cd Homework/protocol-ospf && python3 grade.py | tee ../../logs/protocol-ospf.log) 17 | artifacts: 18 | paths: 19 | - logs 20 | when: always 21 | expire_in: 1 year 22 | 23 | router: 24 | image: jiegec/router-lab-aarch64 25 | tags: 26 | - arm64 27 | script: 28 | # DO NOT EDIT! 29 | - gpg --verify .gitlab-ci.yml.asc .gitlab-ci.yml 30 | - make -C Homework/router/r1 31 | - make -C Homework/router/r2 32 | - make -C Homework/router/r3 33 | - make -C Homework/router/interconnect-r2 34 | - make -C Homework/tftp/pc1 35 | - make -C Homework/tftp/pc2 36 | - make -C Homework/tftp/r2 37 | - make -C Homework/dhcpv6/r1 38 | - make -C Homework/ospf/r1 39 | - make -C Homework/ospf/r2 40 | - make -C Homework/ospf/r3 41 | - make -C Homework/ospf/interconnect-r2 42 | artifacts: 43 | paths: 44 | - Homework/router/r1/router 45 | - Homework/router/r2/router 46 | - Homework/router/r3/router 47 | - Homework/router/interconnect-r2/router 48 | - Homework/tftp/pc1/client 49 | - Homework/tftp/pc1/server 50 | - Homework/tftp/pc2/client 51 | - Homework/tftp/pc2/server 52 | - Homework/tftp/r2/client 53 | - Homework/tftp/r2/server 54 | - Homework/dhcpv6/r1/router 55 | - Homework/ospf/r1/router 56 | - Homework/ospf/r2/router 57 | - Homework/ospf/r3/router 58 | - Homework/ospf/interconnect-r2/router 59 | when: always 60 | expire_in: 1 year 61 | -------------------------------------------------------------------------------- /.gitlab-ci.yml.asc: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP SIGNATURE----- 2 | 3 | iQEzBAABCAAdFiEE4r5HYZJqvbepUleQ2QBvE9fbMRoFAmVOQNAACgkQ2QBvE9fb 4 | MRr2cAf/e9FNyTB+1uWaQOpH7ZXrmowsPn+8xB7tc50Er6lAfQXpeppsMEzs25K3 5 | FYGywC4jmIj1MqEo29S3+JhIrV3PMNO96HJU6z3I/79PO70iTrM9NURQarzZ4Ia0 6 | cAi0YTRzx3KPksCeTC3Snzmrv++XRgJjyvQKiDz1RiaRQ0iwUZyrmgw6V3ue0pz1 7 | O7vWP/KrFv9NnODYUOftGjLofAsm0zi1pXn5PQ9ZtjvYxeFADhWAQSdfLINPtpiw 8 | lnA0cZLUkYC0OTO2MvofVa3bhPMU8x08b0ep6Y8IjpE09gIonPTDakon5Ok64WN0 9 | r3InprBG8d1y/v/C15CG+45Ko0Cxgg== 10 | =mIu+ 11 | -----END PGP SIGNATURE----- 12 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thu-cs-lab/Router-Lab/a4ffe2773060e7c630c63d4a08d184562679b5db/.gitmodules -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | project(ROUTER_LAB LANGUAGES C CXX) 3 | 4 | set(CMAKE_C_STANDARD 11) 5 | set(CMAKE_CXX_STANDARD 11) 6 | 7 | set(BACKEND Linux CACHE STRING "Router platform") 8 | set(BACKEND_VALUES "Linux" "macOS" "stdio") 9 | set_property(CACHE BACKEND PROPERTY STRINGS ${BACKEND_VALUES}) 10 | list(FIND BACKEND_VALUES ${BACKEND} BACKEND_INDEX) 11 | 12 | if(${BACKEND_INDEX} EQUAL -1) 13 | message(WARNING "Backend ${BACKEND} not supported, valid items are: ${BACKEND_VALUES}") 14 | set(BACKEND "Linux") 15 | else() 16 | message("Using backend ${BACKEND}") 17 | endif() 18 | 19 | string(TOUPPER "${BACKEND}" BACKEND) 20 | add_definitions("-DROUTER_BACKEND_${BACKEND}") 21 | 22 | add_subdirectory(HAL) 23 | add_subdirectory(Example) 24 | -------------------------------------------------------------------------------- /Docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:bullseye 2 | RUN sed -i 's/deb.debian.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list 3 | RUN sed -i 's/security.debian.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list 4 | RUN apt update 5 | RUN apt install -y python3-pip python3-py python3-lxml libxml2-dev libxslt-dev gpg wget 6 | RUN pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple pyshark 7 | RUN gpg --keyserver hkp://keyserver.ubuntu.com --recv-keys E2BE4761926ABDB7A9525790D9006F13D7DB311A 8 | RUN wget http://mirrors.tuna.tsinghua.edu.cn/debian/pool/main/libp/libpcap/libpcap_1.10.0.orig.tar.gz 9 | RUN tar xvf libpcap_1.10.0.orig.tar.gz 10 | WORKDIR libpcap-1.10.0 11 | RUN apt install -y flex bison 12 | RUN ./configure --prefix=/usr --disable-dbus --disable-rdma --disable-usb --enable-shared 13 | RUN make -j4 install 14 | 15 | -------------------------------------------------------------------------------- /Docker/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | IMAGE_NAME=jiegec/router-lab-$(uname -m) 3 | sudo docker build -t $IMAGE_NAME . 4 | sudo docker push $IMAGE_NAME 5 | 6 | -------------------------------------------------------------------------------- /Example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | 3 | find_package(PkgConfig REQUIRED) 4 | pkg_check_modules(READLINE readline) 5 | pkg_check_modules(NCURSES ncurses) 6 | if ("${READLINE_FOUND}" AND "${NCURSES_FOUND}") 7 | add_executable(shell shell.cpp) 8 | target_include_directories(shell PRIVATE ../HAL/include) 9 | target_link_libraries(shell router_hal ${READLINE_LIBRARIES} ${NCURSES_LIBRARIES}) 10 | endif() 11 | 12 | add_executable(broadcaster broadcaster.cpp) 13 | target_include_directories(broadcaster PRIVATE ../HAL/include) 14 | target_link_libraries(broadcaster router_hal) 15 | 16 | add_executable(capture capture.cpp) 17 | target_include_directories(capture PRIVATE ../HAL/include) 18 | target_link_libraries(capture router_hal) 19 | -------------------------------------------------------------------------------- /Example/broadcaster.cpp: -------------------------------------------------------------------------------- 1 | #include "router_hal.h" 2 | #include 3 | #include 4 | #include 5 | 6 | uint8_t packet[2048] __attribute__((aligned(4))); 7 | bool cont = false; 8 | 9 | // fd00::0:1 ~ fd00::3:1 10 | in6_addr addrs[N_IFACE_ON_BOARD] = { 11 | {0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 12 | 0x00, 0x00, 0x00, 0x01}, 13 | {0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 14 | 0x00, 0x01, 0x00, 0x01}, 15 | {0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 16 | 0x00, 0x02, 0x00, 0x01}, 17 | {0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 18 | 0x00, 0x03, 0x00, 0x01}, 19 | }; 20 | 21 | void interrupt(int _) { 22 | printf("Interrupt\n"); 23 | cont = false; 24 | return; 25 | } 26 | 27 | int main() { 28 | fprintf(stderr, "HAL init: %d\n", HAL_Init(1, addrs)); 29 | for (int i = 0; i < N_IFACE_ON_BOARD; i++) { 30 | ether_addr mac; 31 | HAL_GetInterfaceMacAddress(i, &mac); 32 | fprintf(stderr, "%d: %s\n", i, ether_ntoa(mac)); 33 | } 34 | 35 | while (1) { 36 | int mask = (1 << N_IFACE_ON_BOARD) - 1; 37 | ether_addr src_mac; 38 | ether_addr dst_mac; 39 | int if_index; 40 | int res = HAL_ReceiveIPPacket(mask, packet, sizeof(packet), &src_mac, 41 | &dst_mac, 1000, &if_index); 42 | if (res > 0) { 43 | for (int i = 0; i < N_IFACE_ON_BOARD; i++) { 44 | HAL_SendIPPacket(i, packet, res, src_mac); 45 | } 46 | } else if (res == 0) { 47 | fprintf(stderr, "Timeout\n"); 48 | } else { 49 | fprintf(stderr, "Error: %d\n", res); 50 | break; 51 | } 52 | } 53 | return 0; 54 | } -------------------------------------------------------------------------------- /Example/capture.cpp: -------------------------------------------------------------------------------- 1 | #include "router_hal.h" 2 | #include 3 | #include 4 | #include 5 | 6 | uint8_t packet[2048] __attribute__((aligned(4))); 7 | bool cont = false; 8 | 9 | // fd00::0:1 ~ fd00::3:1 10 | in6_addr addrs[N_IFACE_ON_BOARD] = { 11 | {0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 12 | 0x00, 0x00, 0x00, 0x01}, 13 | {0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 14 | 0x00, 0x01, 0x00, 0x01}, 15 | {0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 16 | 0x00, 0x02, 0x00, 0x01}, 17 | {0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 18 | 0x00, 0x03, 0x00, 0x01}, 19 | }; 20 | 21 | int main() { 22 | fprintf(stderr, "HAL init: %d\n", HAL_Init(1, addrs)); 23 | for (int i = 0; i < N_IFACE_ON_BOARD; i++) { 24 | ether_addr mac; 25 | HAL_GetInterfaceMacAddress(i, &mac); 26 | fprintf(stderr, "%d: %s\n", i, ether_ntoa(mac)); 27 | } 28 | 29 | while (1) { 30 | int mask = (1 << N_IFACE_ON_BOARD) - 1; 31 | ether_addr src_mac; 32 | ether_addr dst_mac; 33 | int if_index; 34 | int res = HAL_ReceiveIPPacket(mask, packet, sizeof(packet), &src_mac, 35 | &dst_mac, 1000, &if_index); 36 | if (res > 0) { 37 | printf("Got IP packet of length %d from port %d\n", res, if_index); 38 | printf("Src MAC: %s ", ether_ntoa(dst_mac)); 39 | printf("Dst MAC: %s\n", ether_ntoa(src_mac)); 40 | printf("\nData: "); 41 | for (int i = 0; i < res; i++) { 42 | printf("%02X ", packet[i]); 43 | } 44 | printf("\n"); 45 | } else if (res == 0) { 46 | fprintf(stderr, "Timeout\n"); 47 | } else { 48 | fprintf(stderr, "Error: %d\n", res); 49 | break; 50 | } 51 | } 52 | return 0; 53 | } -------------------------------------------------------------------------------- /Example/shell.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include "router_hal.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | char *buffer; 12 | uint8_t packet[2048] __attribute__((aligned(4))); 13 | bool cont = false; 14 | 15 | // fd00::0:1 ~ fd00::3:1 16 | in6_addr addrs[N_IFACE_ON_BOARD] = { 17 | {0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 18 | 0x00, 0x00, 0x00, 0x01}, 19 | {0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 20 | 0x00, 0x01, 0x00, 0x01}, 21 | {0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 22 | 0x00, 0x02, 0x00, 0x01}, 23 | {0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 24 | 0x00, 0x03, 0x00, 0x01}, 25 | }; 26 | 27 | void interrupt(int _) { 28 | printf("Interrupt\n"); 29 | cont = false; 30 | return; 31 | } 32 | 33 | int main() { 34 | printf("HAL init: %d\n", HAL_Init(1, addrs)); 35 | for (int i = 0; i < N_IFACE_ON_BOARD; i++) { 36 | ether_addr mac; 37 | HAL_GetInterfaceMacAddress(i, &mac); 38 | printf("%d: %s\n", i, ether_ntoa(mac)); 39 | } 40 | signal(SIGINT, interrupt); 41 | rl_bind_key('\t', rl_insert); 42 | while ((buffer = readline("> "))) { 43 | add_history(buffer); 44 | if (strcmp(buffer, "time") == 0) { 45 | printf("Current tick %lld\n", HAL_GetTicks()); 46 | } else if (strncmp(buffer, "ndp", strlen("ndp")) == 0) { 47 | int if_index; 48 | int ip1, ip2, ip3, ip4; 49 | char addr_buffer[128]; 50 | sscanf(buffer, "ndp %d %s", &if_index, addr_buffer); 51 | in6_addr ip = {}; 52 | assert(inet_pton(AF_INET6, addr_buffer, &ip) == 1); 53 | printf("Get ndp address of %s if %d\n", inet6_ntoa(ip), if_index); 54 | ether_addr mac; 55 | int res = HAL_GetNeighborMacAddress(if_index, ip, &mac); 56 | if (res == 0) { 57 | printf("Found: %s\n", ether_ntoa(mac)); 58 | } else { 59 | printf("Not found: %d\n", res); 60 | } 61 | } else if (strncmp(buffer, "mac", strlen("mac")) == 0) { 62 | int if_index; 63 | sscanf(buffer, "mac %d", &if_index); 64 | ether_addr mac; 65 | int res = HAL_GetInterfaceMacAddress(if_index, &mac); 66 | if (res == 0) { 67 | printf("Found: %s\n", ether_ntoa(mac)); 68 | } else { 69 | printf("Not found: %d\n", res); 70 | } 71 | } else if (strncmp(buffer, "cap", strlen("cap")) == 0) { 72 | int mask = (1 << N_IFACE_ON_BOARD) - 1; 73 | ether_addr src_mac; 74 | ether_addr dst_mac; 75 | int if_index; 76 | int res = HAL_ReceiveIPPacket(mask, packet, sizeof(packet), &src_mac, 77 | &dst_mac, 1000, &if_index); 78 | if (res > 0) { 79 | printf("Got IP packet of length %d from port %d\n", res, if_index); 80 | printf("Src MAC: %s ", ether_ntoa(src_mac)); 81 | printf(" Dst MAC: %s\n", ether_ntoa(dst_mac)); 82 | printf("Data: "); 83 | for (int i = 0; i < res; i++) { 84 | printf("%02X ", packet[i]); 85 | } 86 | printf("\n"); 87 | } else if (res == 0) { 88 | printf("Timeout\n"); 89 | } else { 90 | printf("Error: %d\n", res); 91 | } 92 | } else if (strncmp(buffer, "out", strlen("out")) == 0) { 93 | int if_index; 94 | sscanf(buffer, "out %d", &if_index); 95 | ether_addr dst_mac; 96 | int len = 64; 97 | for (int i = 0; i < len; i++) { 98 | packet[i] = rand(); 99 | } 100 | for (int i = 0; i < sizeof(ether_addr); i++) { 101 | dst_mac.ether_addr_octet[i] = rand(); 102 | } 103 | int res = HAL_SendIPPacket(if_index, packet, len, dst_mac); 104 | if (res == 0) { 105 | printf("Packet sent\n"); 106 | } else { 107 | printf("Sent failed: %d\n", res); 108 | } 109 | } else if (strncmp(buffer, "loop", strlen("loop")) == 0) { 110 | cont = true; 111 | while (getch() == ERR && cont) { 112 | int mask = (1 << N_IFACE_ON_BOARD) - 1; 113 | ether_addr src_mac; 114 | ether_addr dst_mac; 115 | int if_index; 116 | int res = HAL_ReceiveIPPacket(mask, packet, sizeof(packet), &src_mac, 117 | &dst_mac, 1000, &if_index); 118 | if (res < 0) { 119 | printf("loop failed with %d\n", res); 120 | break; 121 | } 122 | } 123 | } else if (strncmp(buffer, "quit", strlen("quit")) == 0) { 124 | free(buffer); 125 | break; 126 | } else { 127 | printf("Unknown command.\n"); 128 | printf("Usage:\n"); 129 | printf("\ttime: show current ticks\n"); 130 | printf("\tndp index ipv6_addr: lookup by ndp\n"); 131 | printf("\tmac index: print MAC address of interface\n"); 132 | printf("\tcap: capture one packet\n"); 133 | printf("\tout index: send random packet to interface\n"); 134 | printf("\tloop: read packets until interrupted\n"); 135 | printf("\tquit: exit shell\n"); 136 | } 137 | free(buffer); 138 | } 139 | return 0; 140 | } 141 | -------------------------------------------------------------------------------- /HAL/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if(${BACKEND} STREQUAL LINUX) 2 | file(GLOB_RECURSE SOURCES src/linux/*.cpp) 3 | file(GLOB_RECURSE HEADERS src/linux/*.h) 4 | set(LIBRARIES pcap) 5 | elseif(${BACKEND} STREQUAL MACOS) 6 | file(GLOB_RECURSE SOURCES src/macOS/*.cpp) 7 | set(LIBRARIES pcap) 8 | elseif(${BACKEND} STREQUAL STDIO) 9 | file(GLOB_RECURSE SOURCES src/stdio/*.cpp) 10 | set(LIBRARIES pcap) 11 | endif() 12 | set(SOURCES ${SOURCES} src/common.cpp) 13 | 14 | # add sources from homework 15 | set(SOURCES ${SOURCES} ../Homework/eui64/eui64.cpp ../Homework/internet-checksum/checksum.cpp) 16 | 17 | add_library(router_hal ${SOURCES} ${HEADERS}) 18 | target_include_directories(router_hal PUBLIC include) 19 | target_include_directories(router_hal PUBLIC ../Homework/eui64 ../Homework/internet-checksum) 20 | target_link_libraries(router_hal ${LIBRARIES}) -------------------------------------------------------------------------------- /HAL/include/common.h: -------------------------------------------------------------------------------- 1 | #ifndef __COMMON_H__ 2 | #define __COMMON_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | // definition of ether_addr and ether_header 11 | #include 12 | static_assert(sizeof(ether_addr) == 6, "sizeof(ether_addr) should be 6"); 13 | static_assert(sizeof(ether_header) == 14, "sizeof(ether_header) should be 14"); 14 | 15 | // definition of in6_addr 16 | #include 17 | static_assert(sizeof(in6_addr) == 16, "sizeof(in6_addr) should be 16"); 18 | 19 | // definition of ip6_hdr 20 | #include 21 | static_assert(sizeof(ip6_hdr) == 40, "sizeof(ip6_hdr) should be 40"); 22 | 23 | // definition of udphdr 24 | #include 25 | static_assert(sizeof(udphdr) == 8, "sizeof(udphdr) should be 8"); 26 | 27 | // definition of icmp6_hdr 28 | #include 29 | static_assert(sizeof(icmp6_hdr) == 8, "sizeof(icmp6_hdr) should be 8"); 30 | 31 | // utility functions 32 | in6_addr operator&(const in6_addr &a, const in6_addr &b); 33 | bool operator!=(const in6_addr &a, const in6_addr &b); 34 | bool operator==(const in6_addr &a, const in6_addr &b); 35 | bool operator<(const in6_addr &a, const in6_addr &b); 36 | 37 | // compute solicited-node multicast address 38 | // ref: rfc4291 39 | in6_addr get_solicited_node_mcast_addr(const in6_addr ip); 40 | // compute mac for ipv6 mcast addr 41 | // ref: rfc2464 42 | void get_ipv6_mcast_mac(const in6_addr mcast_ip, ether_addr *mac); 43 | 44 | // non thread-safe, beware! 45 | // don't call it twice in a same call to printf! 46 | const char *inet6_ntoa(const in6_addr addr); 47 | const char *ether_ntoa(const ether_addr mac); 48 | in6_addr inet6_pton(const char *addr); 49 | 50 | #endif -------------------------------------------------------------------------------- /HAL/include/router_hal.h: -------------------------------------------------------------------------------- 1 | #ifndef __ROUTER_HAL_H__ 2 | #define __ROUTER_HAL_H__ 3 | 4 | #include "common.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #define HAL_IN const 12 | #define HAL_OUT 13 | 14 | #define N_IFACE_ON_BOARD 4 15 | 16 | enum HAL_ERROR_NUMBER { 17 | HAL_ERR_INVALID_PARAMETER = -1000, 18 | HAL_ERR_IP_NOT_EXIST, 19 | HAL_ERR_IFACE_NOT_EXIST, 20 | HAL_ERR_CALLED_BEFORE_INIT, 21 | HAL_ERR_EOF, 22 | HAL_ERR_NOT_SUPPORTED, 23 | HAL_ERR_UNKNOWN, 24 | }; 25 | 26 | #ifdef __cplusplus 27 | extern "C" { 28 | #endif 29 | 30 | /** 31 | * @brief 初始化,在所有其他函数调用前调用且仅调用一次 32 | * 33 | * @param debug IN,零表示关闭调试信息,非零表示输出调试信息到标准错误输出 34 | * @param if_addrs IN,包含 N_IFACE_ON_BOARD 个 IPv6 地址, 35 | * 对应每个端口的 IPv6 地址(非 Link Local 地址) 36 | * 37 | * @return int 0 表示成功,非 0 表示失败 38 | */ 39 | int HAL_Init(HAL_IN int debug, HAL_IN in6_addr if_addrs[N_IFACE_ON_BOARD]); 40 | 41 | /** 42 | * @brief 获取从启动到当前时刻的毫秒数 43 | * 44 | * @return uint64_t 毫秒数 45 | */ 46 | uint64_t HAL_GetTicks(); 47 | 48 | /** 49 | * @brief 从 NDP 邻居表中查询 IPv6 对应的 MAC 地址 50 | * 51 | * 如果是表中不存在的 IPv6,系统将自动发送 NDP Neighbor Solicitation 52 | * 报文进行查询,待对方主机回应后可重新调用本接口从表中查询。 53 | * 部分后端会限制发送的 NDP 报文数量,如每秒向同一个主机最多发送一个 NDP 报文 54 | * 55 | * @param if_index IN,接口索引号,[0, N_IFACE_ON_BOARD-1] 56 | * @param ip IN,要查询的 IPv6 地址 57 | * @param o_mac OUT,查询结果 MAC 地址 58 | * @return int 0 表示成功,非 0 为失败 59 | */ 60 | int HAL_GetNeighborMacAddress(HAL_IN int if_index, HAL_IN in6_addr ip, 61 | HAL_OUT ether_addr *o_mac); 62 | 63 | /** 64 | * @brief 获取网卡的 MAC 地址,如果为全 0 代表系统中不存在该网卡或者获取失败 65 | * 66 | * @param if_index IN,接口索引号,[0, N_IFACE_ON_BOARD-1] 67 | * @param o_mac OUT,网卡的 MAC 地址 68 | * @return int 0 表示成功,非 0 为失败 69 | */ 70 | int HAL_GetInterfaceMacAddress(HAL_IN int if_index, HAL_OUT ether_addr *o_mac); 71 | 72 | /** 73 | * @brief 接收一个 IPv6 报文,保证不会收到自己发送的报文; 74 | * 请保证缓冲区大小足够大(如大于常见的 MTU),报文只能读取一次 75 | * 76 | * @param if_index_mask IN,接口索引号的 bitset,最低的 N_IFACE_ON_BOARD 77 | * 位有效,对于每一位,1 代表接收对应接口,0 代表不接收; 78 | * 部分平台仅支持所有接口都开启接收的情况 79 | * @param buffer OUT,接收缓冲区,由调用者分配 80 | * @param length IN,接收缓存区大小 81 | * @param src_mac OUT,IPv6 报文下层的源 MAC 地址 82 | * @param dst_mac OUT,IPv6 报文下层的目的 MAC 地址 83 | * @param timeout IN,设置接收超时时间(毫秒),-1 表示无限等待 84 | * @param if_index OUT,实际接收到的报文来源的接口号,不能为空指针 85 | * @return int >0 表示实际接收的报文长度,=0 表示超时返回,<0 表示发生错误 86 | */ 87 | int HAL_ReceiveIPPacket(HAL_IN int if_index_mask, HAL_OUT uint8_t *buffer, 88 | HAL_IN size_t length, HAL_OUT ether_addr *src_mac, 89 | HAL_OUT ether_addr *dst_mac, HAL_IN int64_t timeout, 90 | HAL_OUT int *if_index); 91 | 92 | /** 93 | * @brief 发送一个 IPv6 报文,它的源 MAC 地址就是对应接口的 MAC 地址 94 | * 95 | * @param if_index IN,接口索引号,[0, N_IFACE_ON_BOARD-1] 96 | * @param buffer IN,发送缓冲区 97 | * @param length IN,待发送报文的长度 98 | * @param dst_mac IN,IPv6 报文下层的目的 MAC 地址 99 | * @return int 0 表示成功,非 0 为失败 100 | */ 101 | int HAL_SendIPPacket(HAL_IN int if_index, HAL_IN uint8_t *buffer, 102 | HAL_IN size_t length, HAL_IN ether_addr dst_mac); 103 | 104 | #ifdef __cplusplus 105 | } 106 | #endif 107 | 108 | #endif -------------------------------------------------------------------------------- /HAL/src/common.cpp: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include 3 | 4 | in6_addr operator&(const in6_addr &a, const in6_addr &b) { 5 | in6_addr res; 6 | for (int i = 0; i < 16; i++) { 7 | res.s6_addr[i] = a.s6_addr[i] & b.s6_addr[i]; 8 | } 9 | return res; 10 | } 11 | 12 | bool operator!=(const in6_addr &a, const in6_addr &b) { 13 | for (int i = 0; i < 16; i++) { 14 | if (a.s6_addr[i] != b.s6_addr[i]) { 15 | return true; 16 | } 17 | } 18 | return false; 19 | } 20 | 21 | bool operator==(const in6_addr &a, const in6_addr &b) { return !(a != b); } 22 | 23 | bool operator<(const in6_addr &a, const in6_addr &b) { 24 | for (int i = 0; i < 16; i++) { 25 | if (a.s6_addr[i] != b.s6_addr[i]) { 26 | return a.s6_addr[i] < b.s6_addr[i]; 27 | } 28 | } 29 | return false; 30 | } 31 | 32 | in6_addr get_solicited_node_mcast_addr(const in6_addr ip) { 33 | // https://datatracker.ietf.org/doc/html/rfc4291#section-2.7.1 34 | in6_addr res = { 35 | 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 36 | 0x00, 0x00, 0x00, 0x01, 0xff, 0x00, 0x00, 0x00, 37 | }; 38 | // replace low-order 24 bits 39 | memcpy(&res.s6_addr[13], &ip.s6_addr[13], 3); 40 | return res; 41 | } 42 | 43 | void get_ipv6_mcast_mac(const in6_addr mcast_ip, ether_addr *mac) { 44 | // https://datatracker.ietf.org/doc/html/rfc2464#section-7 45 | mac->ether_addr_octet[0] = mac->ether_addr_octet[1] = 0x33; 46 | memcpy(&mac->ether_addr_octet[2], &mcast_ip.s6_addr[12], 4); 47 | } 48 | 49 | const char *inet6_ntoa(const in6_addr addr) { 50 | static char buffer[INET6_ADDRSTRLEN]; 51 | assert(inet_ntop(AF_INET6, &addr, buffer, sizeof(buffer))); 52 | return buffer; 53 | } 54 | 55 | const char *ether_ntoa(const ether_addr mac) { 56 | static char buffer[32]; 57 | snprintf(buffer, sizeof(buffer), "%02X:%02X:%02X:%02X:%02X:%02X", 58 | mac.ether_addr_octet[0], mac.ether_addr_octet[1], 59 | mac.ether_addr_octet[2], mac.ether_addr_octet[3], 60 | mac.ether_addr_octet[4], mac.ether_addr_octet[5]); 61 | return buffer; 62 | } 63 | 64 | in6_addr inet6_pton(const char *addr) { 65 | in6_addr res; 66 | assert(inet_pton(AF_INET6, addr, &res) == 1); 67 | return res; 68 | } -------------------------------------------------------------------------------- /HAL/src/linux/platform/standard.h: -------------------------------------------------------------------------------- 1 | #include "router_hal.h" 2 | 3 | // for online experiment, don't change 4 | #ifdef ROUTER_PC1 5 | 6 | static const char *interfaces[N_IFACE_ON_BOARD] = { 7 | "pc1r1", 8 | "eth2", 9 | "eth3", 10 | "eth4", 11 | }; 12 | 13 | #elif defined(ROUTER_R1) 14 | 15 | static const char *interfaces[N_IFACE_ON_BOARD] = { 16 | "r1pc1", 17 | "r1r2", 18 | "eth3", 19 | "eth4", 20 | }; 21 | 22 | #elif defined(ROUTER_R2) 23 | 24 | static const char *interfaces[N_IFACE_ON_BOARD] = { 25 | "r2r1", 26 | "r2r3", 27 | "eth3", 28 | "eth4", 29 | }; 30 | 31 | #elif defined(ROUTER_R3) 32 | 33 | static const char *interfaces[N_IFACE_ON_BOARD] = { 34 | "r3r2", 35 | "r3pc2", 36 | "eth3", 37 | "eth4", 38 | }; 39 | 40 | #elif defined(ROUTER_PC2) 41 | 42 | static const char *interfaces[N_IFACE_ON_BOARD] = { 43 | "pc2r3", 44 | "eth2", 45 | "eth3", 46 | "eth4", 47 | }; 48 | 49 | #else 50 | // you can customize this 51 | // configure this to match the output of `ip a` 52 | static const char *interfaces[N_IFACE_ON_BOARD] = { 53 | "r2r1", 54 | "eth2", 55 | "eth3", 56 | "eth4", 57 | }; 58 | #endif 59 | -------------------------------------------------------------------------------- /HAL/src/linux/router_hal.cpp: -------------------------------------------------------------------------------- 1 | #include "router_hal.h" 2 | #include "router_hal_common.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "platform/standard.h" 19 | 20 | extern "C" { 21 | int HAL_Init(HAL_IN int debug, HAL_IN in6_addr if_addrs[N_IFACE_ON_BOARD]) { 22 | if (inited) { 23 | return 0; 24 | } 25 | debugEnabled = debug; 26 | 27 | // find matching interfaces and get their MAC address 28 | struct ifaddrs *ifaddr, *ifa; 29 | if (getifaddrs(&ifaddr) < 0) { 30 | if (debugEnabled) { 31 | fprintf(stderr, "HAL_Init: getifaddrs failed with %s\n", strerror(errno)); 32 | } 33 | return HAL_ERR_UNKNOWN; 34 | } 35 | 36 | for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { 37 | if (ifa->ifa_addr == NULL) 38 | continue; 39 | for (int i = 0; i < N_IFACE_ON_BOARD; i++) { 40 | if (ifa->ifa_addr->sa_family == AF_PACKET && 41 | strcmp(ifa->ifa_name, interfaces[i]) == 0) { 42 | // found 43 | memcpy(&interface_mac[i], 44 | ((struct sockaddr_ll *)ifa->ifa_addr)->sll_addr, 45 | sizeof(ether_addr)); 46 | ndp_table[std::pair(if_addrs[i], i)] = interface_mac[i]; 47 | if (debugEnabled) { 48 | fprintf(stderr, "HAL_Init: found MAC addr of interface %s\n", 49 | interfaces[i]); 50 | } 51 | 52 | // disable ipv6 of this interface 53 | // echo 1 > /proc/sys/net/ipv6/conf/if_name/disable_ipv6 54 | char name_buffer[64]; 55 | snprintf(name_buffer, sizeof(name_buffer), "/proc/sys/net/ipv6/conf/%s/disable_ipv6", 56 | interfaces[i]); 57 | FILE *fp = fopen(name_buffer, "w"); 58 | char ch = '1'; 59 | if (fp) { 60 | fwrite(&ch, 1, 1, fp); 61 | fclose(fp); 62 | 63 | if (debugEnabled) { 64 | fprintf(stderr, "HAL_Init: disabled ipv6 of interface %s\n", 65 | interfaces[i]); 66 | } 67 | } else { 68 | if (debugEnabled) { 69 | fprintf(stderr, "HAL_Init: failed to disable ipv6 of interface %s\n", 70 | interfaces[i]); 71 | } 72 | } 73 | break; 74 | } 75 | } 76 | } 77 | freeifaddrs(ifaddr); 78 | 79 | // init pcap handles 80 | char error_buffer[PCAP_ERRBUF_SIZE]; 81 | for (int i = 0; i < N_IFACE_ON_BOARD; i++) { 82 | pcap_in_handles[i] = 83 | pcap_open_live(interfaces[i], BUFSIZ, 1, 1, error_buffer); 84 | if (pcap_in_handles[i]) { 85 | pcap_setnonblock(pcap_in_handles[i], 1, error_buffer); 86 | if (debugEnabled) { 87 | fprintf(stderr, "HAL_Init: pcap capture enabled for %s\n", 88 | interfaces[i]); 89 | } 90 | } else { 91 | if (debugEnabled) { 92 | fprintf(stderr, 93 | "HAL_Init: pcap capture disabled for %s, either the interface " 94 | "does not exist or permission is denied\n", 95 | interfaces[i]); 96 | } 97 | } 98 | pcap_out_handles[i] = 99 | pcap_open_live(interfaces[i], BUFSIZ, 1, 0, error_buffer); 100 | } 101 | 102 | memcpy(interface_addrs, if_addrs, sizeof(interface_addrs)); 103 | 104 | // generate link local addresses with eui64 105 | for (int i = 0; i < N_IFACE_ON_BOARD; i++) { 106 | interface_link_local_addrs[i] = eui64(interface_mac[i]); 107 | if (debugEnabled) { 108 | fprintf(stderr, 109 | "HAL_Init: interface %d is configured with link local addr %s\n", 110 | i, inet6_ntoa(interface_link_local_addrs[i])); 111 | } 112 | } 113 | 114 | // debug print 115 | if (debugEnabled) { 116 | for (int i = 0; i < N_IFACE_ON_BOARD; i++) { 117 | fprintf(stderr, 118 | "HAL_Init: interface %d is configured with IPv6 addr %s\n", i, 119 | inet6_ntoa(interface_addrs[i])); 120 | } 121 | } 122 | 123 | inited = true; 124 | return 0; 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /HAL/src/macOS/router_hal.cpp: -------------------------------------------------------------------------------- 1 | #include "router_hal.h" 2 | #include "router_hal_common.h" 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | const char *interfaces[N_IFACE_ON_BOARD] = { 22 | "en0", 23 | "en1", 24 | "en2", 25 | "en3", 26 | }; 27 | 28 | extern "C" { 29 | int HAL_Init(HAL_IN int debug, HAL_IN in6_addr if_addrs[N_IFACE_ON_BOARD]) { 30 | if (inited) { 31 | return 0; 32 | } 33 | debugEnabled = debug; 34 | 35 | struct ifaddrs *ifaddr, *ifa; 36 | if (getifaddrs(&ifaddr) < 0) { 37 | if (debugEnabled) { 38 | fprintf(stderr, "HAL_Init: getifaddrs failed with %s\n", strerror(errno)); 39 | } 40 | return HAL_ERR_UNKNOWN; 41 | } 42 | 43 | // ref: 44 | // https://stackoverflow.com/questions/10593736/mac-address-from-interface-on-os-x-c 45 | for (int i = 0; i < N_IFACE_ON_BOARD; i++) { 46 | int index; 47 | if ((index = if_nametoindex(interfaces[i])) == 0) { 48 | if (debugEnabled) { 49 | fprintf(stderr, "HAL_Init: get MAC addr failed for interface %s\n", 50 | interfaces[i]); 51 | } 52 | continue; 53 | } 54 | 55 | int mib[6]; 56 | mib[0] = CTL_NET; 57 | mib[1] = AF_ROUTE; 58 | mib[2] = 0; 59 | mib[3] = AF_LINK; 60 | mib[4] = NET_RT_IFLIST; 61 | mib[5] = index; 62 | 63 | size_t len; 64 | 65 | if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) { 66 | if (debugEnabled) { 67 | fprintf(stderr, "HAL_Init: get MAC addr failed for interface %s\n", 68 | interfaces[i]); 69 | } 70 | continue; 71 | } 72 | char *buf; 73 | 74 | if ((buf = (char *)malloc(len)) == NULL) { 75 | if (debugEnabled) { 76 | fprintf(stderr, "HAL_Init: get MAC addr failed for interface %s\n", 77 | interfaces[i]); 78 | } 79 | continue; 80 | } 81 | 82 | if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) { 83 | if (debugEnabled) { 84 | fprintf(stderr, "HAL_Init: get MAC addr failed for interface %s\n", 85 | interfaces[i]); 86 | } 87 | continue; 88 | } 89 | 90 | struct if_msghdr *ifm = (struct if_msghdr *)buf; 91 | struct sockaddr_dl *sdl = (struct sockaddr_dl *)(ifm + 1); 92 | caddr_t mac = LLADDR(sdl); 93 | // found 94 | memcpy(&interface_mac[i], mac, sizeof(ether_addr)); 95 | ndp_table[std::pair(if_addrs[i], i)] = interface_mac[i]; 96 | if (debugEnabled) { 97 | fprintf(stderr, 98 | "HAL_Init: MAC addr of interface %s is " 99 | "%s\n", 100 | interfaces[i], ether_ntoa(interface_mac[i])); 101 | } 102 | } 103 | 104 | char error_buffer[PCAP_ERRBUF_SIZE]; 105 | for (int i = 0; i < N_IFACE_ON_BOARD; i++) { 106 | pcap_in_handles[i] = 107 | pcap_open_live(interfaces[i], BUFSIZ, 1, 1, error_buffer); 108 | if (pcap_in_handles[i]) { 109 | pcap_setnonblock(pcap_in_handles[i], 1, error_buffer); 110 | if (debugEnabled) { 111 | fprintf(stderr, "HAL_Init: pcap capture enabled for %s\n", 112 | interfaces[i]); 113 | } 114 | } else { 115 | if (debugEnabled) { 116 | fprintf(stderr, 117 | "HAL_Init: pcap capture disabled for %s, either the interface " 118 | "does not exist or permission is denied\n", 119 | interfaces[i]); 120 | } 121 | } 122 | pcap_out_handles[i] = 123 | pcap_open_live(interfaces[i], BUFSIZ, 1, 0, error_buffer); 124 | } 125 | 126 | memcpy(interface_addrs, if_addrs, sizeof(interface_addrs)); 127 | 128 | inited = true; 129 | return 0; 130 | } 131 | } -------------------------------------------------------------------------------- /HAL/src/stdio/router_hal.cpp: -------------------------------------------------------------------------------- 1 | #include "router_hal.h" 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | bool inited = false; 12 | bool outputInited = false; 13 | int debugEnabled = 0; 14 | uint32_t interface_addrs[N_IFACE_ON_BOARD] = {0}; 15 | ether_addr interface_mac[N_IFACE_ON_BOARD] = {0}; 16 | 17 | // input 18 | pcap_t *pcap_handle; 19 | 20 | // output 21 | pcap_t *pcap_out_handle; 22 | pcap_dumper_t *pcap_dumper; 23 | 24 | std::map, ether_addr> ndp_table; 25 | 26 | extern "C" { 27 | int HAL_Init(HAL_IN int debug, HAL_IN in6_addr if_addrs[N_IFACE_ON_BOARD]) { 28 | if (inited) { 29 | return 0; 30 | } 31 | debugEnabled = debug; 32 | 33 | for (int i = 0; i < N_IFACE_ON_BOARD; i++) { 34 | // hard coded MAC 35 | interface_mac[i] = {2, 3, 3, 0, 0, (uint8_t)i}; 36 | ndp_table[std::pair(if_addrs[i], i)] = interface_mac[i]; 37 | } 38 | 39 | char error_buffer[PCAP_ERRBUF_SIZE]; 40 | 41 | // input 42 | pcap_handle = pcap_open_offline("-", error_buffer); 43 | if (!pcap_handle) { 44 | if (debugEnabled) { 45 | fprintf(stderr, "pcap_open_offline failed with %s", error_buffer); 46 | } 47 | return HAL_ERR_UNKNOWN; 48 | } 49 | 50 | memcpy(interface_addrs, if_addrs, sizeof(interface_addrs)); 51 | 52 | inited = true; 53 | return 0; 54 | } 55 | 56 | uint64_t HAL_GetTicks() { 57 | struct timespec tp = {0}; 58 | clock_gettime(CLOCK_MONOTONIC, &tp); 59 | return (uint64_t)tp.tv_sec * 1000 + (uint64_t)tp.tv_nsec / 1000000; 60 | } 61 | 62 | int HAL_GetNeighborMacAddress(int if_index, in6_addr ip, ether_addr *o_mac) { 63 | return HAL_ERR_NOT_SUPPORTED; 64 | } 65 | 66 | int HAL_GetInterfaceMacAddress(int if_index, ether_addr *o_mac) { 67 | return HAL_ERR_NOT_SUPPORTED; 68 | } 69 | 70 | int HAL_ReceiveIPPacket(int if_index_mask, uint8_t *buffer, size_t length, 71 | ether_addr *src_mac, ether_addr *dst_mac, 72 | int64_t timeout, int *if_index) { 73 | if (!inited) { 74 | return HAL_ERR_CALLED_BEFORE_INIT; 75 | } 76 | if ((if_index_mask & ((1 << N_IFACE_ON_BOARD) - 1)) == 0 || 77 | (timeout < 0 && timeout != -1) || (if_index == NULL)) { 78 | return HAL_ERR_INVALID_PARAMETER; 79 | } 80 | 81 | int64_t begin = HAL_GetTicks(); 82 | int64_t current_time = 0; 83 | 84 | struct pcap_pkthdr *hdr; 85 | const u_char *packet; 86 | do { 87 | int res = pcap_next_ex(pcap_handle, &hdr, &packet); 88 | if (res == PCAP_ERROR_BREAK) { 89 | return HAL_ERR_EOF; 90 | } else if (res != 1) { 91 | // retry 92 | continue; 93 | } 94 | 95 | // check 802.1Q 96 | uint32_t vlan = (((uint32_t)packet[14]) << 8) + packet[15]; 97 | vlan &= 0xFFF; 98 | if (packet && hdr->caplen >= sizeof(ether_header) && packet[12] == 0x81 && 99 | packet[13] == 0x00 && 0 < vlan && vlan < N_IFACE_ON_BOARD) { 100 | int current_port = packet[15]; 101 | if (hdr->caplen >= sizeof(ether_header) + 4 && packet[16] == 0x86 && 102 | packet[17] == 0xdd) { 103 | // IPv6 104 | // assuming len == caplen 105 | size_t ip_len = hdr->caplen - sizeof(ether_header) - 4; 106 | size_t real_length = length > ip_len ? ip_len : length; 107 | memcpy(buffer, &packet[sizeof(ether_header) + 4], real_length); 108 | memcpy(dst_mac, &packet[0], sizeof(ether_addr)); 109 | memcpy(src_mac, &packet[6], sizeof(ether_addr)); 110 | *if_index = current_port; 111 | return ip_len; 112 | } 113 | } 114 | 115 | // -1 for infinity 116 | } while ((current_time = HAL_GetTicks()) < begin + timeout || timeout == -1); 117 | return 0; 118 | } 119 | 120 | int HAL_SendIPPacket(HAL_IN int if_index, HAL_IN uint8_t *buffer, 121 | HAL_IN size_t length, HAL_IN ether_addr dst_mac) { 122 | return HAL_ERR_NOT_SUPPORTED; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /Homework/dhcpv6/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | router 3 | std 4 | std.cpp 5 | !Makefile 6 | -------------------------------------------------------------------------------- /Homework/dhcpv6/HONOR-CODE.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thu-cs-lab/Router-Lab/a4ffe2773060e7c630c63d4a08d184562679b5db/Homework/dhcpv6/HONOR-CODE.md -------------------------------------------------------------------------------- /Homework/dhcpv6/common/Makefrag: -------------------------------------------------------------------------------- 1 | CXX ?= g++ 2 | LAB_ROOT ?= ../../.. 3 | BACKEND ?= LINUX 4 | 5 | CXXFLAGS += --std=c++11 -I $(LAB_ROOT)/HAL/include -DROUTER_BACKEND_$(BACKEND) -Wno-psabi -O2 -static -I../../protocol -I../../lookup -I../../internet-checksum -I../../eui64 6 | LDFLAGS += -lpcap 7 | ifeq ($(CI),) 8 | CXXFLAGS += -fsanitize=address 9 | LDFLAGS += -fsanitize=address 10 | else 11 | LDFLAGS += -static 12 | endif 13 | 14 | .PHONY: all clean 15 | all: router 16 | 17 | clean: 18 | rm -f *.o router std 19 | 20 | %.o: ../%.cpp 21 | $(CXX) $(CXXFLAGS) -c $^ -o $@ 22 | 23 | common.o: $(LAB_ROOT)/HAL/src/common.cpp 24 | $(CXX) $(CXXFLAGS) -c $< -o $@ 25 | 26 | hal.o: $(LAB_ROOT)/HAL/src/linux/router_hal.cpp $(LAB_ROOT)/HAL/src/linux/platform/standard.h 27 | $(CXX) $(CXXFLAGS) -c $< -o $@ 28 | 29 | eui64.o: ../../eui64/eui64.cpp 30 | $(CXX) $(CXXFLAGS) -c $< -o $@ 31 | 32 | checksum.o: ../../internet-checksum/checksum.cpp 33 | $(CXX) $(CXXFLAGS) -c $< -o $@ 34 | 35 | lookup.o: ../../lookup/lookup.cpp 36 | $(CXX) $(CXXFLAGS) -c $< -o $@ 37 | 38 | protocol.o: ../../protocol/protocol.cpp 39 | $(CXX) $(CXXFLAGS) -c $< -o $@ 40 | 41 | router: main.o hal.o eui64.o checksum.o lookup.o protocol.o common.o 42 | $(CXX) $^ -o $@ $(LDFLAGS) 43 | -------------------------------------------------------------------------------- /Homework/dhcpv6/custom/Makefile: -------------------------------------------------------------------------------- 1 | include ../common/Makefrag -------------------------------------------------------------------------------- /Homework/dhcpv6/dhcpv6.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct dhcpv6_hdr { 4 | // 1-byte message type 5 | uint8_t msg_type; 6 | // 3-byte transaction id 7 | uint8_t transaction_id_hi; 8 | uint16_t transaction_id_lo; 9 | // options follow 10 | }; -------------------------------------------------------------------------------- /Homework/dhcpv6/r1/Makefile: -------------------------------------------------------------------------------- 1 | CXXFLAGS += -DROUTER_R1 2 | include ../common/Makefrag -------------------------------------------------------------------------------- /Homework/eui64/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | eui64 3 | std 4 | std.cpp 5 | *_output*.txt 6 | !Makefile 7 | -------------------------------------------------------------------------------- /Homework/eui64/HONOR-CODE.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thu-cs-lab/Router-Lab/a4ffe2773060e7c630c63d4a08d184562679b5db/Homework/eui64/HONOR-CODE.md -------------------------------------------------------------------------------- /Homework/eui64/Makefile: -------------------------------------------------------------------------------- 1 | CXX ?= g++ 2 | LAB_ROOT ?= ../.. 3 | CXXFLAGS ?= --std=c++11 -I $(LAB_ROOT)/HAL/include 4 | LDFLAGS ?= 5 | 6 | .PHONY: all clean grade 7 | all: eui64 8 | 9 | clean: 10 | rm -f *.o eui64 std 11 | 12 | grade: eui64 13 | python3 grade.py 14 | 15 | %.o: %.cpp eui64.h 16 | $(CXX) $(CXXFLAGS) -c $< -o $@ 17 | 18 | eui64: eui64.o main.o 19 | $(CXX) $^ -o $@ $(LDFLAGS) 20 | 21 | std: std.o main.o 22 | $(CXX) $^ -o $@ $(LDFLAGS) 23 | -------------------------------------------------------------------------------- /Homework/eui64/README.md: -------------------------------------------------------------------------------- 1 | # eui64 2 | 3 | ## 题目描述 4 | 5 | 在 IPv6 中,Link Local 地址变得比较重要,为了生成一个不会冲突的 Link Local 地址,有一种简单的办法(EUI-64),即利用 MAC 地址的唯一性,通过下面的方法,转换为 Link Local 的 IPv6 地址: 6 | 7 | 1. 输入一个 48 位的 MAC 地址,输出一个 128 位的 IPv6 地址 8 | 2. 设置 IPv6 地址的第 1 个字节为 0xFE,第 2 个字节为 0x80,第 3 到第 8 个字节设为 0。这一步设置了 IPv6 地址属于 Link Local 地址。 9 | 3. 把 MAC 地址分为两半写入 IPv6 地址:将 MAC 地址第 1 到第 3 个字节复制到 IPv6 地址的第 9 到第 11 个字节;再将第 4 到第 6 个字节复制到 IPv6 地址的第 14 到第 16 个字节。 10 | 4. 设置 IPv6 地址的第 12 个字节为 0xFF,第 13 个字节为 0xFE,填充了中间空出来的两个字节数据。 11 | 5. 把 IPv6 地址第 9 个字节按网络位序从左到右第 7 位(见 RFC 4291 中的图示)取反。 12 | 13 | 这个过程在 [RFC 4291](https://datatracker.ietf.org/doc/html/rfc4291) 中定义。 14 | 15 | 你需要在 `eui64.cpp` 文件中实现将这个转换过程实现中在下列的函数中。 16 | 17 | ```cpp 18 | // 函数注释见 eui64.h 19 | in6_addr eui64(const ether_addr mac) { 20 | in6_addr res = {0}; 21 | // TODO 22 | return res; 23 | } 24 | ``` 25 | 26 | `in6_addr` 类型保存了 IPv6 的 128 位地址信息,请通过 `uint8_t s6_addr[16]` 成员来读写,例如 `res.s6_addr[0] = 0xfe;`。类似地,`ether_addr` 类型通过 `uint8_t ether_addr_octet[6]` 成员来读写。如果采用了其他写法(比如 macOS 结构体中的其他名字)会导致 CI 中的编译错误。 27 | 28 | 你不需要处理输入输出,你只需要在本地执行 `make grade` 就可以进行本地评测。 29 | 30 | ## 输入输出文件格式 31 | 32 | 输入文件有若干行,每一行一个 MAC 地址。 33 | 34 | 输出文件有若干行,每行一个 IPv6 地址,为输入文件中每一行 MAC 地址采用 EUI-64 算法生成的 IPv6 地址。 35 | 36 | IPv6 地址采用 `inet_ntop` 函数将 IPv6 地址转换为字符串。简单来说,将 IPv6 地址的 16 个字节分别转换为十六进制,两个为一组,组之间由 `:` 分隔;如果有连续的 0000,那么可以省略。完整的规则见 [Wikipedia](https://en.wikipedia.org/wiki/IPv6_address#Representation)。 37 | 38 | ## 样例 1 39 | 40 | 见 data 目录下的 *eui64_input1.txt* 与 *eui64_answer1.txt*。 41 | 42 | ## 样例 1 解释 43 | 44 | 样例 1 的输入仅有一个 MAC 地址:11:22:33:44:55:66,按照要求的算法进行处理: 45 | 46 | 1. 设置 IPv6 地址的前 8 个字节,得到 fe80:0000:0000:0000:0000:0000:0000:0000。 47 | 2. 把 MAC 地址分为两部分填入,得到 fe80:0000:0000:0000:1122:3300:0044:5566。 48 | 3. 填入中间的空位,得到 fe80:0000:0000:0000:1122:33ff:fe44:5566。 49 | 4. 将 11 的从低到高 2 位取反,即 13,得到 fe80:0000:0000:0000:1322:33ff:fe44:5566。 50 | 5. `main` 函数采用 `inet_ntop` 函数输出 IPv6 地址,连续的零省略为 ::,得到最终结果 fe80::1322:33ff:fe44:5566。 51 | -------------------------------------------------------------------------------- /Homework/eui64/README_en.md: -------------------------------------------------------------------------------- 1 | # eui64 2 | 3 | ## Problem Description 4 | 5 | In IPv6, Link Local addresses become more important. In order to generate a Link Local address that will not conflict, there is a simple way (EUI-64) to convert to a Link Local IPv6 address by using the uniqueness of MAC addresses as follows. 6 | 7 | 1. input a 48-bit MAC address and output a 128-bit IPv6 address 8 | 2. set the first byte of the IPv6 address to 0xFE, the second byte to 0x80, and the third to eighth bytes to 0. This step will make the IPv6 address a Link Local address. 9 | 3. divide the MAC address into two halves to be written into the IPv6 address: copy the 1st to 3rd byte of the MAC address to the 9th to 11th byte of the IPv6 address; then copy the 4th to 6th byte to the 14th to 16th byte of the IPv6 address. 10 | 4. Set the 12th byte of the IPv6 address to 0xFF and the 13th byte to 0xFE to fill the two empty bytes in between. 11 | 5. Invert the 9th byte of the IPv6 address in the network bit order from left to right bit 7 (see the diagram in RFC 4291). 12 | 13 | This procedure is defined in [RFC 4291](https://datatracker.ietf.org/doc/html/rfc4291). 14 | 15 | You need to implement this conversion process in the `eui64.cpp` file in the following functions. 16 | 17 | ```cpp 18 | // See comments of the function in eui64.h 19 | in6_addr eui64(const ether_addr mac) { 20 | in6_addr res = {0}; 21 | // TODO 22 | return res; 23 | } 24 | ``` 25 | 26 | The `in6_addr` type holds 128-bit address information for IPv6, please read and write through the `uint8_t s6_addr[16]` member, e.g. `res.s6_addr[0] = 0xfe;`. Similarly, the `ether_addr` type is read and written via the `uint8_t ether_addr_octet[6]` member. Using other way of writing (e.g. other names in macOS structs) will result in compilation errors in CI. 27 | 28 | You don't need to handle the input and output, you just need to execute `make grade` locally for local evaluation. 29 | 30 | ## Input and output file format 31 | 32 | The input file has a number of lines, one MAC address per line. 33 | 34 | The output file has several lines, one IPv6 address per line, generated using the EUI-64 algorithm for each MAC address line in the input file. 35 | 36 | The IPv6 address is converted to a string using the `inet_ntop` function. In short, each of the 16 bytes of the IPv6 address is converted to hexadecimal, in groups of two, separated by `:`; if there are consecutive 0000s, then they can be omitted. See [Wikipedia](https://en.wikipedia.org/wiki/IPv6_address#Representation) for the full rules. 37 | 38 | ## Sample 1 39 | 40 | See *eui64_input1.txt* and *eui64_answer1.txt* in the data directory. 41 | 42 | ## Example 1 Explanation 43 | 44 | The input of sample 1 has only one MAC address: 11:22:33:44:55:66, and is processed according to the requested algorithm. 45 | 46 | 1. set the first 8 bytes of the IPv6 address to get fe80:0000:0000:0000:0000:0000:0000:0000. 47 | 2. divide the MAC address into two parts and fill them in to get fe80:0000:0000:0000:1122:3300:0044:5566. 48 | 3. Fill in the empty space in the middle to get fe80:0000:0000:0000:1122:33ff:fe44:5566. 49 | 4. Invert the low to high 2 bits of 11, i.e. 13, to get fe80:0000:0000:0000:1322:33ff:fe44:5566. 50 | 5. The `main` function uses the `inet_ntop` function to output the IPv6 address, omitting the consecutive zeros as ::, to get the final result fe80::1322:33ff:fe44:5566. 51 | -------------------------------------------------------------------------------- /Homework/eui64/data/eui64_answer1.txt: -------------------------------------------------------------------------------- 1 | fe80::1322:33ff:fe44:5566 2 | -------------------------------------------------------------------------------- /Homework/eui64/data/eui64_answer2.txt: -------------------------------------------------------------------------------- 1 | fe80::200:ff:fe00:0 2 | -------------------------------------------------------------------------------- /Homework/eui64/data/eui64_answer3.txt: -------------------------------------------------------------------------------- 1 | fe80::fdff:ffff:feff:ffff 2 | -------------------------------------------------------------------------------- /Homework/eui64/data/eui64_answer4.txt: -------------------------------------------------------------------------------- 1 | fe80::221:2fff:feb5:6e10 2 | -------------------------------------------------------------------------------- /Homework/eui64/data/eui64_input1.txt: -------------------------------------------------------------------------------- 1 | 11:22:33:44:55:66 -------------------------------------------------------------------------------- /Homework/eui64/data/eui64_input2.txt: -------------------------------------------------------------------------------- 1 | 00:00:00:00:00:00 -------------------------------------------------------------------------------- /Homework/eui64/data/eui64_input3.txt: -------------------------------------------------------------------------------- 1 | ff:ff:ff:ff:ff:ff -------------------------------------------------------------------------------- /Homework/eui64/data/eui64_input4.txt: -------------------------------------------------------------------------------- 1 | 00:21:2F:B5:6E:10 -------------------------------------------------------------------------------- /Homework/eui64/eui64.cpp: -------------------------------------------------------------------------------- 1 | #include "eui64.h" 2 | #include 3 | #include 4 | 5 | in6_addr eui64(const ether_addr mac) { 6 | in6_addr res = {0}; 7 | // TODO 8 | res.s6_addr[0] = 0xfe; 9 | return res; 10 | } -------------------------------------------------------------------------------- /Homework/eui64/eui64.h: -------------------------------------------------------------------------------- 1 | #ifndef __EUI64_H__ 2 | #define __EUI64_H__ 3 | 4 | #include "common.h" 5 | 6 | /** 7 | * @brief 转换 MAC 地址为 IPv6 地址 8 | * @param mac MAC 地址 9 | * @return IPv6 地址 10 | */ 11 | /** 12 | * @brief Convert MAC address to IPv6 address 13 | * @param mac MAC address 14 | * @return IPv6 address 15 | */ 16 | in6_addr eui64(const ether_addr mac); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /Homework/eui64/grade.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import sys 5 | import os 6 | import json 7 | import subprocess 8 | import time 9 | from os.path import isfile, join 10 | import glob 11 | import traceback 12 | 13 | prefix = 'eui64' 14 | exe = prefix 15 | if len(sys.argv) > 1: 16 | exe = sys.argv[1] 17 | 18 | def write_grade(grade, total): 19 | data = {} 20 | data['grade'] = grade 21 | if os.isatty(1): 22 | print('Passed: {}/{}'.format(grade, total)) 23 | else: 24 | print(json.dumps(data)) 25 | 26 | sys.exit(0) 27 | 28 | 29 | if __name__ == '__main__': 30 | 31 | if sys.version_info[0] != 3: 32 | print("Plz use python3") 33 | sys.exit() 34 | 35 | if os.isatty(1): 36 | print('Removing all output files') 37 | os.system('rm -f data/{}_output*.txt'.format(prefix)) 38 | 39 | total = len(glob.glob("data/{}_input*.txt".format(prefix))) 40 | 41 | grade = 0 42 | 43 | for i in range(1, total+1): 44 | in_file = "data/{}_input{}.txt".format(prefix, i) 45 | out_file = "data/{}_output{}.txt".format(prefix, i) 46 | ans_file = "data/{}_answer{}.txt".format(prefix, i) 47 | 48 | if os.isatty(1): 49 | print('Running \'./{} < {} > {}\''.format(exe, in_file, out_file)) 50 | p = subprocess.Popen(['./{}'.format(exe)], stdout=open(out_file, 'w'), stdin=open(in_file, 'r')) 51 | start_time = time.time() 52 | 53 | while p.poll() is None: 54 | if time.time() - start_time > 1: 55 | p.kill() 56 | 57 | try: 58 | out = [line.strip() for line in open(out_file, 'r').readlines() if line.strip()] 59 | ans = [line.strip() for line in open(ans_file, 'r').readlines() if line.strip()] 60 | 61 | if out == ans: 62 | grade += 1 63 | elif os.isatty(1): 64 | limit = 1 65 | count = 0 66 | print('Wrong Answer:') 67 | print('Diff: ') 68 | os.system('diff -u {} {} | head -n 10'.format(out_file, ans_file)) 69 | except Exception: 70 | if os.isatty(1): 71 | print('Unexpected exception caught:') 72 | traceback.print_exc() 73 | 74 | write_grade(grade, total) 75 | 76 | -------------------------------------------------------------------------------- /Homework/eui64/main.cpp: -------------------------------------------------------------------------------- 1 | #include "eui64.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int main(int argc, char *argv[]) { 10 | ether_addr mac; 11 | uint8_t *mac_bytes = mac.ether_addr_octet; 12 | char buffer[1024]; 13 | while (scanf("%" SCNx8 ":%" SCNx8 ":%" SCNx8 ":%" SCNx8 ":%" SCNx8 ":%" SCNx8, 14 | &mac_bytes[0], &mac_bytes[1], &mac_bytes[2], &mac_bytes[3], 15 | &mac_bytes[4], &mac_bytes[5]) > 0) { 16 | in6_addr ipv6 = eui64(mac); 17 | assert(inet_ntop(AF_INET6, &ipv6, buffer, sizeof(buffer))); 18 | printf("%s\n", buffer); 19 | } 20 | return 0; 21 | } -------------------------------------------------------------------------------- /Homework/internet-checksum/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | checksum 3 | std 4 | std.cpp 5 | *_output*.txt 6 | !Makefile 7 | -------------------------------------------------------------------------------- /Homework/internet-checksum/HONOR-CODE.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thu-cs-lab/Router-Lab/a4ffe2773060e7c630c63d4a08d184562679b5db/Homework/internet-checksum/HONOR-CODE.md -------------------------------------------------------------------------------- /Homework/internet-checksum/Makefile: -------------------------------------------------------------------------------- 1 | CXX ?= g++ 2 | LAB_ROOT ?= ../.. 3 | BACKEND ?= STDIO 4 | CXXFLAGS ?= --std=c++11 -I $(LAB_ROOT)/HAL/include -DROUTER_BACKEND_$(BACKEND) 5 | LDFLAGS ?= -lpcap 6 | 7 | .PHONY: all clean grade 8 | all: checksum 9 | 10 | clean: 11 | rm -f *.o checksum std 12 | 13 | grade: checksum 14 | python3 grade.py 15 | 16 | %.o: %.cpp 17 | $(CXX) $(CXXFLAGS) -c $^ -o $@ 18 | 19 | common.o: $(LAB_ROOT)/HAL/src/common.cpp 20 | $(CXX) $(CXXFLAGS) -c $< -o $@ 21 | 22 | hal.o: $(LAB_ROOT)/HAL/src/stdio/router_hal.cpp 23 | $(CXX) $(CXXFLAGS) -c $^ -o $@ 24 | 25 | checksum: checksum.o main.o hal.o common.o 26 | $(CXX) $^ -o $@ $(LDFLAGS) 27 | 28 | std: std.o main.o hal.o common.o 29 | $(CXX) $^ -o $@ $(LDFLAGS) 30 | -------------------------------------------------------------------------------- /Homework/internet-checksum/README.md: -------------------------------------------------------------------------------- 1 | # internet-checksum 2 | 3 | ## 题目描述 4 | 5 | 你需要在 `checksum.cpp` 中实现下面的函数 `validateAndFillChecksum`,这个函数接收一个 IPv6 的 packet,在 IPv6 Header 之后一定是一个 UDP 或者 ICMPv6 的 packet。该函数的返回值为 UDP 或者 ICMPv6 中的校验和是否正确;同时,无论原来的校验和是否正确,该函数返回时,packet 中的校验和应该被填充为正确的值。 6 | 7 | ```cpp 8 | // 函数注释见 checksum.h 9 | bool validateAndFillChecksum(uint8_t *packet, size_t len) { 10 | return true; 11 | } 12 | ``` 13 | 14 | 在 IPv6 中,Header 不再有校验和(checksum)字段,而在 UDP 和 ICMPv6 协议中计算校验和的时候,需要将 IPv6 Header 的一部分数据考虑进来,这就是 Pseudo Header。其由下面若干部分组成: 15 | 16 | 1. 16 字节的 Source IPv6 Address 17 | 2. 16 字节的 Destination IPv6 Address 18 | 3. 4 字节的 UDP/ICMPv6 Length(别忘了要用网络字节序) 19 | 4. 3 字节的 0,然后是 1 字节的 Next Protocol(对 UDP 来说是 17,对 ICMPv6 来说是 58) 20 | 21 | 如果你对这部分仍有疑惑,可以对照 [UDP Checksum](https://en.wikipedia.org/wiki/User_Datagram_Protocol#IPv6_pseudo_header) 和 [ICMPv6 Checksum](https://en.wikipedia.org/wiki/Internet_Control_Message_Protocol_for_IPv6#Checksum) 网页上的表格进行实现。 22 | 23 | 校验和的检验方式如下: 24 | 25 | 1. 在 IPv6 Pseudo Header 后拼接上 UDP/ICMPv6 packet,拼接后的数据每 16 个二进制位为一组,视作一个大端序的整数。将所有 16 位的整数相加; 26 | 2. 如果上述求和结果发生溢出(二进制表示下超过 16 位),则将其截断为低 16 位及溢出部分,然后将溢出部分加到低 16 位(例如:求和结果为 0x1CB2F,则拆成低 16 位的 0xCB2F 与溢出部分 0x1,并相加 0xCB2F + 0x1); 27 | 3. 如果结果又发生了溢出,则重复上述操作,直到不发生溢出; 28 | 4. 上述结果若等于 0xFFFF,则表示校验和正确,反之错误。 29 | 30 | 特别地,在 UDP 中,校验和字段为 0 表示没有进行校验和的计算。然而,由于 IPv6 头部中没有校验和字段等原因,承载于 IPv6 的 UDP 要求必须进行校验和计算。因此,对于 UDP 而言,若接收方在检查时发现校验和字段为 0,则应该认为其是错误的。 31 | 32 | 由于一个字节占 8 个二进制位,因此校验和检验的步骤 1 中“每 16 个二进制位为一组”可能面临数据不足的情况,比如 UDP/ICMPv6 packet 的长度为奇数个字节(IPv6 Pseudo Header 的长度固定为 40 字节)。将第 1 步的拼接结果后补零至偶数个字节是一种可行的处理方法,下面的校验和计算中也是类似的。 33 | 34 | 校验和的计算方式如下: 35 | 36 | 1. 将校验和字段更改为 0; 37 | 2. 执行校验和检验中的步骤 1、2、3,得到 16 位的结果; 38 | 3. 将得到的 16 位结果按位取反,使用大端序填充到校验和字段,即完成了校验和的计算。 39 | 40 | 与校验和检验中 UDP 的特殊处理一样,由于 UDP 中校验和为 0 有特殊的意义,如果发送方计算出的校验和确实为 0x0000,则也需要将其设置为 0xFFFF,以示区别。对于其他情况(当然这里只有 ICMPv6),当接受到校验和应该为 0x0000 但实际为 0xFFFF 的情况时,函数返回值设为 true(表示校验成功),并将 packet 中的校验和设为 0x0000(改成正确的)。 41 | 42 | 请同学们分析上述两种特殊处理做法的正确性:上述做法是否会混淆校验和为 0x0000 与 0xFFFF 两种情况?对 UDP 校验和的更改能通过校验和的检验吗?又为何要对“应该为 0x0000 但实际为 0xFFFF”的情况特殊处理呢?如果你对这部分有疑惑,可以参考下面“注意事项与说明”中第 2 条的分析。 43 | 44 | 45 | 注意事项及说明: 46 | 47 | 1. 上述对于 checksum 的说明中,我们先给出了校验和的检验方法,而后给出了校验和的计算方法,这种叙述方式是有意为之的。请注意校验和计算方式中的第 1 步,其并非简单地给出了校验和的计算初始值,而是一步需要实际完成的操作:将 UDP/ICMPv6 校验和字段改为 0。完成这一步后,校验和的检验就不能进行了。因此我们建议同学们可以按照上面的过程,逐步进行编写,即使这样的流程可能略显别扭。 48 | 49 | 2. 对于校验和计算中特殊处理方式正确性的进一步讨论:校验和计算与检验方法中有这样一个性质,即按照计算的算法得到的校验和,检验得到的一定是正确的结果,但反之不然。我们可以观察到这么一个事情,即校验和计算方式得到的结果不可能是 0xFFFF:因为在计算的第 2 步过程中,求和的结果不可能是 0x0000,因此在第 3 步取反以后,最终的校验和不可能是 0xFFFF。但如果校验和等于 0xFFFF,按照检验校验和正确的算法,得到的结果依然是正确的。你可以想一想,为什么在这种运算方式下,将 0x0000 替换为 0xFFFF,或者反过来把 0xFFFF 替换为 0x0000 都不影响求和的计算结果,这解释了为什么 UDP 中将结果为 0x0000 的校验和更改为 0xFFFF 是正确的。 50 | 51 | 同学你可能会问,如果大家都用上面的计算方法得到校验和,为何还会出现 0xFFFF 的情况呢?“应该为 0x0000 但实际为 0xFFFF”的情况存在吗?在 [RFC 1141](https://datatracker.ietf.org/doc/html/rfc1141) 中定义的增量更新的算法因为设计上的纰漏,会导致计算出 0xFFFF 的校验和,在之后的 [RFC 1624](https://datatracker.ietf.org/doc/html/rfc1624) 中才得以修复。但很可惜的是,一些系统已经实现了错误的算法。因此目前的网络栈对这个问题的策略是:校验的时候,认为 0xFFFF 是正确的校验和;计算的时候,不会计算出 0xFFFF,因此也就有了上面的特殊处理:当接受到校验和应该为 0x0000 但实际为 0xFFFF 的情况时,函数返回值设为 true,并将 packet 中的校验和设为 0x0000。对于 UDP 而言,由于其校验和为 0 有特殊含义,仍需要按照前文规定的方法将结果为 0x0000 的校验和更改为 0xFFFF 以示区别。 52 | 53 | 3. 作为补充知识,当 UDP 作为 IPv4 的 Payload 时,校验和字段为 0 表示发送方没有进行校验和的计算,此时接收方忽略校验和检查,直接认为校验和是正确的;当校验和字段不为 0 时,接收方同样需要按照上述检验算法进行验证。 54 | 55 | 4. 以上的校验和计算方式和检验方式定义在 [RFC 1071](https://datatracker.ietf.org/doc/html/rfc1071) 中。 56 | 57 | 5. 你可以用一些结构体来简化解析过程:`struct ip6_hdr`、`struct udphdr` 和 `struct icmp6_hdr`,代码中已经提供了一些使用的例子。对于 UDP/ICMPv6 Length 等需要转换字节序的字段,你可以用 `htonl/htons/ntohl/ntohs` 函数来转换字节序。通常的用法是,要写入网络字节序的字段时,用 `htonl/htons` 函数,`hton` 表示 `Host To Network`;反过来,要解析网络字节序的字段的时候,用 `ntohl/ntohs` 函数,`ntoh` 表示 `Network To Host`。 58 | 59 | 6. 你不需要处理输入输出,你只需要在本地执行 `make grade` 就可以进行本地评测。在本题中,保证 packet 中的数据只有 checksum 可能是不合法的。 60 | 61 | # 输入输出格式 62 | 63 | 输入文件是 PCAP 格式,里面含有 n 个 IPv6 packet,main 函数会使用 HAL 读取输入文件中的数据,并通过参数传递给你实现的函数。你可以用 Wireshark 去打开它。 64 | 65 | 输出文件有 2n 行,第 2\*i-1 行一个字符串 Yes/No 表示第 i 个 IPv6 packet 的校验和验证是否正确;第 2\*i 行一个十六进制字符串表示第 i 个 IPv6 packet 进行校验和计算后的数据。 66 | -------------------------------------------------------------------------------- /Homework/internet-checksum/README_en.md: -------------------------------------------------------------------------------- 1 | # internet-checksum 2 | 3 | ## Problem Description 4 | 5 | You need to implement the following function `validateAndFillChecksum` in `checksum.cpp`, which takes an IPv6 packet, which after the IPv6 Header must be a UDP or ICMPv6 packet. The function returns whether the checksum is correct or not; also, the checksum in the packet should be filled with the correct value when the function returns, regardless of whether the original checksum is correct or not. 6 | 7 | ```cpp 8 | // See checksum.h for function comments 9 | bool validateAndFillChecksum(uint8_t *packet, size_t len) { 10 | return true; 11 | } 12 | ``` 13 | 14 | In IPv6, the Header no longer has a checksum field, but when calculating the checksum in UDP and ICMPv6 protocols, a part of the IPv6 Header needs to be taken into account, which is the Pseudo Header, consisting of the following parts. 15 | 16 | 1. 16-byte Source IPv6 Address 17 | 2. 16-byte Destination IPv6 Address 18 | 3. 4 bytes of UDP/ICMPv6 Length (don't forget to use the network byte order) 19 | 4. 3 bytes of 0, followed by 1 byte of Next Protocol (17 for UDP, 58 for ICMPv6) 20 | 21 | If you are still confused about this part, you can compare [UDP Checksum](https://en.wikipedia.org/wiki/User_Datagram_Protocol#IPv6_pseudo_header) and [ICMPv6 Checksum](https://en) .wikipedia.org/wiki/Internet_Control_Message_Protocol_for_IPv6#Checksum) tables on the web page for implementation. 22 | 23 | The checksum is verified as follows. 24 | 25 | 1. Splice the UDP/ICMPv6 packet after the IPv6 Pseudo Header. The spliced data is grouped every 16 binary bits and treated as a big-endian integer. Sum all 16-bit integers. 26 | 2. If the above summation result overflows (more than 16 bits in binary representation), split it into the lower 16 bits and the overflow part, and then add the overflow part to the lower 16 bits (e.g., if the summation result is 0x1CB2F, split it into the lower 16 bits of 0xCB2F and the overflow part 0x1, and add 0xCB2F + 0x1). 27 | 3. If the result overflows again, the above operation is repeated until no overflow occurs. 28 | 4. If the above result is equal to 0xFFFF, the checksum is correct and vice versa. 29 | 30 | In particular, in UDP, a checksum field of 0 means that no checksum calculation is performed. However, due to the absence of a checksum field in the IPv6 header, etc., UDP over IPv6 requires that checksum computation must be performed. Therefore, for UDP, if the receiver finds a checksum field of 0 when checking, it should be considered as an error. 31 | 32 | Since a byte occupies 8 binary bits, the "every 16 binary bits" in step 1 of the checksum check may face insufficient data, for example, if the UDP/ICMPv6 packet length is an odd number of bytes (the length of the IPv6 Pseudo Header is fixed at 40 bytes). It is a feasible way to handle the splicing result in step 1 by adding zero to an even number of bytes, and the same is done in the checksum calculation below. 33 | 34 | The checksum is calculated as follows. 35 | 36 | 1. Change the checksum field to 0. 37 | 2. Perform steps 1, 2 and 3 in the checksum check to obtain the 16-bit result. 38 | 3. Invert the 16-bit result by bit and fill the checksum field using big endian to complete the checksum calculation. 39 | 40 | As with the special handling of UDP in checksum checking, since a checksum of 0 in UDP has a special meaning, if the checksum calculated by the sender is indeed 0x0000, it needs to be set to 0xFFFF as well to show the difference. For other cases (only ICMPv6 here, of course), when the checksum is accepted as 0x0000 but is actually 0xFFFF, the return value of the function is set to true (indicating successful checksum) and the checksum in the packet is set to 0x0000 (changed to the correct one). 41 | 42 | Please analyze the correctness of the above two special handling practices: will the above practices confuse the two cases of checksum 0x0000 and 0xFFFF? Does the change to the UDP checksum pass the checksum test? And why the special treatment for the case of "0x0000 when it should be 0xFFFF"? If you have doubts about this part, you can refer to the analysis of Article 2 in the "Notes and Remarks" below. 43 | 44 | Notes and Remarks: 45 | 46 | 1. In the above description of checksum, we first give the checksum check method and then the checksum calculation method, which is intentional. Please note that step 1 of the checksum calculation method is not simply the initial value of the checksum calculation, but an actual operation that needs to be done: changing the UDP/ICMPv6 checksum field to 0. After this step is done, the checksum check cannot be performed. Therefore, we suggest that students can follow the above process and write it step by step, even though the process may be slightly awkward. 47 | 48 | 2. Further discussion on the correctness of the special treatment in the checksum calculation: There is a property in the checksum calculation and checking method that the checksum obtained according to the algorithm of the calculation must be the correct result from the check, but not vice versa. We can observe that the result of the checksum calculation cannot be 0xFFFF: because the result of the summation in step 2 of the calculation cannot be 0x0000, the final checksum cannot be 0xFFFF after the inversion in step 3, but if the checksum is equal to 0xFFFF, the result obtained by the algorithm for checking the correct checksum is still correct. You can think why replacing 0x0000 with 0xFFFF or vice versa in this operation does not affect the summation result, which explains why it is correct to change the checksum of 0x0000 to 0xFFFF in UDP. 49 | 50 | You may ask, if everyone uses the above calculation method to get the checksum, why does the 0xFFFF situation still occur? Does the case of "0xFFFF when it should be 0x0000" exist? The incremental update algorithm defined in [RFC 1141](https://datatracker.ietf.org/doc/html/rfc1141), due to a design error, results in a checksum of 0xFFFF, which is later fixed in [RFC 1624](https://datatracker.ietf.org/doc/html/rfc1624). But unfortunately, some systems have implemented the wrong algorithm. So the strategy of the current network stack for this problem is: when checking, 0xFFFF is considered as the correct checksum; when calculating, 0xFFFF will not be calculated, so there is a special treatment above: when receiving the checksum that should be 0x0000 but is actually 0xFFFF, the function return value is set to true, and the checksum in the packet is set to 0x0000. For UDP, since the checksum of 0 has a special meaning, it is still necessary to change the checksum of 0x0000 to 0xFFFF as specified in the previous section to show the difference. 51 | 52 | 3. As a supplementary knowledge, when UDP is used as IPv4 Payload, a checksum field of 0 means that the sender does not calculate the checksum, so the receiver ignores the checksum check and considers the checksum correct directly; when the checksum field is not 0, the receiver also needs to verify according to the above check algorithm. 53 | 54 | 4. The above checksum calculation and checking methods are defined in [RFC 1071](https://datatracker.ietf.org/doc/html/rfc1071). 55 | 56 | 5. You can use some structures to simplify the parsing process: `struct ip6_hdr`, `struct udphdr` and `struct icmp6_hdr`, and some examples of their use are provided in the code. For UDP/ICMPv6 Length, you can use the `htonl/htons/ntohl/ntohs` functions to convert the byte order. 57 | 58 | 6. You don't need to handle the input and output, you just need to execute `make grade` locally for local evaluation. In this problem, it is guaranteed that the only data in the packet that may be illegal is checksum. 59 | 60 | # Input and output formats 61 | 62 | The input file is in PCAP format and contains n IPv6 packets. The main function will use HAL to read the data from the input file and pass it to the function you implement with arguments. You can use Wireshark to open it. 63 | 64 | The output file has 2n lines, the 2\*i-1 line is a string Yes/No which indicates whether the checksum of the i IPv6 packet is correct or not; the 2\*i line is a hexadecimal string which indicates the data of the i IPv6 packet after the checksum calculation. 65 | -------------------------------------------------------------------------------- /Homework/internet-checksum/checksum.cpp: -------------------------------------------------------------------------------- 1 | #include "checksum.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | bool validateAndFillChecksum(uint8_t *packet, size_t len) { 8 | // TODO 9 | struct ip6_hdr *ip6 = (struct ip6_hdr *)packet; 10 | 11 | // check next header 12 | uint8_t nxt_header = ip6->ip6_nxt; 13 | if (nxt_header == IPPROTO_UDP) { 14 | // UDP 15 | struct udphdr *udp = (struct udphdr *)&packet[sizeof(struct ip6_hdr)]; 16 | // length: udp->uh_ulen 17 | // checksum: udp->uh_sum 18 | } else if (nxt_header == IPPROTO_ICMPV6) { 19 | // ICMPv6 20 | struct icmp6_hdr *icmp = 21 | (struct icmp6_hdr *)&packet[sizeof(struct ip6_hdr)]; 22 | // length: len-sizeof(struct ip6_hdr) 23 | // checksum: icmp->icmp6_cksum 24 | } else { 25 | assert(false); 26 | } 27 | return true; 28 | } 29 | -------------------------------------------------------------------------------- /Homework/internet-checksum/checksum.h: -------------------------------------------------------------------------------- 1 | #ifndef __CHECKSUM_H__ 2 | #define __CHECKSUM_H__ 3 | 4 | #include "common.h" 5 | 6 | /** 7 | * @brief 进行 IPv6 + UDP/ICMPv6 的校验和的验证和更新 8 | * @param packet 完整的 IPv6 packet 9 | * @param len 即 packet 的长度,单位是字节 10 | * @return 校验和无误则返回 true ,有误则返回 false 11 | */ 12 | /** 13 | * @brief Perform IPv6 + UDP/ICMPv6 checksum validation and update 14 | * @param packet the complete IPv6 packet 15 | * @param len the length of the packet in bytes 16 | * @return returns true if the checksum is correct, false if it is not 17 | */ 18 | bool validateAndFillChecksum(uint8_t *packet, size_t len); 19 | 20 | #endif -------------------------------------------------------------------------------- /Homework/internet-checksum/data/checksum_answer1.txt: -------------------------------------------------------------------------------- 1 | No 2 | 60000000000F11402402F00000010400000000000000000224046800400508200000000000002004D058DEE9000F3F650871C13DA0459B 3 | No 4 | 60000000000C11402402F00000010400000000000000000224046800400508200000000000002004CFF0E615000C45A382D27526 5 | No 6 | 60000000000811402404680040050820000000000000200424046800400508200000000000002004D851CEA30008708D 7 | Yes 8 | 60000000001011402402F0000001040401660111000401002402F000000104040166011100040100D301DCC9001089BBFE10AB7D5B2D8A85 9 | Yes 10 | 60000000001111402402F0000001040000000000000000022402F000000104040166011100040100E660C5710011AD5EE63672B08F37A0F2EA 11 | Yes 12 | 60000000000A11402402F0000001040401660111000401002402F000000104000000000000000002CA14C8F8000A4CA5EC9E 13 | Yes 14 | 60000000000A11402402F00000010400000000000000000224046800400508200000000000002004D870C9D3000AD136802B 15 | Yes 16 | 60000000000E1140240468004005082000000000000020042402F000000104040166011100040100CD00D007000E09F106A496D0ABB3 17 | Yes 18 | 60000000000F1140240468004005082000000000000020042402F000000104040166011100040100E1DCEA44000FB880E308DCBA0DB99E 19 | No 20 | 60000000000B11402402F0000001040000000000000000022402F000000104000000000000000002D10DC8ED000B0297A03993 21 | -------------------------------------------------------------------------------- /Homework/internet-checksum/data/checksum_answer2.txt: -------------------------------------------------------------------------------- 1 | No 2 | 6000000000123A402404680040050820000000000000200424046800400508200000000000002004810036BEEC4843AE8B0C916C4987F346D65A 3 | No 4 | 6000000000123A40240468004005082000000000000020042402F00000010400000000000000000281006D133CC4B3CBFDBAFF7F54E0E5BBDD04 5 | No 6 | 6000000000123A402402F0000001040000000000000000022404680040050820000000000000200481003ACC86D2DE23DEA17DB5DE8884C71315 7 | Yes 8 | 6000000000123A402402F0000001040000000000000000022402F0000001040401660111000401008100DAD0FACF5EAF68CF0290836D305FF7AD 9 | Yes 10 | 6000000000123A402402F0000001040000000000000000022404680040050820000000000000200481004B198A43D4FC7F01B048910B3EA8C927 11 | No 12 | 6000000000123A4024046800400508200000000000002004240468004005082000000000000020048100584C5F0B58CBE4193D193A45F0D139EA 13 | No 14 | 6000000000123A402402F0000001040401660111000401002402F0000001040000000000000000028100E1272D60C5A704476A4129AAC67C184B 15 | Yes 16 | 6000000000123A402402F00000010404016601110004010024046800400508200000000000002004810049E0BD9A6F2F82B0C49147010C015E13 17 | No 18 | 6000000000123A402402F0000001040000000000000000022402F000000104000000000000000002810001210674CE5123F8A05EE74D1BE7B134 19 | Yes 20 | 6000000000123A402402F0000001040000000000000000022402F00000010404016601110004010081007BBF4672252142740CF342765A5E779B 21 | -------------------------------------------------------------------------------- /Homework/internet-checksum/data/checksum_answer3.txt: -------------------------------------------------------------------------------- 1 | Yes 2 | 60000000000F11400000000000000000000000000000000100000000000000000000000000000001C95A1843000FC8C853582359ACB632 3 | No 4 | 60000000000911400000000000000000000000000000000100000000000000000000000000000001E195DC0A0009C5397D 5 | No 6 | 600000000011114000000000000000000000000000000001000000000000000000000000000000011A7B1A730011D58C35F103082ED9037D8A 7 | No 8 | 60000000000E11400000000000000000000000000000000100000000000000000000000000000001D57CB3FD000E3AD2CFA891A3DA37 9 | No 10 | 60000000001211400000000000000000000000000000000100000000000000000000000000000001271027100012FFFF9B9A3CE602CB0909CD53 11 | Yes 12 | 60000000001211400000000000000000000000000000000100000000000000000000000000000001271027100012FFFF9B9A3CE602CB0909CD53 13 | -------------------------------------------------------------------------------- /Homework/internet-checksum/data/checksum_answer4.txt: -------------------------------------------------------------------------------- 1 | Yes 2 | 6000000000123A40000000000000000000000000000000010000000000000000000000000000000181007143E6D47CA33F1233DB17B5EEF4305E 3 | No 4 | 6000000000123A4000000000000000000000000000000001000000000000000000000000000000018100CD8E0DD4D9E536DC54078781ED00CA02 5 | No 6 | 6000000000123A40000000000000000000000000000000010000000000000000000000000000000181001273A8970C7FC24A5CEEDA8449A573C4 7 | No 8 | 6000000000123A4000000000000000000000000000000001000000000000000000000000000000018100D822C32B9780222927E4B7096BF5DED5 9 | Yes 10 | 6000000000123A40000000000000000000000000000000010000000000000000000000000000000181000000D96450F443769C4DA4EC71945E13 11 | Yes 12 | 6000000000123A40000000000000000000000000000000010000000000000000000000000000000181000000D96450F443769C4DA4EC71945E13 13 | -------------------------------------------------------------------------------- /Homework/internet-checksum/data/checksum_input1.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thu-cs-lab/Router-Lab/a4ffe2773060e7c630c63d4a08d184562679b5db/Homework/internet-checksum/data/checksum_input1.pcap -------------------------------------------------------------------------------- /Homework/internet-checksum/data/checksum_input2.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thu-cs-lab/Router-Lab/a4ffe2773060e7c630c63d4a08d184562679b5db/Homework/internet-checksum/data/checksum_input2.pcap -------------------------------------------------------------------------------- /Homework/internet-checksum/data/checksum_input3.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thu-cs-lab/Router-Lab/a4ffe2773060e7c630c63d4a08d184562679b5db/Homework/internet-checksum/data/checksum_input3.pcap -------------------------------------------------------------------------------- /Homework/internet-checksum/data/checksum_input4.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thu-cs-lab/Router-Lab/a4ffe2773060e7c630c63d4a08d184562679b5db/Homework/internet-checksum/data/checksum_input4.pcap -------------------------------------------------------------------------------- /Homework/internet-checksum/grade.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import sys 5 | import os 6 | import json 7 | import subprocess 8 | import time 9 | from os.path import isfile, join 10 | import glob 11 | import traceback 12 | 13 | prefix = 'checksum' 14 | exe = prefix 15 | if len(sys.argv) > 1: 16 | exe = sys.argv[1] 17 | 18 | def write_grade(grade, total): 19 | data = {} 20 | data['grade'] = grade 21 | if os.isatty(1): 22 | print('Passed: {}/{}'.format(grade, total)) 23 | else: 24 | print(json.dumps(data)) 25 | 26 | sys.exit(0) 27 | 28 | 29 | if __name__ == '__main__': 30 | 31 | if sys.version_info[0] != 3: 32 | print("Plz use python3") 33 | sys.exit() 34 | 35 | if os.isatty(1): 36 | print('Removing all output files') 37 | os.system('rm -f data/{}_output*.txt'.format(prefix)) 38 | 39 | total = len(glob.glob("data/{}_input*.pcap".format(prefix))) 40 | 41 | grade = 0 42 | 43 | for i in range(1, total+1): 44 | in_file = "data/{}_input{}.pcap".format(prefix, i) 45 | out_file = "data/{}_output{}.txt".format(prefix, i) 46 | ans_file = "data/{}_answer{}.txt".format(prefix, i) 47 | 48 | if os.isatty(1): 49 | print('Running \'./{} < {} > {}\''.format(exe, in_file, out_file)) 50 | p = subprocess.Popen(['./{}'.format(exe)], stdout=open(out_file, 'w'), stdin=open(in_file, 'r')) 51 | start_time = time.time() 52 | 53 | while p.poll() is None: 54 | if time.time() - start_time > 1: 55 | p.kill() 56 | 57 | try: 58 | out = [line.strip() for line in open(out_file, 'r').readlines() if line.strip()] 59 | ans = [line.strip() for line in open(ans_file, 'r').readlines() if line.strip()] 60 | 61 | if out == ans: 62 | grade += 1 63 | elif os.isatty(1): 64 | limit = 1 65 | count = 0 66 | print('Diff: ') 67 | os.system('diff -u {} {} | head -n 10'.format(out_file, ans_file)) 68 | except Exception: 69 | if os.isatty(1): 70 | print('Unexpected exception caught:') 71 | traceback.print_exc() 72 | 73 | write_grade(grade, total) 74 | 75 | -------------------------------------------------------------------------------- /Homework/internet-checksum/main.cpp: -------------------------------------------------------------------------------- 1 | #include "router_hal.h" 2 | #include 3 | #include 4 | #include 5 | #include "checksum.h" 6 | 7 | in6_addr addrs[N_IFACE_ON_BOARD] = {0}; 8 | uint8_t packet[1024] __attribute__((aligned(4))); 9 | 10 | int main(int argc, char *argv[]) { 11 | int res = HAL_Init(0, addrs); 12 | if (res < 0) { 13 | return res; 14 | } 15 | while (1) { 16 | int mask = (1 << N_IFACE_ON_BOARD) - 1; 17 | ether_addr src_mac; 18 | ether_addr dst_mac; 19 | int if_index; 20 | res = HAL_ReceiveIPPacket(mask, packet, sizeof(packet), &src_mac, &dst_mac, 21 | -1, &if_index); 22 | if (res == HAL_ERR_EOF) { 23 | break; 24 | } else if (res < 0) { 25 | return res; 26 | } 27 | bool correct = validateAndFillChecksum(packet, res); 28 | printf("%s\n", correct ? "Yes" : "No"); 29 | for (int i = 0;i < res;i++) { 30 | printf("%02X", packet[i]); 31 | } 32 | printf("\n"); 33 | } 34 | return 0; 35 | } -------------------------------------------------------------------------------- /Homework/lookup/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | lookup 3 | std 4 | std.cpp 5 | *_output*.txt 6 | !Makefile 7 | -------------------------------------------------------------------------------- /Homework/lookup/HONOR-CODE.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thu-cs-lab/Router-Lab/a4ffe2773060e7c630c63d4a08d184562679b5db/Homework/lookup/HONOR-CODE.md -------------------------------------------------------------------------------- /Homework/lookup/Makefile: -------------------------------------------------------------------------------- 1 | CXX ?= g++ 2 | LAB_ROOT ?= ../.. 3 | CXXFLAGS ?= --std=c++11 -I $(LAB_ROOT)/HAL/include 4 | LDFLAGS ?= 5 | 6 | .PHONY: all clean grade 7 | all: lookup 8 | 9 | clean: 10 | rm -f *.o lookup std 11 | 12 | grade: lookup 13 | python3 grade.py 14 | 15 | %.o: %.cpp 16 | $(CXX) $(CXXFLAGS) -c $^ -o $@ 17 | 18 | common.o: $(LAB_ROOT)/HAL/src/common.cpp 19 | $(CXX) $(CXXFLAGS) -c $< -o $@ 20 | 21 | lookup: lookup.o main.o common.o 22 | $(CXX) $^ -o $@ $(LDFLAGS) 23 | 24 | std: std.o main.o common.o 25 | $(CXX) $^ -o $@ $(LDFLAGS) 26 | -------------------------------------------------------------------------------- /Homework/lookup/README.md: -------------------------------------------------------------------------------- 1 | # lookup 2 | 3 | ## 题目描述 4 | 5 | 这一题要求你实现路由表的更新和查询,包括以下四个函数: 6 | 7 | ```cpp 8 | // 函数注释见 lookup.h 9 | void update(bool insert, RoutingTableEntry entry) { 10 | // TODO 11 | } 12 | 13 | // 函数注释见 lookup.h 14 | bool prefix_query(in6_addr addr, in6_addr *nexthop, uint32_t *if_index) { 15 | // TODO 16 | *nexthop = 0; 17 | *if_index = 0; 18 | return false; 19 | } 20 | 21 | // 函数注释见 lookup.h 22 | int mask_to_len(in6_addr mask) { 23 | // TODO 24 | return -1; 25 | } 26 | 27 | // 函数注释见 lookup.h 28 | in6_addr len_to_mask(int len) { 29 | // TODO 30 | return 0; 31 | } 32 | ``` 33 | 34 | 其中 `RoutingTableEntry` 的定义见 `lookup.h` 文件。 35 | 36 | 你需要实现: 37 | 38 | 1. 路由表的插入和删除 39 | 2. 路由表的查询,返回是否查到,如果查到还需要写入 nexthop 和 if_index 40 | 3. 实现 `mask_to_len` 和 `len_to_mask` 函数,实现前缀长度和地址掩码的转换 41 | 42 | 地址掩码是一种特殊的 IPv6 地址,它的特点是,如果从二进制的视角看,是首先一系列的 1,然后一系列的 0,那么 1 的个数就是前缀长度。比如说,前缀长度 0 对应 `::`,1 对应 `8000::`,4 对应 `f000::`,16 对应 `ffff::`,120 对应 `ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00`,128 对应 `ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff`。 43 | 44 | 在 `data` 目录中提供了评测使用的数据。 45 | 46 | 输入数据一共有 $n$ 行,每一行表示一个操作,第一个字符如果是 `I` ,后面跟了四个空格分隔的字符串对应 `addr` `len` `if_index` 和 `nexthop` ,表示插入;第一个字符如果是 `D` ,后面跟了两个空格分隔的字符串对应 `addr` 和 `len` ,表示删除;第一个字符如果是 `Q` ,后面一个 IPv6 地址表示要查询的 IPv6 地址。 47 | 48 | 输出数据中每一行对应了输入每一行。对于 `I` 或者 `D` 操作,代码会首先判断输入信息中 `addr` 和 `len` 是否合法(即 `addr` 与 `len` 是否构成合法的路由前缀,对应到代码,则是 `(len_to_mask(len) & addr) == addr`),如果不合法就输出 `Invalid`,合法则输出 `Valid` 并进行实际的插入或者删除操作。对于输入的 `Q` 操作,则会查询路由表,如果查询到了,输出 `nexthop` 和 `if_index` ,否则输出 `Not Found` 。你不需要在你的代码中处理输入输出。 49 | 50 | 从这题的数据量来说,实现一个线性查表即可以得到所有分数,我们认为网络课程不是算法课程,不应该在性能上做过高的基本要求。线性查表的实现思路如下: 51 | 52 | 1. 维护一个路由表项的数组 53 | 2. 插入/删除的时候,线性扫描数组里的每一项,检查是否存在。 54 | 3. 最长前缀匹配的时候,线性扫描数组,找到匹配的若干个项,返回其中前缀最长的一个结果。 55 | 56 | 但在后面实际路由器的阶段,线性查表和更优秀的查表算法可能会有性能上的差异。其他的可供参考的实现方法如下: 57 | 58 | 1. 把 IPv6 地址视为一个字母串,每一个字母就是 0/1,用 Trie 实现。 59 | 2. 每一位做一级 Trie 可能查询步骤比较多,可以进行压缩。 60 | 3. Lulea 路由查找算法。 61 | 62 | ## 样例 4 63 | 64 | 见题目目录下的 *lookup_input4.txt* 与 *lookup_answer4.txt*。 65 | 66 | ## 样例 4 解释 67 | 68 | 可以看到这个输入数据中,前两行输入构造了这样的一个路由表: 69 | 70 | ```text 71 | fd00::0102:0300/120 via if9 nexthop fd00::c0a8:0302 72 | fd00::0102:0304/128 via if10 nexthop fd00::c0a8:0901 73 | ``` 74 | 75 | 前三次查询分别查到了第二条、第一条、查不到;删除掉路由表第二条以后,前两次查询匹配到第一条,第三次查不到;路由表删光以后,三次都查不到。 76 | 77 | ## 致谢 78 | 79 | 以下同学对本题目做出了贡献: 80 | 81 | - 计 16 张庆林 82 | - 经 12 - 计 18 李子轩 83 | -------------------------------------------------------------------------------- /Homework/lookup/README_en.md: -------------------------------------------------------------------------------- 1 | # lookup 2 | 3 | ## Problem Description 4 | 5 | This problem requires you to implement routing table updates and lookups, including the following four functions. 6 | 7 | ```cpp 8 | // See lookup.h for function comments 9 | void update(bool insert, RoutingTableEntry entry) { 10 | // TODO 11 | } 12 | 13 | // See lookup.h for function comments 14 | bool prefix_query(in6_addr addr, in6_addr *nexthop, uint32_t *if_index) { 15 | // TODO 16 | *nexthop = 0; 17 | *if_index = 0; 18 | return false; 19 | } 20 | 21 | // See lookup.h for function comments 22 | int mask_to_len(in6_addr mask) { 23 | // TODO 24 | return -1; 25 | } 26 | 27 | // See lookup.h for function comments 28 | in6_addr len_to_mask(int len) { 29 | // TODO 30 | return 0; 31 | } 32 | ``` 33 | 34 | where `RoutingTableEntry` is defined in the `lookup.h` file. 35 | 36 | You need to implement. 37 | 38 | 1. insertion and deletion of routing tables 39 | 2. lookup of routing table, return whether it is found or not, if it is found you also need to write nexthop and if_index 40 | 3. implement `mask_to_len` and `len_to_mask` functions to convert prefix length and address mask 41 | 42 | An address mask is a special kind of IPv6 address that is characterized by a series of 1's followed by a series of 0's if viewed from a binary perspective, so the number of 1's is the prefix length. For example, the prefix length 0 corresponds to `::`, 1 corresponds to `8000::`, 4 corresponds to `f000::`, 16 corresponds to `ffff::`, 120 corresponds to `ffff:fff:fff:fff:fff:fff:fff:fff:ff00`, 128 corresponds to `ffff:fff:fff:fff:fff :fff:fff:fff:fff`. 43 | 44 | The data used for the evaluation is provided in the `data` directory. 45 | 46 | The input data consists of $n$ lines, each line representing an operation. The first character is `I` followed by four space-separated strings corresponding to `addr`, `len`, `if_index` and `nexthop`, indicating insertion; the first character is `D` followed by two space-separated strings corresponding to `addr` and `len`, indicating deletion. If the first character is `Q`, an IPv6 address follows to indicate the IPv6 address to be queried. 47 | 48 | Each line of the output data corresponds to each line of the input. For `I` or `D` operations, the code will first determine whether `addr` and `len` are legal in the input information (when `addr` and `len` form a legal address prefix, they are regarded as legal, which is equivalent to `(len_to_mask(len) & addr) == addr` in C code), and output `Invalid` if they are not legal, or `Valid` if they are legal and perform the actual insert or delete operation. For the input `Q` operation, the routing table is queried, and if it is, `nexthop` and `if_index` are printed, otherwise `Not Found` is printed. You don't need to process the input and output in your code. 49 | 50 | In terms of the amount of data in this question, implementing a linear lookup table means that you can get all the scores. We believe that an online course is not an algorithm course and should not be overly basic in terms of performance. The idea of implementing a linear lookup table is as follows. 51 | 52 | 1. maintain an array of routing table entries 53 | 2. when inserting/deleting, linearly scan each item in the array and check if it exists. 54 | 3. when the longest prefix matches, linearly scan the array, find a number of items that match, and return the result of the one with the longest prefix. 55 | 56 | However, there may be performance differences between linear table lookup and better table lookup algorithms in the later stages of the actual router. Other implementations available for reference are as follows. 57 | 58 | 1. treat the IPv6 address as a string of letters, each of which is 0/1, and implement it with Trie. 59 | 2. Do one level of Trie per bit may have more query steps and can be compressed. 3. 60 | 3. Lulea route lookup algorithm. 61 | 62 | ## Sample 4 63 | 64 | See *lookup_input4.txt* and *lookup_answer4.txt* in the data directory. 65 | 66 | ## Explanation of Example 4 67 | 68 | You can see that the first two lines of input data construct a routing table like this. 69 | 70 | ```text 71 | fd00::0102:0300/120 via if9 nexthop fd00::c0a8:0302 72 | fd00::0102:0304/128 via if10 nexthop fd00::c0a8:0901 73 | ``` 74 | 75 | The first three queries matched the second, the first, and could not be found; after deleting the second entry of the routing table, the first two queries matched the first entry, and the third could not be found; after the routing table was deleted, all three could not be found. -------------------------------------------------------------------------------- /Homework/lookup/data/lookup_answer1.txt: -------------------------------------------------------------------------------- 1 | Valid 2 | Valid 3 | fd00::c0a8:901 10 4 | Valid 5 | Valid 6 | Valid 7 | Valid 8 | Valid 9 | fd00::d0a8:901 7 10 | fd00::c0a8:901 10 11 | fd00::c0a8:302 9 12 | Valid 13 | fd00::d0a8:901 7 14 | fd00::c0a8:302 9 15 | fd00::c0a8:302 9 16 | Valid 17 | fd00::d0a8:901 7 18 | Not Found 19 | Not Found 20 | -------------------------------------------------------------------------------- /Homework/lookup/data/lookup_answer2.txt: -------------------------------------------------------------------------------- 1 | Valid 2 | Valid 3 | fd00::c0a8:302 9 4 | fd00::c0a8:302 9 5 | fd00::c0a8:302 9 6 | fd00::c0a8:901 10 7 | fd00::c0a8:302 9 8 | Valid 9 | fd00::c0a8:901 10 10 | fd00::c0a8:302 9 11 | Valid 12 | fd00::c0a8:901 10 13 | Not Found 14 | Valid 15 | fd00::c0a8:901 10 16 | Not Found 17 | Valid 18 | fd00::c0a8:901 10 19 | Not Found 20 | Valid 21 | Not Found 22 | Not Found 23 | -------------------------------------------------------------------------------- /Homework/lookup/data/lookup_answer3.txt: -------------------------------------------------------------------------------- 1 | Valid 2 | Valid 3 | fd00::c0a8:901 10 4 | fd00::c0a8:302 9 5 | Not Found 6 | Valid 7 | fd00::c0a8:901 10 8 | fd00::c0a8:302 9 9 | fd00::c0a8:302 9 10 | ::1 1 11 | ::1 1 12 | Valid 13 | fd00::c0a8:901 10 14 | fd00::c0a8:302 9 15 | Not Found 16 | Not Found 17 | Valid 18 | Valid 19 | fd00::c0a8:901 10 20 | fd00::c0a8:302 9 21 | ::2 2 22 | ::2 2 23 | Valid 24 | ::3 3 25 | ::2 2 26 | -------------------------------------------------------------------------------- /Homework/lookup/data/lookup_answer4.txt: -------------------------------------------------------------------------------- 1 | Valid 2 | Valid 3 | fd00::c0a8:901 10 4 | fd00::c0a8:302 9 5 | Not Found 6 | Valid 7 | fd00::c0a8:302 9 8 | fd00::c0a8:302 9 9 | Not Found 10 | Valid 11 | Not Found 12 | Not Found 13 | Not Found 14 | -------------------------------------------------------------------------------- /Homework/lookup/data/lookup_answer5.txt: -------------------------------------------------------------------------------- 1 | Valid 2 | Valid 3 | Valid 4 | Valid 5 | Valid 6 | Valid 7 | Valid 8 | ::7 7 9 | ::7 7 10 | ::7 7 11 | ::7 7 12 | ::7 7 13 | ::7 7 14 | ::6 6 15 | ::5 5 16 | ::4 4 17 | ::3 3 18 | ::2 2 19 | ::1 1 20 | Invalid 21 | Invalid 22 | Invalid 23 | Valid 24 | Valid 25 | Valid 26 | Valid 27 | Invalid 28 | Valid 29 | Invalid 30 | Valid 31 | Invalid 32 | Valid 33 | Valid 34 | Invalid 35 | ::3 3 36 | ::1 1 37 | ::7 7 38 | ::5 5 39 | -------------------------------------------------------------------------------- /Homework/lookup/data/lookup_input1.txt: -------------------------------------------------------------------------------- 1 | I fd00::0102:0300 120 9 fd00::c0a8:0302 2 | I fd00::0102:0304 128 10 fd00::c0a8:0901 3 | Q fd00::0102:0304 4 | D fd00::0102:0300 120 5 | D fd00::0102:0304 128 6 | I fd00::8000:0000 97 9 fd00::c0a8:0302 7 | I fd00::8000:0000 98 10 fd00::c0a8:0901 8 | I fd00::8102:0304 128 7 fd00::d0a8:0901 9 | Q fd00::8102:0304 10 | Q fd00::8102:0301 11 | Q fd00::c000:0000 12 | D fd00::8000:0000 98 13 | Q fd00::8102:0304 14 | Q fd00::8102:0301 15 | Q fd00::8000:0000 16 | D fd00::8000:0000 97 17 | Q fd00::8102:0304 18 | Q fd00::8102:0301 19 | Q fd00::8000:0000 -------------------------------------------------------------------------------- /Homework/lookup/data/lookup_input2.txt: -------------------------------------------------------------------------------- 1 | I fd00::0102:0300 120 9 fd00::c0a8:0302 2 | I fd00::0102:0304 128 10 fd00::c0a8:0901 3 | Q fd00::0102:0301 4 | Q fd00::0102:0302 5 | Q fd00::0102:0303 6 | Q fd00::0102:0304 7 | Q fd00::0102:0305 8 | D fd00::0102:0300 128 9 | Q fd00::0102:0304 10 | Q fd00::0102:0305 11 | D fd00::0102:0300 120 12 | Q fd00::0102:0304 13 | Q fd00::0102:0305 14 | D fd00::0102:0300 120 15 | Q fd00::0102:0304 16 | Q fd00::0102:0305 17 | D fd00::0102:0300 120 18 | Q fd00::0102:0304 19 | Q fd00::0102:0305 20 | D fd00::0102:0304 128 21 | Q fd00::0102:0304 22 | Q fd00::0102:0305 -------------------------------------------------------------------------------- /Homework/lookup/data/lookup_input3.txt: -------------------------------------------------------------------------------- 1 | I fd00::0102:0300 120 9 fd00::c0a8:0302 2 | I fd00::0102:0304 128 10 fd00::c0a8:0901 3 | Q fd00::0102:0304 4 | Q fd00::0102:0301 5 | Q :: 6 | I :: 0 1 ::1 7 | Q fd00::0102:0304 8 | Q fd00::0102:0301 9 | Q fd00::0102:0300 10 | Q fd00::0102:0200 11 | Q :: 12 | D :: 0 13 | Q fd00::0102:0304 14 | Q fd00::0102:0301 15 | Q fd00::0102:0200 16 | Q :: 17 | I :: 0 1 ::1 18 | I :: 0 2 ::2 19 | Q fd00::0102:0304 20 | Q fd00::0102:0300 21 | Q fd00::0102:0200 22 | Q :: 23 | I :: 1 3 ::3 24 | Q :: 25 | Q fd00:: -------------------------------------------------------------------------------- /Homework/lookup/data/lookup_input4.txt: -------------------------------------------------------------------------------- 1 | I fd00::0102:0300 120 9 fd00::c0a8:0302 2 | I fd00::0102:0304 128 10 fd00::c0a8:0901 3 | Q fd00::0102:0304 4 | Q fd00::0102:0301 5 | Q :: 6 | D fd00::0102:0304 128 7 | Q fd00::0102:0304 8 | Q fd00::0102:0304 9 | Q :: 10 | D fd00::0102:0300 120 11 | Q fd00::0102:0304 12 | Q fd00::0102:0301 13 | Q :: -------------------------------------------------------------------------------- /Homework/lookup/data/lookup_input5.txt: -------------------------------------------------------------------------------- 1 | I :: 0 1 ::1 2 | I :: 1 2 ::2 3 | I :: 2 3 ::3 4 | I :: 3 4 ::4 5 | I :: 4 5 ::5 6 | I :: 5 6 ::6 7 | I :: 6 7 ::7 8 | Q ::1 9 | Q :: 10 | Q 000f:: 11 | Q 00ff:: 12 | Q 01ff:: 13 | Q 03ff:: 14 | Q 07ff:: 15 | Q 0fff:: 16 | Q 1fff:: 17 | Q 3fff:: 18 | Q 7fff:: 19 | Q ffff:: 20 | I ffff:: 1 1 ::1 21 | I ffff:: 2 1 ::1 22 | I ffff:: 8 1 ::1 23 | I ffff:: 16 1 ::1 24 | I ffff:: 24 1 ::1 25 | I ffff:: 32 1 ::1 26 | I ffff:: 128 1 ::1 27 | I ::1 127 1 ::1 28 | I ::1 128 1 ::1 29 | I ::1 0 6 ::6 30 | I :: 0 5 ::5 31 | I 0::1:0 0 4 ::4 32 | I 0::1:0 128 3 ::3 33 | I 0::1:0 112 2 ::2 34 | I 0::1:0 111 1 ::1 35 | Q ::1:0 36 | Q ::0:1 37 | Q :: 38 | Q fd00:: -------------------------------------------------------------------------------- /Homework/lookup/grade.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import re 5 | import sys 6 | import os 7 | import json 8 | import subprocess 9 | import time 10 | from os.path import isfile, join 11 | import random 12 | import string 13 | import signal 14 | import glob 15 | import traceback 16 | 17 | prefix = 'lookup' 18 | exe = prefix 19 | if len(sys.argv) > 1: 20 | exe = sys.argv[1] 21 | 22 | def write_grade(grade, total): 23 | data = {} 24 | data['grade'] = grade 25 | if os.isatty(1): 26 | print('Passed: {}/{}'.format(grade, total)) 27 | else: 28 | print(json.dumps(data)) 29 | 30 | sys.exit(0) 31 | 32 | 33 | if __name__ == '__main__': 34 | 35 | if sys.version_info[0] != 3: 36 | print("Plz use python3") 37 | sys.exit() 38 | 39 | if os.isatty(1): 40 | print('Removing all output files') 41 | os.system('rm -f data/{}_output*.txt'.format(prefix)) 42 | 43 | total = len(glob.glob("data/{}_input*.txt".format(prefix))) 44 | 45 | grade = 0 46 | 47 | for i in range(1, total+1): 48 | in_file = "data/{}_input{}.txt".format(prefix, i) 49 | out_file = "data/{}_output{}.txt".format(prefix, i) 50 | ans_file = "data/{}_answer{}.txt".format(prefix, i) 51 | 52 | if os.isatty(1): 53 | print('Running \'./{} < {} > {}\''.format(exe, in_file, out_file)) 54 | p = subprocess.Popen(['./{}'.format(exe)], stdout=open(out_file, 'w'), stdin=open(in_file, 'r')) 55 | start_time = time.time() 56 | 57 | while p.poll() is None: 58 | if time.time() - start_time > 1: 59 | p.kill() 60 | 61 | try: 62 | out = [line.strip() for line in open(out_file, 'r').readlines() if line.strip()] 63 | ans = [line.strip() for line in open(ans_file, 'r').readlines() if line.strip()] 64 | 65 | if out == ans: 66 | grade += 1 67 | elif os.isatty(1): 68 | print('Diff: ') 69 | os.system('diff -u {} {} | head -n 10'.format(out_file, ans_file)) 70 | except Exception: 71 | if os.isatty(1): 72 | print('Unexpected exception caught:') 73 | traceback.print_exc() 74 | 75 | write_grade(grade, total) 76 | 77 | -------------------------------------------------------------------------------- /Homework/lookup/lookup.cpp: -------------------------------------------------------------------------------- 1 | #include "lookup.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | void update(bool insert, const RoutingTableEntry entry) { 8 | // TODO 9 | } 10 | 11 | bool prefix_query(const in6_addr addr, in6_addr *nexthop, uint32_t *if_index) { 12 | // TODO 13 | return false; 14 | } 15 | 16 | int mask_to_len(const in6_addr mask) { 17 | // TODO 18 | return -1; 19 | } 20 | 21 | in6_addr len_to_mask(int len) { 22 | // TODO 23 | return {}; 24 | } 25 | -------------------------------------------------------------------------------- /Homework/lookup/lookup.h: -------------------------------------------------------------------------------- 1 | #ifndef __LOOKUP_H__ 2 | #define __LOOKUP_H__ 3 | 4 | #include "common.h" 5 | #include 6 | #include 7 | 8 | /* 9 | 表示路由表的一项。 10 | 保证 addr 和 len 构成合法的网络前缀。 11 | 当 nexthop 为零时这是一条直连路由。 12 | 你可以在全局变量中把路由表以一定的数据结构格式保存下来。 13 | */ 14 | /* 15 | Indicates an entry in the routing table. 16 | Ensures that addr and len form a legal network prefix. 17 | This is a directly connected route when nexthop is zero. 18 | You can keep the routing table in a certain data structure in a global 19 | variable. 20 | */ 21 | typedef struct { 22 | // 匹配的 IPv6 地址前缀 23 | // Matching IPv6 address prefix 24 | in6_addr addr; 25 | // 前缀长度 26 | // Prefix length 27 | uint32_t len; 28 | // 出端口编号 29 | // Egress port number 30 | uint32_t if_index; 31 | // 下一跳的 IPv6 地址 32 | // IPv6 address of the next hop 33 | in6_addr nexthop; 34 | // TODO: 为了实现 RIPng 协议, 35 | // 在 router 作业中需要在这里添加额外的字段保存 metric 36 | // TODO: In order to implement the RIPng protocol, the 37 | // additional fields need to be added here in the router homework to store the 38 | // metric 39 | } RoutingTableEntry; 40 | 41 | /** 42 | * @brief 插入/删除一条路由表表项 43 | * @param insert 如果要插入则为 true ,要删除则为 false 44 | * @param entry 要插入/删除的表项 45 | * 46 | * 插入时如果已经存在一条 addr 和 len 都相同的表项,则替换掉原有的。 47 | * 删除和更新时按照 addr 和 len **精确** 匹配。 48 | */ 49 | /** 50 | * @brief Insert/delete a routing table entry 51 | * @param insert true if you want to insert, false if you want to delete 52 | * @param entry The table entry to be inserted/deleted 53 | * 54 | * If there is already a table entry with the same addr and len when inserting, 55 | * replace the existing one. **Exact** match is required when deleting/updating. 56 | */ 57 | void update(bool insert, const RoutingTableEntry entry); 58 | 59 | /** 60 | * @brief 进行一次路由表的查询,按照最长前缀匹配原则 61 | * @param addr 需要查询的目标地址,网络字节序 62 | * @param nexthop 如果查询到目标,把表项的 nexthop 写入 63 | * @param if_index 如果查询到目标,把表项的 if_index 写入 64 | * @return 查到则返回 true ,没查到则返回 false 65 | */ 66 | /** 67 | * @brief Perform a routing table lookup, following the longest prefix matching 68 | * principle 69 | * @param addr The target address to be queried, in network byte order 70 | * @param nexthop If a match is found, write the nexthop of the table entry 71 | * @param if_index If a match is found, write the if_index of the table entry 72 | * @return true if found, false if not found 73 | */ 74 | bool prefix_query(const in6_addr addr, in6_addr *nexthop, uint32_t *if_index); 75 | 76 | /** 77 | * @brief 转换 mask 为前缀长度 78 | * @param mask 需要转换的 IPv6 mask 79 | * @return mask 合法则返回前缀长度,不合法则返回 -1 80 | */ 81 | /** 82 | * @brief Convert mask to prefix length 83 | * @param mask The IPv6 mask to be converted 84 | * @return returns prefix length if legal, -1 if not 85 | */ 86 | int mask_to_len(const in6_addr mask); 87 | 88 | /** 89 | * @brief 转换前缀长度为 IPv6 mask,前缀长度范围为 [0,128] 90 | * @param len 需要转换的前缀长度 91 | * @return len 合法则返回对应的 mask,不合法则返回 0 92 | */ 93 | /** 94 | * @brief Convert prefix length to IPv6 mask, prefix length range is [0,128] 95 | * @param len The length of the prefix to be converted 96 | * @return If len is legal, return the corresponding mask, if not, return 0 97 | */ 98 | in6_addr len_to_mask(int len); 99 | 100 | #endif -------------------------------------------------------------------------------- /Homework/lookup/main.cpp: -------------------------------------------------------------------------------- 1 | #include "lookup.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | char buffer[1024]; 9 | 10 | int main(int argc, char *argv[]) { 11 | uint32_t len, if_index; 12 | in6_addr addr, nexthop; 13 | char addr_buffer[128]; 14 | char nexthop_buffer[128]; 15 | char tmp; 16 | while (fgets(buffer, sizeof(buffer), stdin)) { 17 | if (buffer[0] == 'I') { 18 | sscanf(buffer, "%c%s%d%d%s", &tmp, addr_buffer, &len, &if_index, 19 | nexthop_buffer); 20 | assert(inet_pton(AF_INET6, addr_buffer, &addr) == 1); 21 | assert(inet_pton(AF_INET6, nexthop_buffer, &nexthop) == 1); 22 | assert(0 <= len && len <= 128); 23 | if ((len_to_mask(len) & addr) != addr || 24 | mask_to_len(len_to_mask(len)) != len) { 25 | printf("Invalid\n"); 26 | } else { 27 | printf("Valid\n"); 28 | RoutingTableEntry entry = { 29 | .addr = addr, .len = len, .if_index = if_index, .nexthop = nexthop}; 30 | update(true, entry); 31 | } 32 | } else if (buffer[0] == 'D') { 33 | sscanf(buffer, "%c%s%d", &tmp, addr_buffer, &len); 34 | assert(inet_pton(AF_INET6, addr_buffer, &addr) == 1); 35 | assert(0 <= len && len <= 128); 36 | if ((len_to_mask(len) & addr) != addr || 37 | mask_to_len(len_to_mask(len)) != len) { 38 | printf("Invalid\n"); 39 | } else { 40 | printf("Valid\n"); 41 | RoutingTableEntry entry = { 42 | .addr = addr, .len = len, .if_index = 0, .nexthop = in6_addr{}}; 43 | update(false, entry); 44 | } 45 | } else if (buffer[0] == 'Q') { 46 | sscanf(buffer, "%c%s", &tmp, addr_buffer); 47 | assert(inet_pton(AF_INET6, addr_buffer, &addr) == 1); 48 | if (prefix_query(addr, &nexthop, &if_index)) { 49 | assert(inet_ntop(AF_INET6, &nexthop, nexthop_buffer, 50 | sizeof(nexthop_buffer))); 51 | printf("%s %d\n", nexthop_buffer, if_index); 52 | } else { 53 | printf("Not Found\n"); 54 | } 55 | } 56 | } 57 | return 0; 58 | } -------------------------------------------------------------------------------- /Homework/ospf/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | router 3 | std 4 | std.cpp 5 | !Makefile 6 | -------------------------------------------------------------------------------- /Homework/ospf/HONOR-CODE.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thu-cs-lab/Router-Lab/a4ffe2773060e7c630c63d4a08d184562679b5db/Homework/ospf/HONOR-CODE.md -------------------------------------------------------------------------------- /Homework/ospf/README.md: -------------------------------------------------------------------------------- 1 | # router 2 | 3 | 在这一阶段,你可以复用前面四个作业的代码,实现一个完整的 OSPF 路由器。你需要修改 `main.cpp` ,在所提供的框架上进行完善。 4 | 5 | 所需要实现的功能请参考代码注释和实验文档。 6 | 7 | ## Makefile 的使用 8 | 9 | 当前目录下,有四个目录,分别对应了不同的路由器配置,其中 `r1-r3` 分别对应在 R1 R2 R3 位置的路由器的配置,`custom` 目录对应自定义的配置,用于调试,配置方法见源代码。在对应目录下运行 `make` 就可以得到对应版本的路由器程序。 10 | 11 | 编译的时候可能会出现如下的 warning: 12 | 13 | ``` 14 | /usr/lib/gcc/aarch64-linux-gnu/6/../../../aarch64-linux-gnu/libpcap.a(nametoaddr.o): In function `pcap_nametoaddrinfo': 15 | (.text+0x94): warning: Using 'getaddrinfo' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking 16 | /usr/lib/gcc/aarch64-linux-gnu/6/../../../aarch64-linux-gnu/libpcap.a(nametoaddr.o): In function `pcap_nametoaddr': 17 | (.text+0x8): warning: Using 'gethostbyname' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking 18 | /usr/lib/gcc/aarch64-linux-gnu/6/../../../aarch64-linux-gnu/libpcap.a(nametoaddr.o): In function `pcap_nametonetaddr': 19 | (.text+0xd8): warning: Using 'getnetbyname' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking 20 | /usr/lib/gcc/aarch64-linux-gnu/6/../../../aarch64-linux-gnu/libpcap.a(nametoaddr.o): In function `pcap_nametoproto': 21 | (.text+0x368): warning: Using 'getprotobyname' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking 22 | /usr/lib/gcc/aarch64-linux-gnu/6/../../../aarch64-linux-gnu/libpcap.a(nametoaddr.o): In function `pcap_nametoport': 23 | (.text+0x124): warning: Using 'getservbyname' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking 24 | ``` 25 | 26 | 这是因为,在 TanLabs 评测中,编译机和评测机环境是不一样的,所以采用了静态编译的方式,而静态编译的话,一些功能会不能使用,不过好消息是,这些功能在路由器里不会用到,所以直接忽略这些 warning 即可。 27 | -------------------------------------------------------------------------------- /Homework/ospf/common/Makefrag: -------------------------------------------------------------------------------- 1 | CXX ?= g++ 2 | LAB_ROOT ?= ../../.. 3 | BACKEND ?= LINUX 4 | 5 | CXXFLAGS += --std=c++11 -I $(LAB_ROOT)/HAL/include -DROUTER_BACKEND_$(BACKEND) -Wno-psabi -static -O2 -I../../protocol-ospf -I../../lookup -I../../internet-checksum -I../../eui64 6 | LDFLAGS += -lpcap 7 | ifeq ($(CI),) 8 | CXXFLAGS += -fsanitize=address 9 | LDFLAGS += -fsanitize=address 10 | else 11 | LDFLAGS += -static 12 | endif 13 | 14 | .PHONY: all clean 15 | all: router 16 | 17 | clean: 18 | rm -f *.o router std 19 | 20 | %.o: ../%.cpp 21 | $(CXX) $(CXXFLAGS) -c $^ -o $@ 22 | 23 | common.o: $(LAB_ROOT)/HAL/src/common.cpp 24 | $(CXX) $(CXXFLAGS) -c $< -o $@ 25 | 26 | hal.o: $(LAB_ROOT)/HAL/src/linux/router_hal.cpp $(LAB_ROOT)/HAL/src/linux/platform/standard.h 27 | $(CXX) $(CXXFLAGS) -c $< -o $@ 28 | 29 | eui64.o: ../../eui64/eui64.cpp 30 | $(CXX) $(CXXFLAGS) -c $< -o $@ 31 | 32 | checksum.o: ../../internet-checksum/checksum.cpp 33 | $(CXX) $(CXXFLAGS) -c $< -o $@ 34 | 35 | lookup.o: ../../lookup/lookup.cpp 36 | $(CXX) $(CXXFLAGS) -c $< -o $@ 37 | 38 | protocol_ospf.o: ../../protocol-ospf/protocol_ospf.cpp 39 | $(CXX) $(CXXFLAGS) -c $< -o $@ 40 | 41 | ospf_lsa_csum.o: ../../protocol-ospf/ospf_lsa_csum.cpp 42 | $(CXX) $(CXXFLAGS) -c $< -o $@ 43 | 44 | router: main.o hal.o eui64.o checksum.o lookup.o protocol_ospf.o ospf_lsa_csum.o common.o 45 | $(CXX) $^ -o $@ $(LDFLAGS) 46 | -------------------------------------------------------------------------------- /Homework/ospf/custom/Makefile: -------------------------------------------------------------------------------- 1 | include ../common/Makefrag -------------------------------------------------------------------------------- /Homework/ospf/interconnect-r2/Makefile: -------------------------------------------------------------------------------- 1 | CXXFLAGS += -DROUTER_R2 -DROUTER_INTERCONNECT 2 | include ../common/Makefrag 3 | -------------------------------------------------------------------------------- /Homework/ospf/r1/Makefile: -------------------------------------------------------------------------------- 1 | CXXFLAGS += -DROUTER_R1 2 | include ../common/Makefrag -------------------------------------------------------------------------------- /Homework/ospf/r2/Makefile: -------------------------------------------------------------------------------- 1 | CXXFLAGS += -DROUTER_R2 2 | include ../common/Makefrag -------------------------------------------------------------------------------- /Homework/ospf/r3/Makefile: -------------------------------------------------------------------------------- 1 | CXXFLAGS += -DROUTER_R3 2 | include ../common/Makefrag -------------------------------------------------------------------------------- /Homework/protocol-ospf/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | protocol_ospf 3 | std 4 | std.cpp 5 | *_output*.txt 6 | !Makefile 7 | -------------------------------------------------------------------------------- /Homework/protocol-ospf/HONOR-CODE.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thu-cs-lab/Router-Lab/a4ffe2773060e7c630c63d4a08d184562679b5db/Homework/protocol-ospf/HONOR-CODE.md -------------------------------------------------------------------------------- /Homework/protocol-ospf/Makefile: -------------------------------------------------------------------------------- 1 | CXX ?= g++ 2 | LAB_ROOT ?= ../.. 3 | BACKEND ?= STDIO 4 | CXXFLAGS ?= --std=c++11 -I $(LAB_ROOT)/HAL/include -DROUTER_BACKEND_$(BACKEND) -I../lookup 5 | LDFLAGS ?= -lpcap 6 | 7 | .PHONY: all clean grade 8 | all: protocol_ospf 9 | 10 | clean: 11 | rm -f *.o protocol_ospf std 12 | 13 | grade: protocol_ospf 14 | python3 grade.py 15 | 16 | %.o: %.cpp protocol_ospf.h 17 | $(CXX) $(CXXFLAGS) -c $< -o $@ 18 | 19 | hal.o: $(LAB_ROOT)/HAL/src/stdio/router_hal.cpp 20 | $(CXX) $(CXXFLAGS) -c $< -o $@ 21 | 22 | common.o: $(LAB_ROOT)/HAL/src/common.cpp 23 | $(CXX) $(CXXFLAGS) -c $< -o $@ 24 | 25 | lookup.o: ../lookup/lookup.cpp 26 | $(CXX) $(CXXFLAGS) -c $< -o $@ 27 | 28 | protocol_ospf: protocol_ospf.o main.o hal.o lookup.o common.o ospf_lsa_csum.o 29 | $(CXX) $^ -o $@ $(LDFLAGS) 30 | 31 | std: std.o main.o hal.o lookup.o common.o 32 | $(CXX) $^ -o $@ $(LDFLAGS) 33 | -------------------------------------------------------------------------------- /Homework/protocol-ospf/README.md: -------------------------------------------------------------------------------- 1 | # protocol 2 | 3 | ## 题目描述 4 | 5 | 这一题要求你实现 [RFC 5340: OSPF for IPv6](https://datatracker.ietf.org/doc/html/rfc5340/) 数据格式的处理,你需要按照 `protocol_ospf.h` 文件中函数的注释,在 `protocol_ospf.cpp` 中实现如下两个函数: 6 | 7 | ```cpp 8 | // 函数注释见 protocol_ospf.h 9 | OspfErrorCode parse_ip(const uint8_t *packet, uint32_t len, const uint8_t **lsa_start, int *lsa_num) { 10 | // TODO 11 | return OspfErrorCode::SUCCESS; 12 | } 13 | 14 | // 函数注释见 protocol_ospf.h 15 | OspfErrorCode disassemble(const uint8_t* lsa, uint16_t buf_len, uint16_t *len, RouterLsa *output) { 16 | // TODO 17 | return OspfErrorCode::SUCCESS; 18 | } 19 | ``` 20 | 21 | 你需要实现: 22 | 23 | 1. 解析 IPv6、OSPF 和 LSU(Link State Update)报文的格式,获取 LSA(Link State Advertisement)的起始位置和数量 24 | 2. 如果上一步得到了合法的数据,解析 LSA 25 | 26 | 需要注意的是,这一题中所有数据都是网络传输的数据格式,所以每一个 OSPF 数据字段都是网络字节序。 27 | 28 | 评测所使用的数据都在 `data` 目录下。输入一共有 $n$ 个 IPv6 分组,`main.cpp` 会调用你的代码来判断每个分组是不是一个合法的 OSPF LS Update 报文。如果合法,则保存下来,输出一行 `Success`。之后是 `lsa_num` 组,每一组先输出 LSA 类型和相应的 LSA 长度。如果 LSA 是 Router-LSA,就进一步输出其 `ls_age` `link_state_id` `advertising_router` 和 `ls_sequence_number`。如果 Router-LSA 含有一些 Entry,则对于每个 Entry,先输出一行 `Entry`,再进一步输出对应的 `type` `metric` `interface_id` `neighbor_interface_id` `neighbor_router_id`。如果不合法,就输出不合法的地方。 29 | 30 | ## 样例 1 31 | 32 | 见题目目录下的 *protocol_ospf_input1.pcap* 与 *protocol_ospf_answer1.txt*。 33 | 34 | ## 样例 1 解释 35 | 36 | 用 Wireshark 打开,可以看到有一个合法的 OSPF 报文,且是一个 LSU 报文。这个 LSU 报文包括了 10 个 LSA,其中有两个是 Router-LSA。由此,只需要将这两个 Router-LSA 中的信息填入对应的结构体即可。 37 | -------------------------------------------------------------------------------- /Homework/protocol-ospf/README_en.md: -------------------------------------------------------------------------------- 1 | # protocol 2 | 3 | ## Problem Description 4 | 5 | This problem requires you to implement the [RFC 5340: OSPF for IPv6](https://datatracker.ietf.org/doc/html/rfc5340/) data format processing. You need to implement the following two functions in `protocol_ospf.cpp`, as annotated in the functions in the `protocol_ospf.h` file: 6 | 7 | ```cpp 8 | // See protocol_ospf.h for the function comments 9 | OspfErrorCode parse_ip(const uint8_t *packet, uint32_t len, const uint8_t **lsa_start, int *lsa_num) { 10 | // TODO 11 | return OspfErrorCode::SUCCESS; 12 | } 13 | 14 | // See protocol_ospf.h for the function comments 15 | OspfErrorCode disassemble(const uint8_t* lsa, uint16_t buf_len, uint16_t *len, RouterLsa *output) { 16 | // TODO 17 | return OspfErrorCode::SUCCESS; 18 | } 19 | ``` 20 | 21 | You need to implement: 22 | 23 | 1. parse the IPv6, OSPF, and LSU (Link State Update) packet formats to get the starting position and number of LSAs (Link State Advertisements) 24 | 2. if the previous step gets legal data, parse the LSAs 25 | 26 | Note that all data in this problem is in network transmission format, so each OSPF data field is in network byte order. 27 | 28 | The data used for the judgment are in the `data` directory. The input has a total of $n$ IPv6 packets, and `main.cpp` calls your code to determine if each packet is a legitimate OSPF packet. If it is legal, save it and output a line `Success`. After that, there are `lsa_num` groups, each group first outputs the LSA type and the corresponding LSA length. If the LSA is a Router-LSA, then further output its `ls_age` `link_state_id` `advertising_router` and `ls_sequence_number`. If the Router-LSA contains some Entries, for each Entry, first output a line `Entry`, and then further output the corresponding `type` `metric` `interface_id` `neighbor_interface_id` `neighbor_router_id`. If it is not legal, output the illegal part. 29 | 30 | ## Sample 1 31 | 32 | See *protocol_ospf_input1.pcap* and *protocol_ospf_answer1.txt* in the data directory. 33 | 34 | ## Sample 1 Explanation 35 | 36 | Open it with Wireshark and you can see that there is a legitimate OSPF packet, which is an LSU packet. This LSU packet includes 10 LSAs, two of which are Router-LSAs. Therefore, you only need to fill in the information in these two Router-LSAs into the corresponding structure. -------------------------------------------------------------------------------- /Homework/protocol-ospf/data/protocol_ospf_answer1.txt: -------------------------------------------------------------------------------- 1 | Success 2 | RouterLSA: 24 3 | ls_age: 12 4 | link_state_id: 0x00000000 5 | advertising_router: 0x0a000000 6 | ls_sequence_number: 0x80000001 7 | Other LSA: 84 8 | Other LSA: 44 9 | Other LSA: 36 10 | Other LSA: 44 11 | RouterLSA: 24 12 | ls_age: 13 13 | link_state_id: 0x00000000 14 | advertising_router: 0x0a000002 15 | ls_sequence_number: 0x80000001 16 | Other LSA: 84 17 | Other LSA: 36 18 | Other LSA: 44 19 | Other LSA: 44 20 | -------------------------------------------------------------------------------- /Homework/protocol-ospf/data/protocol_ospf_answer2.txt: -------------------------------------------------------------------------------- 1 | Success 2 | RouterLSA: 56 3 | ls_age: 1 4 | link_state_id: 0x00000000 5 | advertising_router: 0x0a000001 6 | ls_sequence_number: 0x80000002 7 | Entry 8 | type: 1 9 | metric: 10 10 | interface_id: 86 11 | neighbor_interface_id: 85 12 | neighbor_router_id: 0x0a000002 13 | Entry 14 | type: 1 15 | metric: 10 16 | interface_id: 81 17 | neighbor_interface_id: 82 18 | neighbor_router_id: 0x0a000000 19 | -------------------------------------------------------------------------------- /Homework/protocol-ospf/data/protocol_ospf_input1.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thu-cs-lab/Router-Lab/a4ffe2773060e7c630c63d4a08d184562679b5db/Homework/protocol-ospf/data/protocol_ospf_input1.pcap -------------------------------------------------------------------------------- /Homework/protocol-ospf/data/protocol_ospf_input2.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thu-cs-lab/Router-Lab/a4ffe2773060e7c630c63d4a08d184562679b5db/Homework/protocol-ospf/data/protocol_ospf_input2.pcap -------------------------------------------------------------------------------- /Homework/protocol-ospf/grade.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import re 5 | import sys 6 | import os 7 | import json 8 | import subprocess 9 | import time 10 | from os.path import isfile, join 11 | import random 12 | import string 13 | import signal 14 | import glob 15 | import traceback 16 | 17 | prefix = 'protocol_ospf' 18 | exe = prefix 19 | if len(sys.argv) > 1: 20 | exe = sys.argv[1] 21 | 22 | def write_grade(grade, total): 23 | data = {} 24 | data['grade'] = grade 25 | if os.isatty(1): 26 | print('Passed: {}/{}'.format(grade, total)) 27 | else: 28 | print(json.dumps(data)) 29 | 30 | sys.exit(0) 31 | 32 | 33 | if __name__ == '__main__': 34 | 35 | if sys.version_info[0] != 3: 36 | print("Plz use python3") 37 | sys.exit() 38 | 39 | if os.isatty(1): 40 | print('Removing all output files') 41 | os.system('rm -f data/{}_output*.txt'.format(prefix)) 42 | 43 | total = len(glob.glob("data/{}_input*.pcap".format(prefix))) 44 | 45 | grade = 0 46 | 47 | for i in range(1, total+1): 48 | in_file = "data/{}_input{}.pcap".format(prefix, i) 49 | out_file = "data/{}_output{}.txt".format(prefix, i) 50 | ans_file = "data/{}_answer{}.txt".format(prefix, i) 51 | 52 | if os.isatty(1): 53 | print('Running \'./{} < {} > {}\''.format(exe, in_file, out_file)) 54 | p = subprocess.Popen(['./{}'.format(exe)], stdout=open(out_file, 'w'), stdin=open(in_file, 'r')) 55 | start_time = time.time() 56 | 57 | while p.poll() is None: 58 | if time.time() - start_time > 1: 59 | p.kill() 60 | 61 | try: 62 | out = [line.strip() for line in open(out_file, 'r').readlines() if line.strip()] 63 | ans = [line.strip() for line in open(ans_file, 'r').readlines() if line.strip()] 64 | 65 | if out == ans: 66 | grade += 1 67 | elif os.isatty(1): 68 | print('Diff: ') 69 | os.system('diff -u {} {} | head -n 10'.format(out_file, ans_file)) 70 | except Exception: 71 | if os.isatty(1): 72 | print('Unexpected exception caught:') 73 | traceback.print_exc() 74 | 75 | write_grade(grade, total) 76 | -------------------------------------------------------------------------------- /Homework/protocol-ospf/main.cpp: -------------------------------------------------------------------------------- 1 | #include "protocol_ospf.h" 2 | #include "router_hal.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | uint8_t buffer[2048] __attribute__((aligned(4))); 10 | uint8_t packet[2048] __attribute__((aligned(4))); 11 | in6_addr addrs[N_IFACE_ON_BOARD] = {0}; 12 | char addr_buffer[1024]; 13 | 14 | int main(int argc, char *argv[]) { 15 | int res = HAL_Init(0, addrs); 16 | if (res < 0) { 17 | return res; 18 | } 19 | while (1) { 20 | int mask = (1 << N_IFACE_ON_BOARD) - 1; 21 | ether_addr src_mac; 22 | ether_addr dst_mac; 23 | int if_index; 24 | res = HAL_ReceiveIPPacket(mask, packet, sizeof(packet), &src_mac, &dst_mac, 25 | -1, &if_index); 26 | if (res == HAL_ERR_EOF) { 27 | break; 28 | } else if (res < 0) { 29 | return res; 30 | } 31 | const uint8_t *lsa_start; 32 | int lsa_num; 33 | OspfErrorCode err = parse_ip(packet, res, &lsa_start, &lsa_num); 34 | if (err == OspfErrorCode::SUCCESS) { 35 | printf("Success\n"); 36 | } else { 37 | printf("Error: %s\n", ospf_error_to_string(err)); 38 | continue; 39 | } 40 | res -= (lsa_start - packet); 41 | for (int i = 0; i < lsa_num; i++) { 42 | uint16_t lsa_len; 43 | RouterLsa lsa; 44 | OspfErrorCode err = disassemble(lsa_start, res, &lsa_len, &lsa); 45 | 46 | if (err == OspfErrorCode::SUCCESS) { 47 | printf("RouterLSA: %u\n", lsa_len); 48 | printf(" ls_age: %u\n", lsa.ls_age); 49 | printf(" link_state_id: 0x%08x\n", lsa.link_state_id); 50 | printf(" advertising_router: 0x%08x\n", lsa.advertising_router); 51 | printf(" ls_sequence_number: 0x%08x\n", lsa.ls_sequence_number); 52 | for (auto entry: lsa.entries) { 53 | printf(" Entry\n"); 54 | printf(" type: %u\n", entry.type); 55 | printf(" metric: %u\n", entry.metric); 56 | printf(" interface_id: %u\n", entry.interface_id); 57 | printf(" neighbor_interface_id: %u\n", entry.neighbor_interface_id); 58 | printf(" neighbor_router_id: 0x%08x\n", entry.neighbor_router_id); 59 | } 60 | } else if (err == ERR_LSA_NOT_ROUTER) { 61 | printf("Other LSA: %u\n", lsa_len); 62 | } else if (err == ERR_PACKET_TOO_SHORT) { 63 | printf("Error: %s\n", ospf_error_to_string(err)); 64 | break; 65 | } else { 66 | printf("Error: %s\n", ospf_error_to_string(err)); 67 | } 68 | 69 | res -= lsa_len; 70 | lsa_start += lsa_len; 71 | } 72 | } 73 | return 0; 74 | } -------------------------------------------------------------------------------- /Homework/protocol-ospf/ospf_lsa_csum.cpp: -------------------------------------------------------------------------------- 1 | #include "protocol_ospf.h" 2 | #include 3 | 4 | uint16_t ospf_lsa_checksum(struct ospf_lsa_header *lsa, size_t length) { 5 | // RFC 905 B.3 ALGORITHM FOR GENERATING CHECKSUM PARAMETERS & B.4 ALGORITHM 6 | // FOR CHECKING CHECKSUM PARAMETERS 7 | uint8_t *buff = (uint8_t *)&lsa->ls_type; 8 | length -= buff - (uint8_t *)lsa; 9 | 10 | // "Addition is performed in one of the two following modes: 11 | // a) modulo 255 arithmetic; 12 | // b) one's complement arithmetic in which if any of the 13 | // variables has the value minus zero (i.e. 255) it shall be 14 | // regarded as though it was plus zero (i.e. 0)." 15 | 16 | // For generation: 17 | // "B.3 Set up the complete TPDU with the value of the checksum 18 | // parameter field set to zero." 19 | 20 | // "Initialize C0 and C1 to zero." 21 | uint16_t c0 = 0, c1 = 0; 22 | // "n number (i.e. position) of the first octet of the checksum 23 | // parameter" 24 | size_t n = (uint8_t *)&lsa->ls_checksum - buff + 1; 25 | 26 | // "Process each octet sequentially from i = 1 to L by: 27 | // a) adding the value of the octet to C0; then 28 | // b) adding the value of C0 to C1." 29 | for (size_t i = 0; i < length; ++i) { 30 | c0 += buff[i]; 31 | c0 = (c0 >> 8) + (c0 & 0xff); 32 | c1 += c0; 33 | c1 = (c1 >> 8) + (c1 & 0xff); 34 | } 35 | 36 | // "Calculate X and Y such that 37 | // X = -C1 + (L-n).CO 38 | // Y = C1 - (L-n+1).C0" 39 | uint32_t x = (length - n) * c0; 40 | x = (x >> 16) + (x & 0xffff); 41 | x = (x >> 16) + (x & 0xffff); 42 | x = (x >> 8) + (x & 0xff); 43 | x = (x >> 8) + (x & 0xff); 44 | x += ~c1 & 0xff; 45 | x = (x >> 8) + (x & 0xff); 46 | if (x == 0xff) 47 | x = 0; 48 | uint32_t y = (length - n + 1) * c0; 49 | y = (y >> 16) + (y & 0xffff); 50 | y = (y >> 16) + (y & 0xffff); 51 | y = (y >> 8) + (y & 0xff); 52 | y = (y >> 8) + (y & 0xff); 53 | y = c1 + (~y & 0xff); 54 | y = (y >> 8) + (y & 0xff); 55 | if (y == 0xff) 56 | y = 0; 57 | 58 | // B.3 59 | // "Place the values X and Y in octets n and (n + 1) 60 | // respectively."" 61 | // For generation: save X and Y in LSA checksum 62 | 63 | // B.4 64 | // "If, when all the octets have been processed, either or 65 | // both of C0 and C1 does not have the value zero, the checksum 66 | // formulas in 6.17 have not been satisfied." 67 | // For verification: check if X == 0 and Y == 0 68 | return x | y << 8; 69 | } -------------------------------------------------------------------------------- /Homework/protocol-ospf/protocol_ospf.cpp: -------------------------------------------------------------------------------- 1 | #include "protocol_ospf.h" 2 | #include "common.h" 3 | #include "lookup.h" 4 | #include 5 | #include 6 | #include 7 | 8 | OspfErrorCode parse_ip(const uint8_t *packet, uint32_t len, 9 | const uint8_t **lsa_start, int *lsa_num) { 10 | // TODO 11 | return OspfErrorCode::SUCCESS; 12 | } 13 | 14 | OspfErrorCode disassemble(const uint8_t *lsa, uint16_t buf_len, uint16_t *len, 15 | RouterLsa *output) { 16 | // TODO 17 | return OspfErrorCode::SUCCESS; 18 | } -------------------------------------------------------------------------------- /Homework/protocol/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | protocol 3 | std 4 | std.cpp 5 | *_output*.txt 6 | !Makefile 7 | -------------------------------------------------------------------------------- /Homework/protocol/HONOR-CODE.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thu-cs-lab/Router-Lab/a4ffe2773060e7c630c63d4a08d184562679b5db/Homework/protocol/HONOR-CODE.md -------------------------------------------------------------------------------- /Homework/protocol/Makefile: -------------------------------------------------------------------------------- 1 | CXX ?= g++ 2 | LAB_ROOT ?= ../.. 3 | BACKEND ?= STDIO 4 | CXXFLAGS ?= --std=c++11 -I $(LAB_ROOT)/HAL/include -DROUTER_BACKEND_$(BACKEND) -I../lookup 5 | LDFLAGS ?= -lpcap 6 | 7 | .PHONY: all clean grade 8 | all: protocol 9 | 10 | clean: 11 | rm -f *.o protocol std 12 | 13 | grade: protocol 14 | python3 grade.py 15 | 16 | %.o: %.cpp protocol.h 17 | $(CXX) $(CXXFLAGS) -c $< -o $@ 18 | 19 | hal.o: $(LAB_ROOT)/HAL/src/stdio/router_hal.cpp 20 | $(CXX) $(CXXFLAGS) -c $< -o $@ 21 | 22 | common.o: $(LAB_ROOT)/HAL/src/common.cpp 23 | $(CXX) $(CXXFLAGS) -c $< -o $@ 24 | 25 | lookup.o: ../lookup/lookup.cpp 26 | $(CXX) $(CXXFLAGS) -c $< -o $@ 27 | 28 | protocol: protocol.o main.o hal.o lookup.o common.o 29 | $(CXX) $^ -o $@ $(LDFLAGS) 30 | 31 | std: std.o main.o hal.o lookup.o common.o 32 | $(CXX) $^ -o $@ $(LDFLAGS) 33 | -------------------------------------------------------------------------------- /Homework/protocol/README.md: -------------------------------------------------------------------------------- 1 | # protocol 2 | 3 | ## 题目描述 4 | 5 | 这一题要求你实现 [RFC 2080: RIPng for IPv6](https://datatracker.ietf.org/doc/html/rfc2080) 数据格式的处理,你需要按照 `protocol.h` 文件中函数的注释,在 `protocol.cpp` 中实现如下两个函数: 6 | 7 | ```cpp 8 | // 函数注释见 protocol.h 9 | RipngErrorCode disassemble(const uint8_t *packet, uint32_t len, RipngPacket *output) { 10 | // TODO 11 | return RipngErrorCode::SUCCESS; 12 | } 13 | 14 | // 函数注释见 protocol.h 15 | uint32_t assemble(const RipngPacket *ripng, uint8_t *buffer) { 16 | // TODO 17 | return 0; 18 | } 19 | ``` 20 | 21 | 你需要实现: 22 | 23 | 1. 解析 IPv6、UDP 和 RIPng 的格式,把有用的信息保存下来 24 | 2. 如果上一步得到了合法的数据,从保存下来的信息恢复出 RIPng 的传输格式 25 | 26 | 需要注意的是,这一题中所有数据都是网络传输的数据格式,所以每一个 RIPng 数据字段都是网络字节序,见 `protocol.h` 中的注释。 27 | 28 | 评测所使用的数据都在 `data` 目录下。输入一共有 $n$ 个 IPv6 分组,`main.cpp` 会调用你的代码来判断每个分组是不是一个合法的 RIPng 报文,如果是,则保存下来,输出一行 `Valid {numEntries} {command}`,之后是 `numEntries` 行,每一行的输出分别对应 `prefix_or_nh` `route_tag` `prefix_len` 和 `metric`,再输出一行,是重新构造出来的 RIPng 报文,用十六进制格式输出;如果不合法,就输出不合法的地方。 29 | 30 | ## 样例 1 31 | 32 | 见题目目录下的 *protocol_input1.pcap* 与 *protocol_answer1.txt*。 33 | 34 | ## 样例 1 解释 35 | 36 | 用 Wireshark 打开,可以看到有两个合法的 RIPng packet,它们的内容如下: 37 | 38 | ```text 39 | RIPng 40 | Command: Request (1) 41 | Version: 1 42 | Reserved: 0000 43 | RIPng 44 | Command: Response (2) 45 | Version: 1 46 | Reserved: 0000 47 | ``` 48 | 49 | 可以看到都是合法的 RIPng packet。把对应的内容填入结构体即可,需要注意字节序。 50 | -------------------------------------------------------------------------------- /Homework/protocol/README_en.md: -------------------------------------------------------------------------------- 1 | # protocol 2 | 3 | ## Problem Description 4 | 5 | This problem requires you to implement the [RFC 2080: RIPng for IPv6](https://datatracker.ietf.org/doc/html/rfc2080) data format processing. You need to implement the following two functions in `protocol.cpp`, as annotated in the functions in the `protocol.h` file. 6 | 7 | ```cpp 8 | // See protocol.h for the function comments 9 | RipngErrorCode disassemble(const uint8_t *packet, uint32_t len, RipngPacket *output) { 10 | // TODO 11 | return RipngErrorCode::SUCCESS; 12 | } 13 | 14 | // See protocol.h for the function comments 15 | uint32_t assemble(const RipngPacket *ripng, uint8_t *buffer) { 16 | // TODO 17 | return 0; 18 | } 19 | ``` 20 | 21 | You need to implement. 22 | 23 | 1. parse the IPv6, UDP and RIPng data and save the useful information 24 | 2. recover the contents of RIPng from the saved information if you got the legal data in the previous step 25 | 26 | Note that all data in this problem is in network transmission format, so each RIPng data field is in network byte order, see the note in `protocol.h`. 27 | 28 | The data used for the evaluation are in the `data` directory. The input has a total of $n$ IPv6 packets, and `main.cpp` calls your code to determine if each packet is a legitimate RIPng message, and if so, saves it and outputs a line `Valid {numEntries} {command}`, followed by `numEntries` lines, each of which corresponds to `prefix_or_nh` `route_tag` `prefix_len` and `metric`, and then a line of output, which is the reconstructed RIPng message, in hexadecimal format; if it is not legal, the illegal part is printed. 29 | 30 | ## Sample 1 31 | 32 | See *protocol_input1.pcap* and *protocol_answer1.txt* in the data directory. 33 | 34 | ## Example 1 Explanation 35 | 36 | Open it with Wireshark and you can see that there are two legitimate RIPng packets, which look like this 37 | 38 | ```text 39 | RIPng 40 | Command: Request (1) 41 | Version: 1 42 | Reserved: 0000 43 | RIPng 44 | Command: Response (2) 45 | Version: 1 46 | Reserved: 0000 47 | ``` 48 | 49 | You can see that they are all legal RIPng packets, just fill the corresponding contents into the structure, and pay attention to the byte order. 50 | -------------------------------------------------------------------------------- /Homework/protocol/data/protocol_answer1.txt: -------------------------------------------------------------------------------- 1 | Valid 0 1 2 | 01 01 00 00 3 | Valid 0 2 4 | 02 01 00 00 5 | -------------------------------------------------------------------------------- /Homework/protocol/data/protocol_answer10.txt: -------------------------------------------------------------------------------- 1 | Valid 0 1 2 | 01 01 00 00 3 | Valid 71 1 4 | :: 0 0 1 5 | :: 0 0 1 6 | :: 1 0 1 7 | :: 2 0 1 8 | :: 3 0 1 9 | :: 4 0 1 10 | :: 5 0 1 11 | :: 6 0 1 12 | :: 7 0 1 13 | :: 8 0 1 14 | :: 9 0 1 15 | :: 10 0 1 16 | :: 11 0 1 17 | :: 12 0 1 18 | :: 13 0 1 19 | :: 14 0 1 20 | :: 15 0 1 21 | :: 16 0 1 22 | :: 17 0 1 23 | :: 18 0 1 24 | :: 19 0 1 25 | :: 20 0 1 26 | :: 21 0 1 27 | :: 22 0 1 28 | :: 23 0 1 29 | :: 24 0 1 30 | :: 25 0 1 31 | :: 26 0 1 32 | :: 27 0 1 33 | :: 28 0 1 34 | :: 29 0 1 35 | :: 30 0 1 36 | :: 31 0 1 37 | :: 32 0 1 38 | :: 33 0 1 39 | :: 34 0 1 40 | :: 35 0 1 41 | :: 36 0 1 42 | :: 37 0 1 43 | :: 38 0 1 44 | :: 39 0 1 45 | :: 40 0 1 46 | :: 41 0 1 47 | :: 42 0 1 48 | :: 43 0 1 49 | :: 44 0 1 50 | :: 45 0 1 51 | :: 46 0 1 52 | :: 47 0 1 53 | :: 48 0 1 54 | :: 49 0 1 55 | :: 50 0 1 56 | :: 51 0 1 57 | :: 52 0 1 58 | :: 53 0 1 59 | :: 54 0 1 60 | :: 55 0 1 61 | :: 56 0 1 62 | :: 57 0 1 63 | :: 58 0 1 64 | :: 59 0 1 65 | :: 60 0 1 66 | :: 61 0 1 67 | :: 62 0 1 68 | :: 63 0 1 69 | :: 64 0 1 70 | :: 65 0 1 71 | :: 66 0 1 72 | :: 67 0 1 73 | :: 68 0 1 74 | :: 69 0 1 75 | 01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 03 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 05 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 06 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 07 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 09 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0a 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0b 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0c 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0d 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0e 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0f 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 11 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 12 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 13 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 14 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 15 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 16 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 17 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 18 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 19 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1a 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1b 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1c 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1d 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1e 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1f 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 20 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 21 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 22 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 23 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 24 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 25 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 26 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 27 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 28 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 29 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 2a 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 2b 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 2c 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 2d 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 2e 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 2f 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 30 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 31 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 32 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 33 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 34 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 35 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 36 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 37 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 38 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 39 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3a 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3b 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3c 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3d 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3e 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3f 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 40 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 41 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 42 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 43 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 44 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 45 00 01 76 | -------------------------------------------------------------------------------- /Homework/protocol/data/protocol_answer11.txt: -------------------------------------------------------------------------------- 1 | UDP port is not 521 2 | Command field is bad 3 | -------------------------------------------------------------------------------- /Homework/protocol/data/protocol_answer12.txt: -------------------------------------------------------------------------------- 1 | Route tag field is bad 2 | Valid 1 1 3 | ::1 0 0 255 4 | 01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 ff 5 | -------------------------------------------------------------------------------- /Homework/protocol/data/protocol_answer2.txt: -------------------------------------------------------------------------------- 1 | Version field is bad 2 | Zero(Reserved) field is bad 3 | -------------------------------------------------------------------------------- /Homework/protocol/data/protocol_answer3.txt: -------------------------------------------------------------------------------- 1 | Valid 1 1 2 | :: 0 0 1 3 | 01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 4 | Metric field is bad 5 | -------------------------------------------------------------------------------- /Homework/protocol/data/protocol_answer4.txt: -------------------------------------------------------------------------------- 1 | Prefix field is inconsistent with Prefix len field 2 | Prefix len field is bad 3 | -------------------------------------------------------------------------------- /Homework/protocol/data/protocol_answer5.txt: -------------------------------------------------------------------------------- 1 | Metric field is bad 2 | Valid 1 1 3 | :: 0 0 255 4 | 01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff 5 | -------------------------------------------------------------------------------- /Homework/protocol/data/protocol_answer6.txt: -------------------------------------------------------------------------------- 1 | Prefix field is inconsistent with Prefix len field 2 | Valid 2 1 3 | fd00:: 0 8 1 4 | f000:: 0 8 1 5 | 01 01 00 00 fd 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 01 f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 08 01 6 | -------------------------------------------------------------------------------- /Homework/protocol/data/protocol_answer7.txt: -------------------------------------------------------------------------------- 1 | UDP port is not 521 2 | IP next header field is not UDP 3 | -------------------------------------------------------------------------------- /Homework/protocol/data/protocol_answer8.txt: -------------------------------------------------------------------------------- 1 | Length is inconsistent 2 | Valid 1 1 3 | :: 1 0 1 4 | 01 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 01 5 | -------------------------------------------------------------------------------- /Homework/protocol/data/protocol_answer9.txt: -------------------------------------------------------------------------------- 1 | Length is inconsistent 2 | Length is inconsistent 3 | -------------------------------------------------------------------------------- /Homework/protocol/data/protocol_input1.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thu-cs-lab/Router-Lab/a4ffe2773060e7c630c63d4a08d184562679b5db/Homework/protocol/data/protocol_input1.pcap -------------------------------------------------------------------------------- /Homework/protocol/data/protocol_input10.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thu-cs-lab/Router-Lab/a4ffe2773060e7c630c63d4a08d184562679b5db/Homework/protocol/data/protocol_input10.pcap -------------------------------------------------------------------------------- /Homework/protocol/data/protocol_input11.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thu-cs-lab/Router-Lab/a4ffe2773060e7c630c63d4a08d184562679b5db/Homework/protocol/data/protocol_input11.pcap -------------------------------------------------------------------------------- /Homework/protocol/data/protocol_input12.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thu-cs-lab/Router-Lab/a4ffe2773060e7c630c63d4a08d184562679b5db/Homework/protocol/data/protocol_input12.pcap -------------------------------------------------------------------------------- /Homework/protocol/data/protocol_input2.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thu-cs-lab/Router-Lab/a4ffe2773060e7c630c63d4a08d184562679b5db/Homework/protocol/data/protocol_input2.pcap -------------------------------------------------------------------------------- /Homework/protocol/data/protocol_input3.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thu-cs-lab/Router-Lab/a4ffe2773060e7c630c63d4a08d184562679b5db/Homework/protocol/data/protocol_input3.pcap -------------------------------------------------------------------------------- /Homework/protocol/data/protocol_input4.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thu-cs-lab/Router-Lab/a4ffe2773060e7c630c63d4a08d184562679b5db/Homework/protocol/data/protocol_input4.pcap -------------------------------------------------------------------------------- /Homework/protocol/data/protocol_input5.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thu-cs-lab/Router-Lab/a4ffe2773060e7c630c63d4a08d184562679b5db/Homework/protocol/data/protocol_input5.pcap -------------------------------------------------------------------------------- /Homework/protocol/data/protocol_input6.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thu-cs-lab/Router-Lab/a4ffe2773060e7c630c63d4a08d184562679b5db/Homework/protocol/data/protocol_input6.pcap -------------------------------------------------------------------------------- /Homework/protocol/data/protocol_input7.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thu-cs-lab/Router-Lab/a4ffe2773060e7c630c63d4a08d184562679b5db/Homework/protocol/data/protocol_input7.pcap -------------------------------------------------------------------------------- /Homework/protocol/data/protocol_input8.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thu-cs-lab/Router-Lab/a4ffe2773060e7c630c63d4a08d184562679b5db/Homework/protocol/data/protocol_input8.pcap -------------------------------------------------------------------------------- /Homework/protocol/data/protocol_input9.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thu-cs-lab/Router-Lab/a4ffe2773060e7c630c63d4a08d184562679b5db/Homework/protocol/data/protocol_input9.pcap -------------------------------------------------------------------------------- /Homework/protocol/grade.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | import re 5 | import sys 6 | import os 7 | import json 8 | import subprocess 9 | import time 10 | from os.path import isfile, join 11 | import random 12 | import string 13 | import signal 14 | import glob 15 | import traceback 16 | 17 | prefix = 'protocol' 18 | exe = prefix 19 | if len(sys.argv) > 1: 20 | exe = sys.argv[1] 21 | 22 | def write_grade(grade, total): 23 | data = {} 24 | data['grade'] = grade 25 | if os.isatty(1): 26 | print('Passed: {}/{}'.format(grade, total)) 27 | else: 28 | print(json.dumps(data)) 29 | 30 | sys.exit(0) 31 | 32 | 33 | if __name__ == '__main__': 34 | 35 | if sys.version_info[0] != 3: 36 | print("Plz use python3") 37 | sys.exit() 38 | 39 | if os.isatty(1): 40 | print('Removing all output files') 41 | os.system('rm -f data/{}_output*.txt'.format(prefix)) 42 | 43 | total = len(glob.glob("data/{}_input*.pcap".format(prefix))) 44 | 45 | grade = 0 46 | 47 | for i in range(1, total+1): 48 | in_file = "data/{}_input{}.pcap".format(prefix, i) 49 | out_file = "data/{}_output{}.txt".format(prefix, i) 50 | ans_file = "data/{}_answer{}.txt".format(prefix, i) 51 | 52 | if os.isatty(1): 53 | print('Running \'./{} < {} > {}\''.format(exe, in_file, out_file)) 54 | p = subprocess.Popen(['./{}'.format(exe)], stdout=open(out_file, 'w'), stdin=open(in_file, 'r')) 55 | start_time = time.time() 56 | 57 | while p.poll() is None: 58 | if time.time() - start_time > 1: 59 | p.kill() 60 | 61 | try: 62 | out = [line.strip() for line in open(out_file, 'r').readlines() if line.strip()] 63 | ans = [line.strip() for line in open(ans_file, 'r').readlines() if line.strip()] 64 | 65 | if out == ans: 66 | grade += 1 67 | elif os.isatty(1): 68 | print('Diff: ') 69 | os.system('diff -u {} {} | head -n 10'.format(out_file, ans_file)) 70 | except Exception: 71 | if os.isatty(1): 72 | print('Unexpected exception caught:') 73 | traceback.print_exc() 74 | 75 | write_grade(grade, total) 76 | 77 | -------------------------------------------------------------------------------- /Homework/protocol/main.cpp: -------------------------------------------------------------------------------- 1 | #include "protocol.h" 2 | #include "router_hal.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | uint8_t buffer[2048] __attribute__((aligned(4))); 10 | uint8_t packet[2048] __attribute__((aligned(4))); 11 | RipngPacket ripng; 12 | in6_addr addrs[N_IFACE_ON_BOARD] = {0}; 13 | char addr_buffer[1024]; 14 | 15 | int main(int argc, char *argv[]) { 16 | int res = HAL_Init(0, addrs); 17 | if (res < 0) { 18 | return res; 19 | } 20 | while (1) { 21 | int mask = (1 << N_IFACE_ON_BOARD) - 1; 22 | ether_addr src_mac; 23 | ether_addr dst_mac; 24 | int if_index; 25 | res = HAL_ReceiveIPPacket(mask, packet, sizeof(packet), &src_mac, &dst_mac, 26 | -1, &if_index); 27 | if (res == HAL_ERR_EOF) { 28 | break; 29 | } else if (res < 0) { 30 | return res; 31 | } 32 | RipngErrorCode err = disassemble(packet, res, &ripng); 33 | if (err == RipngErrorCode::SUCCESS) { 34 | printf("Valid %d %d\n", ripng.numEntries, ripng.command); 35 | for (int i = 0; i < ripng.numEntries; i++) { 36 | assert(inet_ntop(AF_INET6, &ripng.entries[i].prefix_or_nh, addr_buffer, 37 | sizeof(addr_buffer))); 38 | printf("%s %d %d %d\n", addr_buffer, htons(ripng.entries[i].route_tag), 39 | ripng.entries[i].prefix_len, ripng.entries[i].metric); 40 | } 41 | uint32_t len = assemble(&ripng, buffer); 42 | for (uint32_t i = 0; i < len; i++) { 43 | printf("%02x ", buffer[i]); 44 | } 45 | printf("\n"); 46 | } else { 47 | printf("%s\n", ripng_error_to_string(err)); 48 | } 49 | } 50 | return 0; 51 | } -------------------------------------------------------------------------------- /Homework/protocol/protocol.cpp: -------------------------------------------------------------------------------- 1 | #include "protocol.h" 2 | #include "common.h" 3 | #include "lookup.h" 4 | #include 5 | #include 6 | #include 7 | 8 | RipngErrorCode disassemble(const uint8_t *packet, uint32_t len, 9 | RipngPacket *output) { 10 | // TODO 11 | return RipngErrorCode::SUCCESS; 12 | } 13 | 14 | uint32_t assemble(const RipngPacket *ripng, uint8_t *buffer) { 15 | // TODO 16 | return 0; 17 | } -------------------------------------------------------------------------------- /Homework/protocol/protocol.h: -------------------------------------------------------------------------------- 1 | #include "common.h" 2 | #include 3 | 4 | // disassemble 函数的返回值定义如下 5 | // 如果同时出现多种错误,返回满足下面错误描述的第一条错误 6 | // The return value of the disassemble function is defined as follows 7 | // If multiple errors occur at the same time, the first error that satisfies the 8 | // following error description is returned 9 | enum RipngErrorCode { 10 | // 没有错误 11 | // No errors 12 | SUCCESS = 0, 13 | // IPv6 头中 next header 字段不是 UDP 协议 14 | // The next header field in the IPv6 header is not UDP protocol 15 | ERR_IPV6_NEXT_HEADER_NOT_UDP, 16 | // UDP 头中源端口号或者目的端口号不是 521(RIPng) 17 | // The source or destination port number in the UDP header is not 521 (RIPng) 18 | ERR_UDP_PORT_NOT_RIPNG, 19 | // IPv6 头、UDP 头和实际的 RIPng 路由项的长度出现错误或者不一致 20 | // The length of the IPv6 header, UDP header and the actual RIPng routing 21 | // entry are incorrect or do not match 22 | ERR_LENGTH, 23 | // RIPng 的 Command 字段错误 24 | // The Command field of RIPng is wrong 25 | ERR_RIPNG_BAD_COMMAND, 26 | // RIPng 的 Version 字段错误 27 | // The Version field of RIPng is wrong 28 | ERR_RIPNG_BAD_VERSION, 29 | // RIPng 的 Zero(Reserved)字段错误 30 | // The Zero(Reserved) field of RIPng is wrong 31 | ERR_RIPNG_BAD_ZERO, 32 | // RIPng 表项的 Metric 字段错误 33 | // Wrong Metric field in RIPng table entry 34 | ERR_RIPNG_BAD_METRIC, 35 | // RIPng 表项的 Prefix Len 字段错误 36 | // Wrong Prefix Len field in RIPng table entry 37 | ERR_RIPNG_BAD_PREFIX_LEN, 38 | // RIPng 表项的 Route Tag 字段错误 39 | // Wrong Route Tag field in RIPng table entry 40 | ERR_RIPNG_BAD_ROUTE_TAG, 41 | // RIPng 表项的 Prefix 和 Prefix Len 字段不符合要求 42 | // The Prefix and Prefix Len fields of the RIPng table entry are inconsistent 43 | ERR_RIPNG_INCONSISTENT_PREFIX_LENGTH, 44 | }; 45 | 46 | // RIPng header 定义 47 | // RIPng header definition 48 | // https://datatracker.ietf.org/doc/html/rfc2080#page-5 49 | // "The RIPng packet format is:" 50 | // 0 1 2 3 51 | // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 52 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 53 | // | command (1) | version (1) | must be zero (2) | 54 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 55 | // | | 56 | // ~ Route Table Entry 1 (20) ~ 57 | // | | 58 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 59 | // | | 60 | // ~ ... ~ 61 | // | | 62 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 63 | // | | 64 | // ~ Route Table Entry N (20) ~ 65 | // | | 66 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 67 | typedef struct ripng_hdr { 68 | // 1 表示 request,2 表示 response 69 | // 1 for request, 2 for response 70 | uint8_t command; 71 | // 应当为 1 72 | // should be 1 73 | uint8_t version; 74 | // 应当为 0 75 | // should be 0 76 | uint16_t zero; 77 | } ripng_hdr; 78 | 79 | // (1500-40-8-4)/20=72 80 | // https://datatracker.ietf.org/doc/html/rfc2080#page-7 81 | // "The maximum datagram size is limited by the MTU of the medium over 82 | // which the protocol is being used. Since an unsolicited RIPng update 83 | // is never propagated across a router, there is no danger of an MTU 84 | // mismatch. The determination of the number of RTEs which may be put 85 | // into a given message is a function of the medium's MTU, the number of 86 | // octets of header information preceeding the RIPng message, the size 87 | // of the RIPng header, and the size of an RTE. The formula is:" 88 | // +- -+ 89 | // | MTU - sizeof(IPv6_hdrs) - UDP_hdrlen - RIPng_hdrlen | 90 | // #RTEs = INT | --------------------------------------------------- | 91 | // | RTE_size | 92 | // +- -+ 93 | #define RIPNG_MAX_RTE 72 94 | 95 | // RIPng entry 定义 96 | // RIPng entry definition 97 | // https://datatracker.ietf.org/doc/html/rfc2080#page-6 98 | // "Route Table Entry (RTE) has the following format:" 99 | // 0 1 2 3 100 | // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 101 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 102 | // | | 103 | // ~ IPv6 prefix (16) ~ 104 | // | | 105 | // +---------------------------------------------------------------+ 106 | // | route tag (2) | prefix len (1)| metric (1) | 107 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 108 | typedef struct { 109 | // 当 Metric=0xFF 时记录了 Nexthop;当 Metric!=0xFF 时记录了 Prefix 110 | // Nexthop is recorded when Metric=0xFF; Prefix is recorded when Metric!=0xFF 111 | // "A next hop RTE is identified by a value of 0xFF in the metric field 112 | // of an RTE. The prefix field specifies the IPv6 address of the next 113 | // hop. The route tag and prefix length in the next hop RTE must be set 114 | // to zero on sending and ignored on receiption." 115 | in6_addr prefix_or_nh; 116 | // 网络字节序存储 117 | // Saved in network byte order 118 | uint16_t route_tag; 119 | uint8_t prefix_len; 120 | uint8_t metric; 121 | } ripng_rte; 122 | 123 | typedef struct { 124 | uint32_t numEntries; 125 | // 1 表示 request,2 表示 response 126 | // 1 for request, 2 for response 127 | uint8_t command; 128 | // 不用存储 `version`,因为它总是 1 129 | // No need to store `version`, because it is always 1 130 | // 不用存储 `zero`,因为它总是 0 131 | // no need to store `zero`, because it is always 0 132 | ripng_rte entries[RIPNG_MAX_RTE]; 133 | } RipngPacket; 134 | 135 | static const char *ripng_error_to_string(RipngErrorCode err) { 136 | switch (err) { 137 | case RipngErrorCode::ERR_IPV6_NEXT_HEADER_NOT_UDP: 138 | return "IP next header field is not UDP"; 139 | case RipngErrorCode::ERR_UDP_PORT_NOT_RIPNG: 140 | return "UDP port is not 521"; 141 | case RipngErrorCode::ERR_LENGTH: 142 | return "Length is inconsistent"; 143 | case RipngErrorCode::ERR_RIPNG_BAD_COMMAND: 144 | return "Command field is bad"; 145 | case RipngErrorCode::ERR_RIPNG_BAD_VERSION: 146 | return "Version field is bad"; 147 | case RipngErrorCode::ERR_RIPNG_BAD_ZERO: 148 | return "Zero(Reserved) field is bad"; 149 | case RipngErrorCode::ERR_RIPNG_BAD_METRIC: 150 | return "Metric field is bad"; 151 | case RipngErrorCode::ERR_RIPNG_BAD_PREFIX_LEN: 152 | return "Prefix len field is bad"; 153 | case RipngErrorCode::ERR_RIPNG_BAD_ROUTE_TAG: 154 | return "Route tag field is bad"; 155 | case RipngErrorCode::ERR_RIPNG_INCONSISTENT_PREFIX_LENGTH: 156 | return "Prefix field is inconsistent with Prefix len field"; 157 | default: 158 | return "Unknown error code"; 159 | } 160 | } 161 | 162 | /* 163 | 你需要从 IPv6 packet 中解析出 RipngPacket 结构体,也要从 RipngPacket 164 | 结构体构造出对应的 IPv6 packet。 由于 RIPng 本身不记录表项的个数,需要从 IPv6 165 | header 的长度中推断,所以在 RipngPacket 中额外记录了个数。 166 | */ 167 | /* 168 | You need to extract the RipngPacket structure from the IPv6 packet, and also 169 | construct the corresponding IPv6 packet from the RipngPacket. Since RIPng 170 | itself does not record the number of table entries, you need to infer it from 171 | the length of the IPv6 header, so the number is additionally recorded in the 172 | RipngPacket. 173 | */ 174 | 175 | /** 176 | * @brief 从接受到的 IPv6 packet 解析出 RIPng 协议的数据 177 | * @param packet 接受到的 IPv6 分组 178 | * @param len 即 packet 的长度 179 | * @param output 把解析结果写入 *output 180 | * @return 如果输入是一个合法的 RIPng 报文,把它的内容写入 RipngPacket 并且返回 181 | * RipngErrorCode::SUCCESS;否则按照要求返回 RipngErrorCode 的具体类型 182 | * 183 | * 你需要按照以下顺序检查: 184 | * 1. len 是否不小于一个 IPv6 header 的长度。 185 | * 2. IPv6 Header 中的 Payload Length 加上 Header 长度是否等于 len。 186 | * 3. IPv6 Header 中的 Next header 字段是否为 UDP 协议。 187 | * 4. IPv6 Header 中的 Payload Length 是否包括一个 UDP header 的长度。 188 | * 5. 检查 UDP 源端口和目的端口是否都为 521。 189 | * 6. 检查 UDP header 中 Length 是否等于 UDP header 长度加上 RIPng header 190 | * 长度加上 RIPng entry 长度的整数倍。 191 | * 7. 检查 RIPng header 中的 Command 是否为 1 或 2, 192 | * Version 是否为 1,Zero(Reserved) 是否为 0。 193 | * 8. 对每个 RIPng entry,当 Metric=0xFF 时,检查 Prefix Len 194 | * 和 Route Tag 是否为 0。 195 | * 9. 对每个 RIPng entry,当 Metric!=0xFF 时,检查 Metric 是否属于 196 | * [1,16],并检查 Prefix Len 是否属于 [0,128],Prefix Len 是否与 IPv6 prefix 197 | * 字段组成合法的 IPv6 前缀。 198 | */ 199 | /** 200 | * @brief Parse the RIPng protocol data from the received IPv6 packet 201 | * @param packet The received IPv6 packet 202 | * @param len the length of the packet 203 | * @param output writes the parsed result to *output 204 | * @return If the input is a legitimate RIPng message, write its content to the 205 | * RipngPacket and return RipngErrorCode::SUCCESS; otherwise return the specific 206 | * type of RipngErrorCode as required 207 | * 208 | * You need to check in the following order: 209 | * 1. Whether len is not less than the length of an IPv6 header. 210 | * 2. Whether the Payload Length plus the Header Length in the IPv6 Header is 211 | * equal to len. 212 | * 3. Whether the Next header field in the IPv6 Header is UDP protocol. 213 | * 4. Whether the Payload Length in the IPv6 Header includes the length of a UDP 214 | * header. 215 | * 5. Check if the UDP source port and destination port are both 521. 216 | * 6. Check that the Length in the UDP header is equal to the UDP header length 217 | * plus the RIPng header length plus the integer multiple of the RIPng entry 218 | * length. 219 | * 7. Check if Command in the RIPng header is 1 or 2. Version is 1, and Zero 220 | * (Reserved) is 0. 221 | * 8. For each RIPng entry, when Metric=0xFF, check if Prefix Len 222 | * and Route Tag is 0. 223 | * 9. For each RIPng entry, when Metric!=0xFF, check if Metric belongs to 224 | * [1,16], and check if Prefix Len belongs to [0,128], and check if Prefix Len 225 | * and IPv6 prefix form a legal IPv6 prefix. 226 | */ 227 | RipngErrorCode disassemble(const uint8_t *packet, uint32_t len, 228 | RipngPacket *output); 229 | 230 | /** 231 | * @brief 从 RipngPacket 的数据结构构造出 RIPng 协议的二进制格式 232 | * @param ripng 一个 RipngPacket 结构体 233 | * @param buffer 一个足够大的缓冲区,你要把 RIPng 协议的数据写进去 234 | * @return 写入 buffer 的数据长度 235 | * 236 | * 在构造二进制格式的时候,你需要把 RipngPacket 中没有保存的一些固定值补充上, 237 | * 包括 Version 和 Zero 字段。 238 | * 你写入 buffer 的数据长度(同时也是该函数的返回值)都应该是四个字节(RIPng 239 | * 头)加上每个 RIPng 表项 20 字节。需要注意一些没有保存在 RipngPacket 240 | * 结构体内的数据的填写。 241 | */ 242 | /** 243 | * @brief Constructs the binary format of the RIPng protocol from the data 244 | * structure of the RipngPacket 245 | * @param ripng a RipngPacket structure 246 | * @param buffer a buffer large enough for you to write the RIPng protocol data 247 | * into 248 | * @return the length of the data written to the buffer 249 | * 250 | * When constructing the binary format, you need to add some fixed values that 251 | * are not stored in the RipngPacket, including the Version and Zero fields. The 252 | * length of the data you write to the buffer (which is also the return value of 253 | * the function) should be four bytes (the RIPng header) plus 20 bytes for each 254 | * RIPng table entry. Note that some data to be written is not stored in the 255 | * RipngPacket structure. 256 | */ 257 | uint32_t assemble(const RipngPacket *ripng, uint8_t *buffer); -------------------------------------------------------------------------------- /Homework/router/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | router 3 | std 4 | std.cpp 5 | !Makefile 6 | -------------------------------------------------------------------------------- /Homework/router/HONOR-CODE.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thu-cs-lab/Router-Lab/a4ffe2773060e7c630c63d4a08d184562679b5db/Homework/router/HONOR-CODE.md -------------------------------------------------------------------------------- /Homework/router/README.md: -------------------------------------------------------------------------------- 1 | # router 2 | 3 | 在这一阶段,你可以复用前面四个作业的代码,实现一个完整的 RIPng 路由器。你需要修改 `main.cpp` ,在所提供的框架上进行完善。 4 | 5 | 所需要实现的功能请参考代码注释和实验文档。 6 | 7 | ## Makefile 的使用 8 | 9 | 当前目录下,有四个目录,分别对应了不同的路由器配置,其中 `r1-r3` 分别对应在 R1 R2 R3 位置的路由器的配置,`custom` 目录对应自定义的配置,用于调试,配置方法见源代码。在对应目录下运行 `make` 就可以得到对应版本的路由器程序。 10 | 11 | 编译的时候可能会出现如下的 warning: 12 | 13 | ``` 14 | /usr/lib/gcc/aarch64-linux-gnu/6/../../../aarch64-linux-gnu/libpcap.a(nametoaddr.o): In function `pcap_nametoaddrinfo': 15 | (.text+0x94): warning: Using 'getaddrinfo' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking 16 | /usr/lib/gcc/aarch64-linux-gnu/6/../../../aarch64-linux-gnu/libpcap.a(nametoaddr.o): In function `pcap_nametoaddr': 17 | (.text+0x8): warning: Using 'gethostbyname' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking 18 | /usr/lib/gcc/aarch64-linux-gnu/6/../../../aarch64-linux-gnu/libpcap.a(nametoaddr.o): In function `pcap_nametonetaddr': 19 | (.text+0xd8): warning: Using 'getnetbyname' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking 20 | /usr/lib/gcc/aarch64-linux-gnu/6/../../../aarch64-linux-gnu/libpcap.a(nametoaddr.o): In function `pcap_nametoproto': 21 | (.text+0x368): warning: Using 'getprotobyname' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking 22 | /usr/lib/gcc/aarch64-linux-gnu/6/../../../aarch64-linux-gnu/libpcap.a(nametoaddr.o): In function `pcap_nametoport': 23 | (.text+0x124): warning: Using 'getservbyname' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking 24 | ``` 25 | 26 | 这是因为,在 TanLabs 评测中,编译机和评测机环境是不一样的,所以采用了静态编译的方式,而静态编译的话,一些功能会不能使用,不过好消息是,这些功能在路由器里不会用到,所以直接忽略这些 warning 即可。 27 | -------------------------------------------------------------------------------- /Homework/router/common/Makefrag: -------------------------------------------------------------------------------- 1 | CXX ?= g++ 2 | LAB_ROOT ?= ../../.. 3 | BACKEND ?= LINUX 4 | 5 | CXXFLAGS += --std=c++11 -I $(LAB_ROOT)/HAL/include -DROUTER_BACKEND_$(BACKEND) -Wno-psabi -O2 -static -I../../protocol -I../../lookup -I../../internet-checksum -I../../eui64 6 | LDFLAGS += -lpcap 7 | ifeq ($(CI),) 8 | CXXFLAGS += -fsanitize=address 9 | LDFLAGS += -fsanitize=address 10 | else 11 | LDFLAGS += -static 12 | endif 13 | 14 | .PHONY: all clean 15 | all: router 16 | 17 | clean: 18 | rm -f *.o router std 19 | 20 | %.o: ../%.cpp 21 | $(CXX) $(CXXFLAGS) -c $^ -o $@ 22 | 23 | common.o: $(LAB_ROOT)/HAL/src/common.cpp 24 | $(CXX) $(CXXFLAGS) -c $< -o $@ 25 | 26 | hal.o: $(LAB_ROOT)/HAL/src/linux/router_hal.cpp $(LAB_ROOT)/HAL/src/linux/platform/standard.h 27 | $(CXX) $(CXXFLAGS) -c $< -o $@ 28 | 29 | eui64.o: ../../eui64/eui64.cpp 30 | $(CXX) $(CXXFLAGS) -c $< -o $@ 31 | 32 | checksum.o: ../../internet-checksum/checksum.cpp 33 | $(CXX) $(CXXFLAGS) -c $< -o $@ 34 | 35 | lookup.o: ../../lookup/lookup.cpp 36 | $(CXX) $(CXXFLAGS) -c $< -o $@ 37 | 38 | protocol.o: ../../protocol/protocol.cpp 39 | $(CXX) $(CXXFLAGS) -c $< -o $@ 40 | 41 | router: main.o hal.o eui64.o checksum.o lookup.o protocol.o common.o 42 | $(CXX) $^ -o $@ $(LDFLAGS) 43 | -------------------------------------------------------------------------------- /Homework/router/custom/Makefile: -------------------------------------------------------------------------------- 1 | include ../common/Makefrag -------------------------------------------------------------------------------- /Homework/router/interconnect-r2/Makefile: -------------------------------------------------------------------------------- 1 | CXXFLAGS += -DROUTER_R2 -DROUTER_INTERCONNECT 2 | include ../common/Makefrag 3 | -------------------------------------------------------------------------------- /Homework/router/main.cpp: -------------------------------------------------------------------------------- 1 | #include "checksum.h" 2 | #include "common.h" 3 | #include "eui64.h" 4 | #include "lookup.h" 5 | #include "protocol.h" 6 | #include "router_hal.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | uint8_t packet[2048] __attribute__((aligned(4))); 13 | uint8_t output[2048] __attribute__((aligned(4))); 14 | 15 | // for online experiment, don't change 16 | #ifdef ROUTER_R1 17 | // 0: fd00::1:1/112 18 | // 1: fd00::3:1/112 19 | // 2: fd00::6:1/112 20 | // 3: fd00::7:1/112 21 | in6_addr addrs[N_IFACE_ON_BOARD] = { 22 | {0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 23 | 0x00, 0x01, 0x00, 0x01}, 24 | {0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 25 | 0x00, 0x03, 0x00, 0x01}, 26 | {0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 27 | 0x00, 0x06, 0x00, 0x01}, 28 | {0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 29 | 0x00, 0x07, 0x00, 0x01}, 30 | }; 31 | #elif defined(ROUTER_R2) 32 | // 0: fd00::3:2/112 33 | // 1: fd00::4:1/112 34 | // 2: fd00::8:1/112 35 | // 3: fd00::9:1/112 36 | in6_addr addrs[N_IFACE_ON_BOARD] = { 37 | {0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 38 | 0x00, 0x03, 0x00, 0x02}, 39 | {0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 40 | 0x00, 0x04, 0x00, 0x01}, 41 | {0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 42 | 0x00, 0x08, 0x00, 0x01}, 43 | {0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 44 | 0x00, 0x09, 0x00, 0x01}, 45 | }; 46 | #elif defined(ROUTER_R3) 47 | // 0: fd00::4:2/112 48 | // 1: fd00::5:2/112 49 | // 2: fd00::a:1/112 50 | // 3: fd00::b:1/112 51 | in6_addr addrs[N_IFACE_ON_BOARD] = { 52 | {0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 53 | 0x00, 0x04, 0x00, 0x02}, 54 | {0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 55 | 0x00, 0x05, 0x00, 0x02}, 56 | {0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 57 | 0x00, 0x0a, 0x00, 0x01}, 58 | {0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 59 | 0x00, 0x0b, 0x00, 0x01}, 60 | }; 61 | #else 62 | 63 | // 自己调试用,你可以按需进行修改 64 | // 0: fd00::0:1 65 | // 1: fd00::1:1 66 | // 2: fd00::2:1 67 | // 3: fd00::3:1 68 | in6_addr addrs[N_IFACE_ON_BOARD] = { 69 | {0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 70 | 0x00, 0x00, 0x00, 0x01}, 71 | {0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 72 | 0x00, 0x01, 0x00, 0x01}, 73 | {0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 74 | 0x00, 0x02, 0x00, 0x01}, 75 | {0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 76 | 0x00, 0x03, 0x00, 0x01}, 77 | }; 78 | #endif 79 | 80 | int main(int argc, char *argv[]) { 81 | // 初始化 HAL 82 | int res = HAL_Init(1, addrs); 83 | if (res < 0) { 84 | return res; 85 | } 86 | 87 | // 插入直连路由 88 | // 例如 R2: 89 | // fd00::3:0/112 if 0 90 | // fd00::4:0/112 if 1 91 | // fd00::8:0/112 if 2 92 | // fd00::9:0/112 if 3 93 | for (uint32_t i = 0; i < N_IFACE_ON_BOARD; i++) { 94 | in6_addr mask = len_to_mask(112); 95 | // TODO(1 行) 96 | // 这里需要添加额外的字段来初始化 metric 97 | RoutingTableEntry entry = { 98 | .addr = addrs[i] & mask, 99 | .len = 112, 100 | .if_index = i, 101 | .nexthop = in6_addr{0} // 全 0 表示直连路由 102 | }; 103 | update(true, entry); 104 | } 105 | 106 | #ifdef ROUTER_INTERCONNECT 107 | // 互联测试 108 | // 添加路由: 109 | // fd00::1:0/112 via fd00::3:1 if 0 110 | // TODO(1 行) 111 | // 这里需要添加额外的字段来初始化 metric 112 | RoutingTableEntry entry = { 113 | .addr = {0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 114 | 0x00, 0x00, 0x01, 0x00, 0x00}, 115 | .len = 112, 116 | .if_index = 0, 117 | .nexthop = {0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 118 | 0x00, 0x00, 0x00, 0x03, 0x00, 0x01}}; 119 | update(true, entry); 120 | #endif 121 | 122 | uint64_t last_time = 0; 123 | while (1) { 124 | uint64_t time = HAL_GetTicks(); 125 | // RFC 要求每 30s 发送一次 126 | // 为了提高收敛速度,设为 5s 127 | if (time > last_time + 5 * 1000) { 128 | // 提示:你可以打印完整的路由表到 stdout/stderr 来帮助调试。 129 | printf("5s Timer\n"); 130 | 131 | for (int i = 0; i < N_IFACE_ON_BOARD; i++) { 132 | ether_addr mac; 133 | HAL_GetInterfaceMacAddress(i, &mac); 134 | 135 | // 下面举一个构造 IPv6 packet 136 | // 的例子,之后有多处代码需要实现类似的功能,请参考此处的例子进行编写。建议实现单独的函数来简化这个过程。 137 | 138 | // IPv6 header 139 | ip6_hdr *reply_ip6 = (ip6_hdr *)&output[0]; 140 | // flow label 141 | reply_ip6->ip6_flow = 0; 142 | // version 143 | reply_ip6->ip6_vfc = 6 << 4; 144 | // payload length 145 | // ip6->ip6_plen = htons(???); 146 | // next header 147 | reply_ip6->ip6_nxt = IPPROTO_UDP; 148 | // hop limit 149 | reply_ip6->ip6_hlim = 255; 150 | // src ip 151 | reply_ip6->ip6_src = eui64(mac); 152 | // dst ip 153 | reply_ip6->ip6_dst = inet6_pton("ff02::9"); 154 | 155 | udphdr *udp = (udphdr *)&output[sizeof(ip6_hdr)]; 156 | // dst port 157 | udp->uh_dport = htons(521); 158 | // src port 159 | udp->uh_sport = htons(521); 160 | 161 | // TODO(40 行) 162 | // 这一步需要向所有 interface 发送当前的完整路由表,设置 Command 为 163 | // Response,并且注意当路由表表项较多时,需要拆分为多个 IPv6 164 | // packet。此时 IPv6 packet 的源地址应为使用 eui64 计算得到的 Link Local 165 | // 地址,目的地址为 ff02::9,以太网帧的源 MAC 地址为当前 interface 的 166 | // MAC 地址,目的 MAC 地址为 33:33:00:00:00:09,详见 RFC 2080 167 | // Section 2.5.2 Generating Response Messages。 168 | // 169 | // 注意需要实现水平分割以及毒性反转(Split Horizon with Poisoned 170 | // Reverse)即,如果某一条路由表项是从 interface A 学习到的,那么发送给 171 | // interface A 的 RIPng 表项中,该项的 metric 设为 16。详见 RFC 2080 172 | // Section 2.6 Split Horizon。因此,发往各个 interface 的 RIPng 173 | // 表项是不同的。 174 | } 175 | last_time = time; 176 | } 177 | 178 | int mask = (1 << N_IFACE_ON_BOARD) - 1; 179 | ether_addr src_mac; 180 | ether_addr dst_mac; 181 | int if_index; 182 | res = HAL_ReceiveIPPacket(mask, packet, sizeof(packet), &src_mac, &dst_mac, 183 | 1000, &if_index); 184 | if (res == HAL_ERR_EOF) { 185 | break; 186 | } else if (res < 0) { 187 | return res; 188 | } else if (res == 0) { 189 | // Timeout 190 | continue; 191 | } else if (res > sizeof(packet)) { 192 | // packet is truncated, ignore it 193 | continue; 194 | } 195 | 196 | // 检查 IPv6 头部长度 197 | ip6_hdr *ip6 = (ip6_hdr *)packet; 198 | if (res < sizeof(ip6_hdr)) { 199 | printf("Received invalid ipv6 packet (%d < %d)\n", res, sizeof(ip6_hdr)); 200 | continue; 201 | } 202 | uint16_t plen = ntohs(ip6->ip6_plen); 203 | if (res < plen + sizeof(ip6_hdr)) { 204 | printf("Received invalid ipv6 packet (%d < %d + %d)\n", res, plen, 205 | sizeof(ip6_hdr)); 206 | continue; 207 | } 208 | 209 | // 检查 IPv6 头部目的地址是否为我自己 210 | bool dst_is_me = false; 211 | for (int i = 0; i < N_IFACE_ON_BOARD; i++) { 212 | if (memcmp(&ip6->ip6_dst, &addrs[i], sizeof(in6_addr)) == 0) { 213 | dst_is_me = true; 214 | break; 215 | } 216 | } 217 | 218 | // TODO(1 行) 219 | // 修改这个检查,当目的地址为 RIPng 的组播目的地址(ff02::9)时也设置 220 | // dst_is_me 为 true。 221 | if (false) { 222 | dst_is_me = true; 223 | } 224 | 225 | if (dst_is_me) { 226 | // 目的地址是我,按照类型进行处理 227 | 228 | // 检查 checksum 是否正确 229 | if (ip6->ip6_nxt == IPPROTO_UDP || ip6->ip6_nxt == IPPROTO_ICMPV6) { 230 | if (!validateAndFillChecksum(packet, res)) { 231 | printf("Received packet with bad checksum\n"); 232 | continue; 233 | } 234 | } 235 | 236 | if (ip6->ip6_nxt == IPPROTO_UDP) { 237 | // 检查是否为 RIPng packet 238 | RipngPacket ripng; 239 | RipngErrorCode err = disassemble(packet, res, &ripng); 240 | if (err == SUCCESS) { 241 | if (ripng.command == 1) { 242 | // 可选功能,实现了可以加快路由表收敛速度 243 | // Command 为 Request 244 | // 参考 RFC 2080 Section 2.4.1 Request Messages 实现 245 | // 本次实验中,可以简化为只考虑输出完整路由表的情况 246 | 247 | RipngPacket resp; 248 | // 与 5s Timer 时的处理类似,也需要实现水平分割和毒性反转 249 | // 可以把两部分代码写到单独的函数中 250 | // 不同的是,在 5s Timer 251 | // 中要组播发给所有的路由器;这里则是某一个路由器 Request 252 | // 本路由器,因此回复 Response 的时候,目的 IPv6 地址和 MAC 253 | // 地址都应该指向发出请求的路由器 254 | 255 | // 最后把 RIPng 报文发送出去 256 | } else { 257 | // TODO(40 行) 258 | // Command 为 Response 259 | // 参考 RFC 2080 Section 2.4.2 Request Messages 实现 260 | // 按照接受到的 RIPng 表项更新自己的路由表 261 | // 在本实验中,可以忽略 metric=0xFF 的表项,它表示的是 Nexthop 262 | // 的设置,可以忽略 263 | 264 | // 接下来的处理中,都首先对输入的 RIPng 表项做如下处理: 265 | // metric = MIN(metric + cost, infinity) 266 | // 其中 cost 取 1,表示经过了一跳路由器;infinity 用 16 表示 267 | 268 | // 如果出现了一条新的路由表项,并且 metric 不等于 16: 269 | // 插入到自己的路由表中,设置 nexthop 270 | // 地址为发送这个 Response 的路由器。 271 | 272 | // 如果收到的路由表项和已知的重复(注意,是精确匹配), 273 | // 进行以下的判断:如果路由表中的表项是之前从该路由器从学习而来,那么直接更新 274 | // metric 275 | // 为新的值;如果路由表中表现是从其他路由器那里学来,就比较已有的表项和 276 | // RIPng 表项中的 metric 大小,如果 RIPng 表项中的 metric 277 | // 更小,说明找到了一条更新的路径,那就用新的表项替换原有的,同时更新 278 | // nexthop 地址。 279 | 280 | // 可选功能:实现 Triggered 281 | // Updates,即在路由表出现更新的时候,向所有 interface 282 | // 发送出现变化的路由表项,注意此时依然要实现水平分割和毒性反转。详见 283 | // RFC 2080 Section 2.5.1。 284 | } 285 | } else { 286 | // 接受到一个错误的 RIPng packet >_< 287 | printf("Got bad RIPng packet from IP %s with error: %s\n", 288 | inet6_ntoa(ip6->ip6_src), ripng_error_to_string(err)); 289 | } 290 | } else if (ip6->ip6_nxt == IPPROTO_ICMPV6) { 291 | // TODO(20 行) 292 | // 如果是 ICMPv6 packet 293 | // 检查是否是 Echo Request 294 | 295 | if (false) { 296 | // 如果是 Echo Request,生成一个对应的 Echo Reply:交换源和目的 IPv6 297 | // 地址,设置 type 为 Echo Reply,设置 TTL(Hop Limit)为 298 | // 64,重新计算 Checksum 并发送出去。详见 RFC 4443 Section 4.2 Echo 299 | // Reply Message 300 | } 301 | } 302 | continue; 303 | } else { 304 | // 目标地址不是我,考虑转发给下一跳 305 | // 检查是否是组播地址(ff00::/8),不需要转发组播分组 306 | if (ip6->ip6_dst.s6_addr[0] == 0xff) { 307 | printf("Don't forward multicast packet to %s\n", 308 | inet6_ntoa(ip6->ip6_dst)); 309 | continue; 310 | } 311 | 312 | // 检查 TTL(Hop Limit)是否小于或等于 1 313 | uint8_t ttl = ip6->ip6_hops; 314 | if (ttl <= 1) { 315 | // TODO(40 行) 316 | // 发送 ICMP Time Exceeded 消息 317 | // 将接受到的 IPv6 packet 附在 ICMPv6 头部之后。 318 | // 如果长度大于 1232 字节,则取前 1232 字节: 319 | // 1232 = IPv6 Minimum MTU(1280) - IPv6 Header(40) - ICMPv6 Header(8) 320 | // 意味着发送的 ICMP Time Exceeded packet 大小不大于 IPv6 Minimum MTU 321 | // 不会因为 MTU 问题被丢弃。 322 | // 详见 RFC 4443 Section 3.3 Time Exceeded Message 323 | // 计算 Checksum 后由自己的 IPv6 地址(不能使用 Link Local 地址) 324 | // 发送给原始分组的源 IPv6 地址。 325 | } else { 326 | // 转发给下一跳 327 | // 按最长前缀匹配查询路由表 328 | in6_addr nexthop; 329 | uint32_t dest_if; 330 | if (prefix_query(ip6->ip6_dst, &nexthop, &dest_if)) { 331 | // 找到路由 332 | ether_addr dest_mac; 333 | // 如果下一跳为全 0,表示的是直连路由,目的机器和本路由器可以直接访问 334 | if (nexthop == in6_addr{0}) { 335 | nexthop = ip6->ip6_dst; 336 | } 337 | if (HAL_GetNeighborMacAddress(dest_if, nexthop, &dest_mac) == 0) { 338 | // 在 NDP 表中找到了下一跳的 MAC 地址 339 | // TTL-1 340 | ip6->ip6_hops--; 341 | 342 | // 转发出去 343 | memcpy(output, packet, res); 344 | HAL_SendIPPacket(dest_if, output, res, dest_mac); 345 | } else { 346 | // 没有找到下一跳的 MAC 地址 347 | // 本实验中可以直接丢掉,等对方回复 NDP 之后,再恢复正常转发。 348 | printf("Nexthop ip %s is not found in NDP table\n", 349 | inet6_ntoa(nexthop)); 350 | } 351 | } else { 352 | // TODO(40 行) 353 | // 没有找到路由 354 | // 发送 ICMPv6 Destination Unreachable 消息 355 | // 要求与上面发送 ICMPv6 Time Exceeded 消息一致 356 | // Code 取 0,表示 No route to destination 357 | // 详见 RFC 4443 Section 3.1 Destination Unreachable Message 358 | // 计算 Checksum 后由自己的 IPv6 地址(不能使用 Link Local 地址) 359 | // 发送给原始分组的源 IPv6 地址。 360 | 361 | printf("Destination IP %s not found in routing table", 362 | inet6_ntoa(ip6->ip6_dst)); 363 | printf(" and source IP is %s\n", inet6_ntoa(ip6->ip6_src)); 364 | } 365 | } 366 | } 367 | } 368 | return 0; 369 | } 370 | -------------------------------------------------------------------------------- /Homework/router/r1/Makefile: -------------------------------------------------------------------------------- 1 | CXXFLAGS += -DROUTER_R1 2 | include ../common/Makefrag -------------------------------------------------------------------------------- /Homework/router/r2/Makefile: -------------------------------------------------------------------------------- 1 | CXXFLAGS += -DROUTER_R2 2 | include ../common/Makefrag -------------------------------------------------------------------------------- /Homework/router/r3/Makefile: -------------------------------------------------------------------------------- 1 | CXXFLAGS += -DROUTER_R3 2 | include ../common/Makefrag -------------------------------------------------------------------------------- /Homework/tftp/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | client 3 | server 4 | std 5 | std.cpp 6 | test 7 | !Makefile 8 | -------------------------------------------------------------------------------- /Homework/tftp/HONOR-CODE.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thu-cs-lab/Router-Lab/a4ffe2773060e7c630c63d4a08d184562679b5db/Homework/tftp/HONOR-CODE.md -------------------------------------------------------------------------------- /Homework/tftp/common/Makefrag: -------------------------------------------------------------------------------- 1 | CXX ?= g++ 2 | LAB_ROOT ?= ../../.. 3 | BACKEND ?= LINUX 4 | 5 | CXXFLAGS += --std=c++11 -I $(LAB_ROOT)/HAL/include -DROUTER_BACKEND_$(BACKEND) -Wno-psabi -O2 -static -I../../protocol -I../../lookup -I../../internet-checksum -I../../eui64 6 | LDFLAGS += -lpcap 7 | ifeq ($(CI),) 8 | CXXFLAGS += -fsanitize=address 9 | LDFLAGS += -fsanitize=address 10 | else 11 | LDFLAGS += -static 12 | endif 13 | 14 | .PHONY: all clean 15 | all: client server 16 | 17 | clean: 18 | rm -f *.o client server std 19 | 20 | %.o: ../%.cpp 21 | $(CXX) $(CXXFLAGS) -c $^ -o $@ 22 | 23 | common.o: $(LAB_ROOT)/HAL/src/common.cpp 24 | $(CXX) $(CXXFLAGS) -c $< -o $@ 25 | 26 | hal.o: $(LAB_ROOT)/HAL/src/linux/router_hal.cpp $(LAB_ROOT)/HAL/src/linux/platform/standard.h 27 | $(CXX) $(CXXFLAGS) -c $< -o $@ 28 | 29 | eui64.o: ../../eui64/eui64.cpp 30 | $(CXX) $(CXXFLAGS) -c $< -o $@ 31 | 32 | checksum.o: ../../internet-checksum/checksum.cpp 33 | $(CXX) $(CXXFLAGS) -c $< -o $@ 34 | 35 | lookup.o: ../../lookup/lookup.cpp 36 | $(CXX) $(CXXFLAGS) -c $< -o $@ 37 | 38 | protocol.o: ../../protocol/protocol.cpp 39 | $(CXX) $(CXXFLAGS) -c $< -o $@ 40 | 41 | client: client.o hal.o eui64.o checksum.o lookup.o protocol.o common.o 42 | $(CXX) $^ -o $@ $(LDFLAGS) 43 | 44 | server: server.o hal.o eui64.o checksum.o lookup.o protocol.o common.o 45 | $(CXX) $^ -o $@ $(LDFLAGS) 46 | -------------------------------------------------------------------------------- /Homework/tftp/pc1/Makefile: -------------------------------------------------------------------------------- 1 | CXXFLAGS += -DROUTER_PC1 2 | include ../common/Makefrag -------------------------------------------------------------------------------- /Homework/tftp/pc2/Makefile: -------------------------------------------------------------------------------- 1 | CXXFLAGS += -DROUTER_PC2 2 | include ../common/Makefrag 3 | -------------------------------------------------------------------------------- /Homework/tftp/pc2/router: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thu-cs-lab/Router-Lab/a4ffe2773060e7c630c63d4a08d184562679b5db/Homework/tftp/pc2/router -------------------------------------------------------------------------------- /Homework/tftp/r2/Makefile: -------------------------------------------------------------------------------- 1 | CXXFLAGS += -DROUTER_R2 2 | include ../common/Makefrag 3 | -------------------------------------------------------------------------------- /Homework/tftp/r2/router: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thu-cs-lab/Router-Lab/a4ffe2773060e7c630c63d4a08d184562679b5db/Homework/tftp/r2/router -------------------------------------------------------------------------------- /Homework/tftp/tftp.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | struct tftp_hdr { 4 | // 2-byte opcode 5 | uint16_t opcode; 6 | union { 7 | // opcode = DATA(0x3) or ACK(0x4): 8 | // 2-byte block number 9 | uint16_t block_number; 10 | // opcode = ERROR(0x5): 11 | // 2-byte error code 12 | uint16_t error_code; 13 | }; 14 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Router-Lab 2 | 3 | ![C/C++ CI](https://github.com/z4yx/Router-Lab/workflows/C/C++%20CI/badge.svg) 4 | 5 | 仓库地址:[GitHub](http://github.com/thu-cs-lab/Router-Lab) [Git.Tsinghua](https://git.tsinghua.edu.cn/Router-Lab/Router-Lab) [Gitee](https://gitee.com/jiegec/Router-Lab.git) 6 | 7 | ## 版权声明 8 | 9 | 本项目为 2019-2020 学年秋季学期、2020-2021 学年秋季学期和春季学期、2021-2022 学年秋季学期清华大学计算机科学与技术系开设的《计算机网络原理》课程的实验框架。 10 | **未经作者授权,禁止用作任何其他用途,包括且不限于在其他课程或者其他学校中使用。** 11 | 作者保留一切追究侵权责任的权利。 12 | 13 | 请注意,本仓库不是在开源协议下发布的,所以本课程选课同学是不能在网络上公开代码的。 14 | 15 | ## 文档 16 | 17 | 文档源码见 [Router-Lab-Docs](https://github.com/thu-cs-lab/Router-Lab-Docs) 仓库,编译后的版本在 [此处](https://lab.cs.tsinghua.edu.cn/router/doc/) 发布。 18 | -------------------------------------------------------------------------------- /Setup/dhcpv6/setup/setup-netns.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Setup netns for individual judge 3 | 4 | # netns 5 | for ns in PC1 R1 R2 6 | do 7 | ip netns delete $ns 2>/dev/null 8 | sleep 1 9 | ip netns add $ns 10 | done 11 | echo 'Netns created' 12 | 13 | # PC1 <-> R1 14 | ip l del pc1r1 2>/dev/null 15 | ip l add pc1r1 type veth peer name r1pc1 16 | ip l set pc1r1 netns PC1 17 | ip l set r1pc1 netns R1 18 | ip netns exec PC1 ip l set pc1r1 up 19 | ip netns exec PC1 ethtool -K pc1r1 tx off 20 | ip netns exec R1 ip l set r1pc1 up 21 | ip netns exec R1 ethtool -K r1pc1 tx off 22 | echo 'PC1 <-> R1 done' 23 | 24 | # R1 <-> R2 25 | ip l del r1r2 2>/dev/null 26 | ip l add r1r2 type veth peer name r2r1 27 | ip l set r1r2 netns R1 28 | ip l set r2r1 netns R2 29 | ip netns exec R1 ip l set r1r2 up 30 | ip netns exec R1 ethtool -K r1r2 tx off 31 | ip netns exec R2 ip l set r2r1 up 32 | ip netns exec R2 ip a add fd00::3:2/112 dev r2r1 33 | ip netns exec R2 ip r add fd00::1:0/112 via fd00::3:1 dev r2r1 34 | ip netns exec R2 ethtool -K r2r1 tx off 35 | echo 'R1 <-> R2 done' -------------------------------------------------------------------------------- /Setup/dhcpv6/setup/start-r1.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Start DHCPv6 server on R1 3 | ip netns exec R1 ../../../Homework/dhcpv6/r1/router 4 | -------------------------------------------------------------------------------- /Setup/dhcpv6/test/test2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Test 2 3 | # Check router advertisement 4 | ip netns exec PC1 rdisc6 pc1r1 -------------------------------------------------------------------------------- /Setup/dhcpv6/test/test3.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Test 3 3 | # Check dhcpv6 client 4 | ip netns exec PC1 dhcpcd -6 -1 -B -C resolv.conf -d pc1r1 5 | ip netns exec PC1 ip -6 a -------------------------------------------------------------------------------- /Setup/dhcpv6/test/test4.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Test 4 3 | # Check ping 4 | ip netns exec PC1 ping -c 4 fd00::3:2 -------------------------------------------------------------------------------- /Setup/dhcpv6/test/test5.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Test 5 3 | # Benchmark using iperf3 4 | ip netns exec R2 iperf3 -s & 5 | IPERF_PID=$! 6 | ip netns exec PC1 iperf3 -c fd00::3:2 -O 5 -P 10 7 | kill $IPERF_PID -------------------------------------------------------------------------------- /Setup/interconnect/client/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /Setup/interconnect/server/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /Setup/interconnect/setup/.gitignore: -------------------------------------------------------------------------------- 1 | *.leases 2 | *.leases~ 3 | *.pid 4 | -------------------------------------------------------------------------------- /Setup/interconnect/setup/bird-v2/bird-r2-v2-ospf.conf: -------------------------------------------------------------------------------- 1 | # bird v2.0 2 | 3 | # random router id 4 | router id 1.1.1.2; 5 | 6 | protocol device { 7 | 8 | } 9 | 10 | protocol kernel { 11 | learn; 12 | persist off; 13 | ipv6 { 14 | import all; 15 | export all; 16 | }; 17 | # avoid overriding existing routes in kernel 18 | # it has nothing to do with the metric system of RIP 19 | metric 1024; 20 | } 21 | 22 | protocol static { 23 | ipv6; 24 | } 25 | 26 | protocol ospf v3 { 27 | debug all; 28 | area 0 { 29 | stubnet fd00::1:0/112; 30 | interface "r2r3" { 31 | hello 5; 32 | type pointopoint; 33 | link lsa suppression yes; 34 | }; 35 | interface "r2r1" { 36 | stub on; 37 | link lsa suppression yes; 38 | }; 39 | }; 40 | } 41 | -------------------------------------------------------------------------------- /Setup/interconnect/setup/bird-v2/bird-r2-v2-ospf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Run standard OSPF on r2 3 | # For BIRD v2 4 | 5 | ip netns exec R2 sysctl net.ipv6.conf.r2r1.disable_ipv6=0 6 | ip netns exec R2 sysctl net.ipv6.conf.r2r1.accept_ra=0 7 | ip netns exec R2 ip a add fd00::3:2/112 dev r2r1 8 | ip netns exec R2 ip r add fd00::1:0/112 via fd00::3:1 dev r2r1 9 | ip netns exec R2 sysctl net.ipv6.conf.r2r3.disable_ipv6=0 10 | ip netns exec R2 ip a add fd00::4:1/112 dev r2r3 11 | 12 | # enable IPv6 forwarding 13 | ip netns exec R2 sysctl -w net.ipv6.conf.all.forwarding=1 14 | ip netns exec R2 bird -c bird-r2-v2-ospf.conf -d -s bird-r2-ospf.ctl 15 | -------------------------------------------------------------------------------- /Setup/interconnect/setup/bird-v2/bird-r2-v2-ripng.conf: -------------------------------------------------------------------------------- 1 | # bird v2.0 2 | 3 | # random router id 4 | router id 1.1.1.2; 5 | 6 | protocol device { 7 | 8 | } 9 | 10 | protocol kernel { 11 | learn; 12 | persist off; 13 | ipv6 { 14 | import all; 15 | export all; 16 | }; 17 | # avoid overriding existing routes in kernel 18 | # it has nothing to do with the metric system of RIP 19 | metric 1024; 20 | } 21 | 22 | protocol direct { 23 | ipv6; 24 | interface "r2*"; 25 | } 26 | 27 | protocol static { 28 | ipv6; 29 | } 30 | 31 | protocol rip ng { 32 | ipv6 { 33 | import all; 34 | export all; 35 | }; 36 | debug all; 37 | interface "r2r3" { 38 | update time 5; 39 | }; 40 | } 41 | -------------------------------------------------------------------------------- /Setup/interconnect/setup/bird-v2/bird-r2-v2-ripng.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Run standard RIPng on r2 3 | # For BIRD v2 4 | 5 | ip netns exec R2 sysctl net.ipv6.conf.r2r1.disable_ipv6=0 6 | ip netns exec R2 sysctl net.ipv6.conf.r2r1.accept_ra=0 7 | ip netns exec R2 ip a add fd00::3:2/112 dev r2r1 8 | ip netns exec R2 ip r add fd00::1:0/112 via fd00::3:1 dev r2r1 9 | ip netns exec R2 sysctl net.ipv6.conf.r2r3.disable_ipv6=0 10 | ip netns exec R2 ip a add fd00::4:1/112 dev r2r3 11 | 12 | # enable IPv6 forwarding 13 | ip netns exec R2 sysctl -w net.ipv6.conf.all.forwarding=1 14 | ip netns exec R2 bird -c bird-r2-v2-ripng.conf -d -s bird-r2-ripng.ctl 15 | -------------------------------------------------------------------------------- /Setup/interconnect/setup/bird-v2/bird-r3-v2-ospf.conf: -------------------------------------------------------------------------------- 1 | # bird v2.0 2 | 3 | # random router id 4 | router id 1.1.1.3; 5 | 6 | protocol device { 7 | 8 | } 9 | 10 | protocol kernel { 11 | learn; 12 | persist off; 13 | ipv6 { 14 | import all; 15 | export all; 16 | }; 17 | # avoid overriding existing routes in kernel 18 | # it has nothing to do with the metric system of RIP 19 | metric 1024; 20 | } 21 | 22 | protocol static { 23 | ipv6; 24 | } 25 | 26 | protocol ospf v3 { 27 | debug all; 28 | area 0 { 29 | interface "r3r2" { 30 | hello 5; 31 | type pointopoint; 32 | link lsa suppression yes; 33 | }; 34 | interface "r3pc2" { 35 | stub on; 36 | link lsa suppression yes; 37 | }; 38 | }; 39 | } 40 | -------------------------------------------------------------------------------- /Setup/interconnect/setup/bird-v2/bird-r3-v2-ospf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Run standard OSPF on r3 3 | # For BIRD v2 4 | 5 | ip netns exec R3 sysctl net.ipv6.conf.r3r2.disable_ipv6=0 6 | ip netns exec R3 ip a add fd00::4:2/112 dev r3r2 7 | ip netns exec R3 sysctl net.ipv6.conf.r3pc2.disable_ipv6=0 8 | ip netns exec R3 ip a add fd00::5:2/112 dev r3pc2 9 | 10 | # enable IPv6 forwarding 11 | ip netns exec R3 sysctl -w net.ipv6.conf.all.forwarding=1 12 | ip netns exec R3 bird -c bird-r3-v2-ospf.conf -d -s bird-r3-ospf.ctl 13 | 14 | -------------------------------------------------------------------------------- /Setup/interconnect/setup/bird-v2/bird-r3-v2-ripng.conf: -------------------------------------------------------------------------------- 1 | # bird v2.0 2 | 3 | # random router id 4 | router id 1.1.1.3; 5 | 6 | protocol device { 7 | 8 | } 9 | 10 | protocol kernel { 11 | learn; 12 | persist off; 13 | ipv6 { 14 | import all; 15 | export all; 16 | }; 17 | # avoid overriding existing routes in kernel 18 | # it has nothing to do with the metric system of RIP 19 | metric 1024; 20 | } 21 | 22 | protocol direct { 23 | ipv6; 24 | interface "r3*"; 25 | } 26 | 27 | protocol static { 28 | ipv6; 29 | } 30 | 31 | protocol rip ng { 32 | ipv6 { 33 | import all; 34 | export all; 35 | }; 36 | debug all; 37 | interface "r3r2" { 38 | update time 5; 39 | }; 40 | } 41 | -------------------------------------------------------------------------------- /Setup/interconnect/setup/bird-v2/bird-r3-v2-ripng.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Run standard RIPng on r3 3 | # For BIRD v2 4 | 5 | ip netns exec R3 sysctl net.ipv6.conf.r3r2.disable_ipv6=0 6 | ip netns exec R3 ip a add fd00::4:2/112 dev r3r2 7 | ip netns exec R3 sysctl net.ipv6.conf.r3pc2.disable_ipv6=0 8 | ip netns exec R3 ip a add fd00::5:2/112 dev r3pc2 9 | 10 | # enable IPv6 forwarding 11 | ip netns exec R3 sysctl -w net.ipv6.conf.all.forwarding=1 12 | ip netns exec R3 bird -c bird-r3-v2-ripng.conf -d -s bird-r3-ripng.ctl 13 | -------------------------------------------------------------------------------- /Setup/interconnect/setup/dhcpd.conf: -------------------------------------------------------------------------------- 1 | 2 | default-lease-time 600; 3 | max-lease-time 7200; 4 | ddns-update-style none; 5 | authoritative; 6 | subnet6 fd00::1:0/112 { 7 | range6 fd00::1:2 fd00::1:2; 8 | option dhcp6.name-servers 2402:f000:1:801::8:28, 2402:f000:1:801::8:29; 9 | } 10 | -------------------------------------------------------------------------------- /Setup/interconnect/setup/radvd.conf: -------------------------------------------------------------------------------- 1 | interface r1pc1 2 | { 3 | AdvSendAdvert on; 4 | AdvManagedFlag on; 5 | prefix fd00::1:0/112 6 | { 7 | AdvOnLink on; 8 | AdvAutonomous on; 9 | }; 10 | }; -------------------------------------------------------------------------------- /Setup/interconnect/setup/setup-netns.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Setup netns for interconnect judge 3 | 4 | # netns 5 | for ns in PC1 R1 R2 R3 PC2 6 | do 7 | ip netns delete $ns 2>/dev/null 8 | sleep 1 9 | ip netns add $ns 10 | done 11 | echo 'Netns created' 12 | 13 | # PC1 <-> R1 14 | ip l del pc1r1 2>/dev/null 15 | ip l add pc1r1 type veth peer name r1pc1 16 | ip l set pc1r1 netns PC1 17 | ip l set r1pc1 netns R1 18 | ip netns exec PC1 ip l set pc1r1 up 19 | ip netns exec PC1 ethtool -K pc1r1 tx off 20 | ip netns exec R1 ip l set r1pc1 up 21 | ip netns exec R1 ethtool -K r1pc1 tx off 22 | echo 'PC1 <-> R1 done' 23 | 24 | # R1 <-> R2 25 | ip l del r1r2 2>/dev/null 26 | ip l add r1r2 type veth peer name r2r1 27 | ip l set r1r2 netns R1 28 | ip l set r2r1 netns R2 29 | ip netns exec R1 ip l set r1r2 up 30 | ip netns exec R1 ethtool -K r1r2 tx off 31 | ip netns exec R2 ip l set r2r1 up 32 | ip netns exec R2 ethtool -K r2r1 tx off 33 | echo 'R1 <-> R2 done' 34 | 35 | # R2 <-> R3 36 | ip l del r2r3 2>/dev/null 37 | ip l add r2r3 type veth peer name r3r2 38 | ip l set r2r3 netns R2 39 | ip l set r3r2 netns R3 40 | ip netns exec R3 ip l set r3r2 up 41 | ip netns exec R3 ethtool -K r3r2 tx off 42 | ip netns exec R2 ip l set r2r3 up 43 | ip netns exec R2 ethtool -K r2r3 tx off 44 | echo 'R2 <-> R3 done' 45 | 46 | # R3 <-> PC2 47 | ip l del r3pc2 2>/dev/null 48 | ip l add r3pc2 type veth peer name pc2r3 49 | ip l set r3pc2 netns R3 50 | ip l set pc2r3 netns PC2 51 | ip netns exec PC2 ip l set pc2r3 up 52 | ip netns exec PC2 ethtool -K pc2r3 tx off 53 | ip netns exec R3 ip l set r3pc2 up 54 | ip netns exec R3 ethtool -K r3pc2 tx off 55 | echo 'R3 <-> PC2 done' 56 | -------------------------------------------------------------------------------- /Setup/interconnect/setup/start-custom-pc2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Start custom tftp server on pc2 3 | cd ../server && ip netns exec PC2 ../../../Homework/tftp/pc2/server 4 | -------------------------------------------------------------------------------- /Setup/interconnect/setup/start-custom-r1.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Start custom DHCPv6 server on R1 3 | ip netns exec R1 ../../../Homework/dhcpv6/r1/router 4 | -------------------------------------------------------------------------------- /Setup/interconnect/setup/start-custom-r2-ospf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Start custom OSPF router on R2 3 | ip netns exec R2 ../../../Homework/ospf/interconnect-r2/router 4 | -------------------------------------------------------------------------------- /Setup/interconnect/setup/start-custom-r2-ripng.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Start custom RIPng router on R2 3 | ip netns exec R2 ../../../Homework/router/interconnect-r2/router 4 | -------------------------------------------------------------------------------- /Setup/interconnect/setup/start-custom-r3-ospf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Start custom OSPF router on R3 3 | ip netns exec R3 ../../../Homework/ospf/r3/router 4 | -------------------------------------------------------------------------------- /Setup/interconnect/setup/start-custom-r3-ripng.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Start custom RIPng router on R3 3 | ip netns exec R3 ../../../Homework/router/r3/router 4 | -------------------------------------------------------------------------------- /Setup/interconnect/setup/start-standard-pc2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Start standard tftp server on pc2 3 | # Setup ipv6 address and route 4 | ip netns exec PC2 sysctl net.ipv6.conf.pc2r3.disable_ipv6=0 5 | ip netns exec PC2 ip a add fd00::5:1/112 dev pc2r3 6 | ip netns exec PC2 ip l set pc2r3 up 7 | ip netns exec PC2 ip -6 r add default via fd00::5:2 8 | # Start tftpd-hpa server 9 | cd ../server && ip netns exec PC2 /usr/sbin/in.tftpd --foreground --listen --address :69 --verbose --secure --create --user root . -------------------------------------------------------------------------------- /Setup/interconnect/setup/start-standard-r1-dhcpd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Start standard DHCPv6 server on R1 3 | ip netns exec R1 sysctl net.ipv6.conf.r1pc1.disable_ipv6=0 4 | ip netns exec R1 ip a add fd00::1:1/112 dev r1pc1 5 | ip netns exec R1 ip l set r1pc1 up 6 | ip netns exec R1 sysctl net.ipv6.conf.r1r2.disable_ipv6=0 7 | ip netns exec R1 ip a add fd00::3:1/112 dev r1r2 8 | ip netns exec R1 ip l set r1r2 up 9 | ip netns exec R1 ip -6 r add default via fd00::3:2 10 | rm -rf dhcp6.leases 11 | touch dhcp6.leases 12 | ip netns exec R1 /usr/sbin/dhcpd -6 -f -d --no-pid -cf dhcpd.conf -lf dhcp6.leases 13 | -------------------------------------------------------------------------------- /Setup/interconnect/setup/start-standard-r1-radvd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Start standard IPv6 RA server on R1 3 | ip netns exec R1 sysctl net.ipv6.conf.r1pc1.disable_ipv6=0 4 | ip netns exec R1 ip a add fd00::1:1/112 dev r1pc1 5 | ip netns exec R1 ip l set r1pc1 up 6 | ip netns exec R1 sysctl net.ipv6.conf.r1r2.disable_ipv6=0 7 | ip netns exec R1 ip a add fd00::3:1/112 dev r1r2 8 | ip netns exec R1 ip l set r1r2 up 9 | ip netns exec R1 ip -6 r add default via fd00::3:2 10 | # enable IPv6 forwarding 11 | ip netns exec R1 sysctl -w net.ipv6.conf.all.forwarding=1 12 | ip netns exec R1 /usr/sbin/radvd -n -C radvd.conf -p radvd.pid 13 | -------------------------------------------------------------------------------- /Setup/interconnect/test/test2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Test 2 3 | # Check dhcpv6 client 4 | ip netns exec PC1 sysctl net.ipv6.conf.pc1r1.disable_ipv6=0 5 | ip netns exec PC1 dhcpcd -6 -1 -B -C resolv.conf -d pc1r1 6 | ip netns exec PC1 ip -6 a -------------------------------------------------------------------------------- /Setup/interconnect/test/test4-bird-r2-v2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Test 4 3 | # Check OSPF neighbor state 4 | sudo birdc -s ../setup/bird-v2/bird-r2-ospf.ctl show ospf neighbor 5 | -------------------------------------------------------------------------------- /Setup/interconnect/test/test4-bird-r3-v2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Test 4 3 | # Check OSPF neighbor state 4 | sudo birdc -s ../setup/bird-v2/bird-r3-ospf.ctl show ospf neighbor 5 | -------------------------------------------------------------------------------- /Setup/interconnect/test/test5.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Test 5 3 | # You need to ensure that PC1 receives dynamic IPv6 via dhcpcd 4 | # Ping fd00::3:2 5 | ip netns exec PC1 ping6 -c4 fd00::3:2 6 | -------------------------------------------------------------------------------- /Setup/interconnect/test/test6.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Test 6 3 | # Ping fd00::5:1 with hop limit = 3 4 | ip netns exec PC1 ping6 -c 4 -t 3 fd00::5:1 5 | -------------------------------------------------------------------------------- /Setup/interconnect/test/test7-standard.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Test 7 3 | # Standard client on PC1 <- server on PC2 4 | # You need to ensure that a tftp server is running 5 | rm -rf ../client/test7 6 | echo "content of test7, standard client" > ../server/test7 7 | cd ../client && ip netns exec PC1 sh -c "echo \"get test7\" | tftp fd00::5:1" 8 | cat ../client/test7 9 | -------------------------------------------------------------------------------- /Setup/interconnect/test/test7.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Test 7 3 | # Custom client on PC1 <- server on PC2 4 | # You need to ensure that a tftp server is running 5 | rm -rf ../client/test7 6 | echo "content of test7, custom client" > ../server/test7 7 | cd ../client && ip netns exec PC1 ../../../Homework/tftp/pc1/client get fd00::5:1 test7 8 | cat ../client/test7 9 | -------------------------------------------------------------------------------- /Setup/interconnect/test/test8-standard.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Test 8 3 | # Standard client on PC1 -> server on PC2 4 | # You need to ensure that a tftp server is running 5 | rm -rf ../server/test8 6 | echo "content of test8, custom client" > ../client/test8 7 | cd ../client && ip netns exec PC1 sh -c "echo \"put test8\" | tftp fd00::5:1" 8 | cat ../server/test8 9 | -------------------------------------------------------------------------------- /Setup/interconnect/test/test8.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Test 8 3 | # Custom client on PC1 -> server on PC2 4 | # You need to ensure that a tftp server is running 5 | rm -rf ../server/test8 6 | echo "content of test8, custom client" > ../client/test8 7 | cd ../client && ip netns exec PC1 ../../../Homework/tftp/pc1/client put fd00::5:1 test8 8 | cat ../server/test8 9 | -------------------------------------------------------------------------------- /Setup/interconnect/test/test9-standard.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Test 9 3 | # Standard client on PC1 <- server on PC2 4 | # Benchmark 5 | # You need to ensure that a tftp server is running 6 | rm -rf ../client/test9 7 | dd if=/dev/zero of=../server/test9 bs=1024 count=1024 8 | cd ../client && time ip netns exec PC1 sh -c "echo \"get test9\" | tftp fd00::5:1" 9 | ls -alh ../client/test9 10 | -------------------------------------------------------------------------------- /Setup/interconnect/test/test9.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Test 9 3 | # Custom client on PC1 <- server on PC2 4 | # Benchmark 5 | # You need to ensure that a tftp server is running 6 | rm -rf ../client/test9 7 | dd if=/dev/zero of=../server/test9 bs=1024 count=1024 8 | cd ../client && time ip netns exec PC1 ../../../Homework/tftp/pc1/client get fd00::5:1 test9 9 | ls -alh ../client/test9 10 | -------------------------------------------------------------------------------- /Setup/ospf/setup/bird-v2/bird-r1-v2.conf: -------------------------------------------------------------------------------- 1 | # bird v2.0 2 | 3 | # random router id 4 | router id 1.1.1.1; 5 | 6 | protocol device { 7 | 8 | } 9 | 10 | protocol kernel { 11 | learn; 12 | persist off; 13 | ipv6 { 14 | import all; 15 | export all; 16 | }; 17 | # avoid overriding existing routes in kernel 18 | # it has nothing to do with the metric system of RIP 19 | metric 1024; 20 | } 21 | 22 | protocol static { 23 | ipv6; 24 | } 25 | 26 | protocol ospf v3 { 27 | debug all; 28 | area 0 { 29 | interface "r1r2" { 30 | hello 5; 31 | type pointopoint; 32 | link lsa suppression yes; 33 | }; 34 | interface "r1pc1" { 35 | stub on; 36 | link lsa suppression yes; 37 | }; 38 | }; 39 | } 40 | -------------------------------------------------------------------------------- /Setup/ospf/setup/bird-v2/bird-r1-v2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ip netns exec R1 bird -c bird-r1-v2.conf -d -s bird-r1.ctl 4 | -------------------------------------------------------------------------------- /Setup/ospf/setup/bird-v2/bird-r3-v2.conf: -------------------------------------------------------------------------------- 1 | # bird v2.0 2 | 3 | # random router id 4 | router id 1.1.1.3; 5 | 6 | protocol device { 7 | 8 | } 9 | 10 | protocol kernel { 11 | learn; 12 | persist off; 13 | ipv6 { 14 | import all; 15 | export all; 16 | }; 17 | # avoid overriding existing routes in kernel 18 | # it has nothing to do with the metric system of RIP 19 | metric 1024; 20 | } 21 | 22 | protocol static { 23 | ipv6; 24 | } 25 | 26 | protocol ospf v3 { 27 | debug all; 28 | area 0 { 29 | interface "r3r2" { 30 | hello 5; 31 | type pointopoint; 32 | link lsa suppression yes; 33 | }; 34 | interface "r3pc2" { 35 | stub on; 36 | link lsa suppression yes; 37 | }; 38 | }; 39 | } 40 | -------------------------------------------------------------------------------- /Setup/ospf/setup/bird-v2/bird-r3-v2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ip netns exec R3 bird -c bird-r3-v2.conf -d -s bird-r3.ctl 4 | -------------------------------------------------------------------------------- /Setup/ospf/setup/setup-netns.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Setup netns for individual judge 3 | # IPv6 forwarding is enabled for R1 & R3 4 | 5 | # netns 6 | for ns in PC1 R1 R2 R3 PC2 7 | do 8 | ip netns delete $ns 2>/dev/null 9 | sleep 1 10 | ip netns add $ns 11 | done 12 | echo 'Netns created' 13 | 14 | # PC1 <-> R1 15 | ip l del pc1r1 2>/dev/null 16 | ip l add pc1r1 type veth peer name r1pc1 17 | ip l set pc1r1 netns PC1 18 | ip l set r1pc1 netns R1 19 | ip netns exec PC1 ip a add fd00::1:2/112 dev pc1r1 20 | ip netns exec PC1 ip l set pc1r1 up 21 | ip netns exec PC1 ip -6 r add default via fd00::1:1 22 | ip netns exec PC1 ethtool -K pc1r1 tx off 23 | ip netns exec R1 ip a add fd00::1:1/112 dev r1pc1 24 | ip netns exec R1 ip l set r1pc1 up 25 | ip netns exec R1 ethtool -K r1pc1 tx off 26 | echo 'PC1 <-> R1 done' 27 | 28 | # R1 <-> R2 29 | ip l del r1r2 2>/dev/null 30 | ip l add r1r2 type veth peer name r2r1 31 | ip l set r1r2 netns R1 32 | ip l set r2r1 netns R2 33 | ip netns exec R1 ip a add fd00::3:1/112 dev r1r2 34 | ip netns exec R1 ip l set r1r2 up 35 | ip netns exec R1 ethtool -K r1r2 tx off 36 | ip netns exec R2 ip l set r2r1 up 37 | ip netns exec R2 ethtool -K r2r1 tx off 38 | echo 'R1 <-> R2 done' 39 | 40 | # R2 <-> R3 41 | ip l del r2r3 2>/dev/null 42 | ip l add r2r3 type veth peer name r3r2 43 | ip l set r2r3 netns R2 44 | ip l set r3r2 netns R3 45 | ip netns exec R3 ip a add fd00::4:2/112 dev r3r2 46 | ip netns exec R3 ip l set r3r2 up 47 | ip netns exec R3 ethtool -K r3r2 tx off 48 | ip netns exec R2 ip l set r2r3 up 49 | ip netns exec R2 ethtool -K r2r3 tx off 50 | echo 'R2 <-> R3 done' 51 | 52 | # R3 <-> PC2 53 | ip l del r3pc2 2>/dev/null 54 | ip l add r3pc2 type veth peer name pc2r3 55 | ip l set r3pc2 netns R3 56 | ip l set pc2r3 netns PC2 57 | ip netns exec PC2 ip a add fd00::5:1/112 dev pc2r3 58 | ip netns exec PC2 ip l set pc2r3 up 59 | ip netns exec PC2 ip -6 r add default via fd00::5:2 60 | ip netns exec PC2 ethtool -K pc2r3 tx off 61 | ip netns exec R3 ip a add fd00::5:2/112 dev r3pc2 62 | ip netns exec R3 ip l set r3pc2 up 63 | ip netns exec R3 ethtool -K r3pc2 tx off 64 | echo 'R3 <-> PC2 done' 65 | 66 | # enable IPv6 forwarding 67 | ip netns exec R1 sysctl -w net.ipv6.conf.all.forwarding=1 68 | ip netns exec R3 sysctl -w net.ipv6.conf.all.forwarding=1 69 | -------------------------------------------------------------------------------- /Setup/ospf/setup/start-r2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ip netns exec R2 ../../../Homework/ospf/r2/router 3 | -------------------------------------------------------------------------------- /Setup/ospf/test/test3-bird-v2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Test 3 3 | # Check OSPF neighbor state 4 | sudo birdc -s ../setup/bird-v2/bird-r1.ctl show ospf neighbor 5 | sudo birdc -s ../setup/bird-v2/bird-r3.ctl show ospf neighbor -------------------------------------------------------------------------------- /Setup/ospf/test/test4.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Test 4 3 | # Check ping 4 | ip netns exec PC1 ping -c 4 fd00::5:1 5 | ip netns exec PC2 ping -c 4 fd00::1:2 -------------------------------------------------------------------------------- /Setup/ospf/test/test5.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Test 5 3 | # Check TCP 4 | echo "test" | ip netns exec PC1 nc -6 -l -p 80 -N & 5 | NC_PID=$! 6 | timeout 4s ip netns exec PC2 nc fd00::1:2 80 7 | kill $NC_PID 8 | 9 | echo "test" | ip netns exec PC2 nc -6 -l -p 80 -N & 10 | NC_PID=$! 11 | timeout 4s ip netns exec PC1 nc fd00::5:1 80 12 | kill $NC_PID -------------------------------------------------------------------------------- /Setup/ospf/test/test6.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Test 6 3 | # Check route 4 | ip netns exec R1 ip -6 route 5 | ip netns exec R3 ip -6 route -------------------------------------------------------------------------------- /Setup/ospf/test/test7.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Test 7 3 | # Benchmark using iperf3 4 | ip netns exec PC2 iperf3 -s & 5 | IPERF_PID=$! 6 | ip netns exec PC1 iperf3 -c fd00::5:1 -O 5 -P 10 7 | kill $IPERF_PID 8 | -------------------------------------------------------------------------------- /Setup/ripng/setup/bird-v2/bird-r1-v2.conf: -------------------------------------------------------------------------------- 1 | # bird v2.0 2 | 3 | # random router id 4 | router id 1.1.1.1; 5 | 6 | protocol device { 7 | 8 | } 9 | 10 | protocol kernel { 11 | learn; 12 | persist off; 13 | ipv6 { 14 | import all; 15 | export all; 16 | }; 17 | # avoid overriding existing routes in kernel 18 | # it has nothing to do with the metric system of RIP 19 | metric 1024; 20 | } 21 | 22 | protocol direct { 23 | ipv6; 24 | interface "r1*"; 25 | } 26 | 27 | protocol static { 28 | ipv6; 29 | } 30 | 31 | protocol rip ng { 32 | ipv6 { 33 | import all; 34 | export all; 35 | }; 36 | debug all; 37 | interface "r1r2" { 38 | update time 5; 39 | }; 40 | } 41 | -------------------------------------------------------------------------------- /Setup/ripng/setup/bird-v2/bird-r1-v2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ip netns exec R1 bird -c bird-r1-v2.conf -d -s bird-r1.ctl 4 | -------------------------------------------------------------------------------- /Setup/ripng/setup/bird-v2/bird-r3-v2.conf: -------------------------------------------------------------------------------- 1 | # bird v2.0 2 | 3 | # random router id 4 | router id 1.1.1.1; 5 | 6 | protocol device { 7 | 8 | } 9 | 10 | protocol kernel { 11 | learn; 12 | persist off; 13 | ipv6 { 14 | import all; 15 | export all; 16 | }; 17 | # avoid overriding existing routes in kernel 18 | # it has nothing to do with the metric system of RIP 19 | metric 1024; 20 | } 21 | 22 | protocol direct { 23 | ipv6; 24 | interface "r3*"; 25 | } 26 | 27 | protocol static { 28 | ipv6; 29 | } 30 | 31 | protocol rip ng { 32 | ipv6 { 33 | import all; 34 | export all; 35 | }; 36 | debug all; 37 | interface "r3r2" { 38 | update time 5; 39 | }; 40 | } 41 | -------------------------------------------------------------------------------- /Setup/ripng/setup/bird-v2/bird-r3-v2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ip netns exec R3 bird -c bird-r3-v2.conf -d -s bird-r3.ctl 4 | -------------------------------------------------------------------------------- /Setup/ripng/setup/setup-netns.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Setup netns for individual judge 3 | # IPv6 forwarding is enabled for R1 & R3 4 | 5 | # netns 6 | for ns in PC1 R1 R2 R3 PC2 7 | do 8 | ip netns delete $ns 2>/dev/null 9 | sleep 1 10 | ip netns add $ns 11 | done 12 | echo 'Netns created' 13 | 14 | # PC1 <-> R1 15 | ip l del pc1r1 2>/dev/null 16 | ip l add pc1r1 type veth peer name r1pc1 17 | ip l set pc1r1 netns PC1 18 | ip l set r1pc1 netns R1 19 | ip netns exec PC1 ip a add fd00::1:2/112 dev pc1r1 20 | ip netns exec PC1 ip l set pc1r1 up 21 | ip netns exec PC1 ip -6 r add default via fd00::1:1 22 | ip netns exec PC1 ethtool -K pc1r1 tx off 23 | ip netns exec R1 ip a add fd00::1:1/112 dev r1pc1 24 | ip netns exec R1 ip l set r1pc1 up 25 | ip netns exec R1 ethtool -K r1pc1 tx off 26 | echo 'PC1 <-> R1 done' 27 | 28 | # R1 <-> R2 29 | ip l del r1r2 2>/dev/null 30 | ip l add r1r2 type veth peer name r2r1 31 | ip l set r1r2 netns R1 32 | ip l set r2r1 netns R2 33 | ip netns exec R1 ip a add fd00::3:1/112 dev r1r2 34 | ip netns exec R1 ip l set r1r2 up 35 | ip netns exec R1 ethtool -K r1r2 tx off 36 | ip netns exec R2 ip l set r2r1 up 37 | ip netns exec R2 ethtool -K r2r1 tx off 38 | echo 'R1 <-> R2 done' 39 | 40 | # R2 <-> R3 41 | ip l del r2r3 2>/dev/null 42 | ip l add r2r3 type veth peer name r3r2 43 | ip l set r2r3 netns R2 44 | ip l set r3r2 netns R3 45 | ip netns exec R3 ip a add fd00::4:2/112 dev r3r2 46 | ip netns exec R3 ip l set r3r2 up 47 | ip netns exec R3 ethtool -K r3r2 tx off 48 | ip netns exec R2 ip l set r2r3 up 49 | ip netns exec R2 ethtool -K r2r3 tx off 50 | echo 'R2 <-> R3 done' 51 | 52 | # R3 <-> PC2 53 | ip l del r3pc2 2>/dev/null 54 | ip l add r3pc2 type veth peer name pc2r3 55 | ip l set r3pc2 netns R3 56 | ip l set pc2r3 netns PC2 57 | ip netns exec PC2 ip a add fd00::5:1/112 dev pc2r3 58 | ip netns exec PC2 ip l set pc2r3 up 59 | ip netns exec PC2 ip -6 r add default via fd00::5:2 60 | ip netns exec PC2 ethtool -K pc2r3 tx off 61 | ip netns exec R3 ip a add fd00::5:2/112 dev r3pc2 62 | ip netns exec R3 ip l set r3pc2 up 63 | ip netns exec R3 ethtool -K r3pc2 tx off 64 | echo 'R3 <-> PC2 done' 65 | 66 | # enable IPv6 forwarding 67 | ip netns exec R1 sysctl -w net.ipv6.conf.all.forwarding=1 68 | ip netns exec R3 sysctl -w net.ipv6.conf.all.forwarding=1 69 | -------------------------------------------------------------------------------- /Setup/ripng/setup/start-r2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ip netns exec R2 ../../../Homework/router/r2/router 3 | -------------------------------------------------------------------------------- /Setup/ripng/test/test3.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Test 3 3 | # Check ping 4 | ip netns exec PC1 ping -c 4 fd00::5:1 5 | ip netns exec PC2 ping -c 4 fd00::1:2 -------------------------------------------------------------------------------- /Setup/ripng/test/test4.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Test 4 3 | # Check TCP 4 | echo "test" | ip netns exec PC1 nc -6 -l -p 80 -N & 5 | NC_PID=$! 6 | timeout 4s ip netns exec PC2 nc fd00::1:2 80 7 | kill $NC_PID 8 | 9 | echo "test" | ip netns exec PC2 nc -6 -l -p 80 -N & 10 | NC_PID=$! 11 | timeout 4s ip netns exec PC1 nc fd00::5:1 80 12 | kill $NC_PID -------------------------------------------------------------------------------- /Setup/ripng/test/test5.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Test 5 3 | # Check route 4 | ip netns exec R1 ip -6 route 5 | ip netns exec R3 ip -6 route -------------------------------------------------------------------------------- /Setup/ripng/test/test6.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Test 6 3 | # Check hop limit 4 | ip netns exec PC1 ping -c 4 -t 2 fd00::5:1 5 | ip netns exec PC2 ping -c 4 -t 2 fd00::1:2 -------------------------------------------------------------------------------- /Setup/ripng/test/test7.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Test 7 3 | # Check icmp echo reply 4 | ip netns exec PC1 ping -c 4 fd00::3:2 5 | ip netns exec PC2 ping -c 4 fd00::4:1 -------------------------------------------------------------------------------- /Setup/ripng/test/test8.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Test 8 3 | # Benchmark using iperf3 4 | ip netns exec PC2 iperf3 -s & 5 | IPERF_PID=$! 6 | ip netns exec PC1 iperf3 -c fd00::5:1 -O 5 -P 10 7 | kill $IPERF_PID -------------------------------------------------------------------------------- /Setup/tftp/client/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /Setup/tftp/server/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /Setup/tftp/setup/setup-netns.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Setup netns for individual judge 3 | # IPv6 forwarding is enabled for R1 4 | 5 | # netns 6 | for ns in PC1 R1 R2 7 | do 8 | ip netns delete $ns 2>/dev/null 9 | sleep 1 10 | ip netns add $ns 11 | done 12 | echo 'Netns created' 13 | 14 | # PC1 <-> R1 15 | ip l del pc1r1 2>/dev/null 16 | ip l add pc1r1 type veth peer name r1pc1 17 | ip l set pc1r1 netns PC1 18 | ip l set r1pc1 netns R1 19 | ip netns exec PC1 ip l set pc1r1 up 20 | ip netns exec PC1 ethtool -K pc1r1 tx off 21 | ip netns exec R1 ip a add fd00::1:1/112 dev r1pc1 22 | ip netns exec R1 ip l set r1pc1 up 23 | ip netns exec R1 ethtool -K r1pc1 tx off 24 | echo 'PC1 <-> R1 done' 25 | 26 | # R1 <-> R2 27 | ip l del r1r2 2>/dev/null 28 | ip l add r1r2 type veth peer name r2r1 29 | ip l set r1r2 netns R1 30 | ip l set r2r1 netns R2 31 | ip netns exec R1 ip a add fd00::3:1/112 dev r1r2 32 | ip netns exec R1 ip l set r1r2 up 33 | ip netns exec R1 ethtool -K r1r2 tx off 34 | ip netns exec R2 ip l set r2r1 up 35 | ip netns exec R2 ethtool -K r2r1 tx off 36 | echo 'R1 <-> R2 done' 37 | 38 | # enable IPv6 forwarding 39 | ip netns exec R1 sysctl -w net.ipv6.conf.all.forwarding=1 -------------------------------------------------------------------------------- /Setup/tftp/setup/start-custom-tftpd-r2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Start custom tftpd on r2 3 | ip netns exec R2 sysctl net.ipv6.conf.r2r1.disable_ipv6=1 4 | cd ../server && ip netns exec R2 ../../../Homework/tftp/r2/server -------------------------------------------------------------------------------- /Setup/tftp/setup/start-standard-tftpd-r2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Start standard tftpd on r2 3 | # Setup ipv6 address and route 4 | ip netns exec R2 sysctl net.ipv6.conf.r2r1.disable_ipv6=0 5 | ip netns exec R2 ip a add fd00::3:2/112 dev r2r1 6 | ip netns exec R2 ip l set r2r1 up 7 | ip netns exec R2 ip -6 r add default via fd00::3:1 8 | # Start tftpd-hpa server 9 | cd ../server && ip netns exec R2 /usr/sbin/in.tftpd --foreground --listen --address :69 --verbose --secure --create --user root . -------------------------------------------------------------------------------- /Setup/tftp/test/test2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Test 2 3 | # Custom client <- Standard server 4 | # You need to ensure that a standard server is running 5 | rm -rf ../client/test2 6 | echo "content of test2, custom client & standard server" > ../server/test2 7 | cd ../client && ip netns exec PC1 ../../../Homework/tftp/pc1/client get fd00::3:2 test2 8 | cat ../client/test2 -------------------------------------------------------------------------------- /Setup/tftp/test/test3.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Test 3 3 | # Custom client -> Standard server 4 | # You need to ensure that a standard server is running 5 | rm -rf ../server/test3 6 | echo "content of test3, custom client & standard server" > ../client/test3 7 | cd ../client && ip netns exec PC1 ../../../Homework/tftp/pc1/client put fd00::3:2 test3 8 | cat ../server/test3 -------------------------------------------------------------------------------- /Setup/tftp/test/test5.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Test 5 3 | # Standard client <- Custom server 4 | # You need to ensure that a custom server is running 5 | # Setup ipv6 address and route 6 | ip netns exec PC1 sysctl net.ipv6.conf.pc1r1.disable_ipv6=0 7 | ip netns exec PC1 ip a add fd00::1:2/112 dev pc1r1 8 | ip netns exec PC1 ip l set pc1r1 up 9 | ip netns exec PC1 ip -6 r add default via fd00::1:1 10 | 11 | rm -rf ../client/test5 12 | echo "content of test5, standard client & custom server" > ../server/test5 13 | cd ../client && ip netns exec PC1 sh -c "echo \"get test5\" | tftp fd00::3:2" 14 | cat ../client/test5 -------------------------------------------------------------------------------- /Setup/tftp/test/test6.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Test 6 3 | # Standard client -> Custom server 4 | # You need to ensure that a custom server is running 5 | # Setup ipv6 address and route 6 | ip netns exec PC1 sysctl net.ipv6.conf.pc1r1.disable_ipv6=0 7 | ip netns exec PC1 ip a add fd00::1:2/112 dev pc1r1 8 | ip netns exec PC1 ip l set pc1r1 up 9 | ip netns exec PC1 ip -6 r add default via fd00::1:1 10 | 11 | rm -rf ../server/test6 12 | echo "content of test6, standard client & custom server" > ../client/test6 13 | cd ../client && ip netns exec PC1 sh -c "echo \"put test6\" | tftp fd00::3:2" 14 | cat ../server/test6 -------------------------------------------------------------------------------- /Setup/tftp/test/test7.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Test 7 3 | # Custom client <- Custom server 4 | # You need to ensure that a custom server is running 5 | rm -rf ../client/test7 6 | echo "content of test7, custom client & custom server" > ../server/test7 7 | cd ../client && ip netns exec PC1 ../../../Homework/tftp/pc1/client get fd00::3:2 test7 8 | cat ../client/test7 -------------------------------------------------------------------------------- /Setup/tftp/test/test8.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Test 8 3 | # Custom client -> Custom server 4 | # You need to ensure that a custom server is running 5 | rm -rf ../server/test8 6 | echo "content of test8, custom client & custom server" > ../client/test8 7 | cd ../client && ip netns exec PC1 ../../../Homework/tftp/pc1/client put fd00::3:2 test8 8 | cat ../server/test8 -------------------------------------------------------------------------------- /Setup/tftp/test/test9.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Test 9 3 | # Custom client <- Custom server 4 | # Benchmark 5 | # You need to ensure that a custom server is running 6 | rm -rf ../client/test9 7 | dd if=/dev/zero of=../server/test9 bs=1024 count=1024 8 | cd ../client && time ip netns exec PC1 ../../../Homework/tftp/pc1/client get fd00::3:2 test9 9 | ls -alh ../client/test9 --------------------------------------------------------------------------------