├── .github └── workflows │ └── c-cpp.yml ├── .gitignore ├── .gitmodules ├── Makefile ├── README.md ├── arp.cpp ├── arp.h ├── binary_trie.h ├── config.cpp ├── config.h ├── dpdk ├── Makefile └── dpdk_main.cpp ├── ethernet.cpp ├── ethernet.h ├── icmp.cpp ├── icmp.h ├── icmpv6.cpp ├── icmpv6.h ├── ip.cpp ├── ip.h ├── ipv6.cpp ├── ipv6.h ├── log.h ├── my_buf.h ├── nat.cpp ├── nat.h ├── nd.cpp ├── nd.h ├── net.cpp ├── net.h ├── pf_packet ├── Makefile └── main.cpp ├── utils.cpp └── utils.h /.github/workflows/c-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: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v3 16 | - name: make 17 | run: make 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | cmake-build-debug 2 | .idea 3 | build 4 | .vscode -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "xdp"] 2 | path = xdp 3 | url = https://github.com/kametan0730/xdp-tutorial.git 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #!/bin/make 2 | OUTDIR = ./build 3 | SOURCES = $(wildcard *.cpp) 4 | OBJECTS = $(addprefix $(OUTDIR)/, $(SOURCES:.cpp=.o)) 5 | 6 | STATIC_LIB = $(OUTDIR)/libcuro.a 7 | SHARED_LIB = $(OUTDIR)/libcuro.so 8 | 9 | CFLAGS = -fPIC 10 | 11 | PLATFORM = pf_packet 12 | 13 | .PHONY: all 14 | all: $(STATIC_LIB) $(SHARED_LIB) 15 | 16 | .PHONY: static 17 | static: $(STATIC_LIB) 18 | 19 | .PHONY: shared 20 | shared: $(SHARED_LIB) 21 | 22 | .PHONY: clean 23 | clean: 24 | $(RM) $(OBJECTS) $(STATIC_LIB) $(SHARED_LIB) 25 | 26 | .PHONY: run 27 | run: $(TARGET) 28 | make -C $(PLATFORM) run 29 | 30 | $(STATIC_LIB): $(OBJECTS) Makefile 31 | $(AR) rcs $(STATIC_LIB) $(OBJECTS) 32 | 33 | $(SHARED_LIB): $(OBJECTS) Makefile 34 | $(CXX) $(CFLAGS) -shared -o $(SHARED_LIB) $(OBJECTS) 35 | 36 | 37 | $(OUTDIR)/%.o: %.cpp Makefile 38 | mkdir -p build 39 | $(CXX) $(CFLAGS) -o $@ -c $< 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # curo 2 | curo = curonos - nos 3 | 4 | curo is a software router 5 | 6 | ## Functions 7 | 8 | ### Platform 9 | 10 | - [x] PF_PACKET (Raw socket for linux) 11 | - [x] DPDK(Data Plane Development Kit) 12 | - [ ] XDP (eXpress Data Path) 13 | 14 | ### Network 15 | - [x] ARP Reply 16 | - [x] ARP Request 17 | - [x] Trie IP Routing Table 18 | - [x] IP Forwarding 19 | - [x] ICMP Echo Reply 20 | - [x] ICMP Time Exceeded 21 | - [x] ICMP Destination Unreachable 22 | - [x] NAT (UDP,TCP,ICMP(Query)) 23 | - [x] NAT (ICMP(Error)) 24 | 25 | ## Build 26 | ```shell 27 | sudo apt install build-essential 28 | git clone https://github.com/kametan0730/curo.git 29 | cd curo 30 | make all 31 | ``` 32 | 33 | ## Example (for PF_PACKET) 34 | ### 0. Download sources and build 35 | 36 | ### 1. Create environment with network namespace 37 | ```shell 38 | ip netns add host1 39 | ip netns add router1 40 | ip netns add router2 41 | ip netns add host2 42 | 43 | ip link add name host1-router1 type veth peer name router1-host1 44 | ip link add name router1-router2 type veth peer name router2-router1 45 | ip link add name router2-host2 type veth peer name host2-router2 46 | 47 | ip link set host1-router1 netns host1 48 | ip link set router1-host1 netns router1 49 | ip link set router1-router2 netns router1 50 | ip link set router2-router1 netns router2 51 | ip link set router2-host2 netns router2 52 | ip link set host2-router2 netns host2 53 | 54 | ip netns exec host1 ip addr add 192.168.1.2/24 dev host1-router1 55 | ip netns exec host1 ip link set host1-router1 up 56 | ip netns exec host1 ethtool -K host1-router1 rx off tx off 57 | ip netns exec host1 ip route add default via 192.168.1.1 58 | 59 | ip netns exec router1 ip link set router1-host1 up 60 | ip netns exec router1 ethtool -K router1-host1 rx off tx off 61 | ip netns exec router1 ip link set router1-router2 up 62 | ip netns exec router1 ethtool -K router1-router2 rx off tx off 63 | 64 | ip netns exec router2 ip addr add 192.168.0.2/24 dev router2-router1 65 | ip netns exec router2 ip link set router2-router1 up 66 | ip netns exec router2 ethtool -K router2-router1 rx off tx off 67 | ip netns exec router2 ip route add default via 192.168.0.1 68 | ip netns exec router2 ip addr add 192.168.2.1/24 dev router2-host2 69 | ip netns exec router2 ip link set router2-host2 up 70 | ip netns exec router2 ethtool -K router2-host2 rx off tx off 71 | ip netns exec router2 sysctl -w net.ipv4.ip_forward=1 72 | 73 | ip netns exec host2 ip addr add 192.168.2.2/24 dev host2-router2 74 | ip netns exec host2 ip link set host2-router2 up 75 | ip netns exec host2 ethtool -K host2-router2 rx off tx off 76 | ip netns exec host2 ip route add default via 192.168.2.1 77 | ``` 78 | 79 | ### 2. Config 80 | #### Open pf_packet/main.cpp and change configure function as follows 81 | ```cpp 82 | void configure(){ 83 | configure_ip_address(get_net_device_by_name("router1-host1"), IP_ADDRESS(192, 168, 1, 1), IP_ADDRESS(255, 255, 255, 0)); 84 | configure_ip_address(get_net_device_by_name("router1-router2"), IP_ADDRESS(192, 168, 0, 1), IP_ADDRESS(255, 255, 255, 0)); 85 | configure_ip_net_route(IP_ADDRESS(192, 168, 2, 0), 24, IP_ADDRESS(192, 168, 0, 2)); 86 | } 87 | ``` 88 | 89 | ## 3. Execute 90 | ### 3.1 Join router1 namespace 91 | ```shell 92 | sudo ip netns exec router1 bash 93 | ``` 94 | 95 | ### 3.2 Move the directory to where curo is located 96 | 97 | ### 3.3 Execute 98 | ```shell 99 | make run 100 | ``` 101 | 102 | ## 4. Enjoy! 103 | ### Ready two terminals 104 | ```shell 105 | sudo ip netns exec host1 iperf3 -s 106 | ``` 107 | ```shell 108 | sudo ip netns exec host2 iperf3 -c 192.168.1.2 109 | ``` 110 | -------------------------------------------------------------------------------- /arp.cpp: -------------------------------------------------------------------------------- 1 | #include "arp.h" 2 | 3 | #include "config.h" 4 | #include "ethernet.h" 5 | #include "ip.h" 6 | #include "log.h" 7 | #include "my_buf.h" 8 | #include "net.h" 9 | #include "utils.h" 10 | #include 11 | 12 | /** 13 | * ARPテーブル 14 | * グローバル変数にテーブルを保持 15 | */ 16 | arp_table_entry arp_table[ARP_TABLE_SIZE]; 17 | 18 | /** 19 | * ARPテーブルにエントリの追加と更新 20 | * @param dev 21 | * @param mac_addr 22 | * @param ip_addr 23 | */ 24 | void add_arp_table_entry(net_device *dev, uint8_t *mac_addr, uint32_t ip_addr) { 25 | // 候補の場所は、HashテーブルのIPアドレスのハッシュがindexのもの 26 | const uint32_t index = ip_addr % ARP_TABLE_SIZE; 27 | arp_table_entry *candidate = &arp_table[index]; 28 | 29 | // テーブルに入れられるか確認 30 | if (candidate->ip_addr == 0 or 31 | candidate->ip_addr == ip_addr) { // 初めの候補の場所に入れられるとき 32 | // エントリをセット 33 | memcpy(candidate->mac_addr, mac_addr, 6); 34 | candidate->ip_addr = ip_addr; 35 | candidate->dev = dev; 36 | return; 37 | } 38 | 39 | // 入れられなかった場合は、その候補にあるエントリに連結する 40 | while (candidate->next != nullptr) { // 連結リストの末尾までたどる 41 | candidate = candidate->next; 42 | // 途中で同じIPアドレスのエントリがあったら、そのエントリを更新する 43 | if (candidate->ip_addr == ip_addr) { 44 | memcpy(candidate->mac_addr, mac_addr, 6); 45 | candidate->ip_addr = ip_addr; 46 | candidate->dev = dev; 47 | return; 48 | } 49 | } 50 | // 連結リストの末尾に新しくエントリを作成 51 | candidate->next = (arp_table_entry *)calloc(1, sizeof(arp_table_entry)); 52 | memcpy(candidate->next->mac_addr, mac_addr, 6); 53 | candidate->next->ip_addr = ip_addr; 54 | candidate->next->dev = dev; 55 | } 56 | 57 | /** 58 | * ARPテーブルの検索 59 | * @param ip_addr 60 | */ 61 | arp_table_entry *search_arp_table_entry(uint32_t ip_addr) { 62 | // 初めの候補の場所は、HashテーブルのIPアドレスのハッシュがindexのもの 63 | arp_table_entry *candidate = &arp_table[ip_addr % ARP_TABLE_SIZE]; 64 | 65 | if (candidate->ip_addr == 66 | ip_addr) { // 候補のエントリが検索しているIPアドレスの物だったら 67 | return candidate; 68 | } else if (candidate->ip_addr == 69 | 0) { // 候補のエントリが登録されていなかったら 70 | return nullptr; 71 | } 72 | // 候補のエントリが検索しているIPアドレスの物でなかった場合、そのエントリの連結リストを調べる 73 | while (candidate->next != nullptr) { 74 | candidate = candidate->next; 75 | if (candidate->ip_addr == 76 | ip_addr) { // 連結リストの中に検索しているIPアドレスの物があったら 77 | return candidate; 78 | } 79 | } 80 | 81 | // 連結リストの中に見つからなかったら 82 | return nullptr; 83 | } 84 | 85 | /** 86 | * ARPテーブルの出力 87 | */ 88 | void dump_arp_table_entry() { 89 | printf("|---IP ADDRESS----|----MAC " 90 | "ADDRESS----|------DEVICE-------|-INDEX-|\n"); 91 | for (int i = 0; i < ARP_TABLE_SIZE; ++i) { 92 | if (arp_table[i].ip_addr == 0) { 93 | continue; 94 | } 95 | // エントリの連結リストを順に出力する 96 | for (arp_table_entry *entry = &arp_table[i]; entry; 97 | entry = entry->next) { 98 | printf("| %15s | %14s | %17s | %04d |\n", ip_htoa(entry->ip_addr), 99 | mac_addr_toa(entry->mac_addr), entry->dev->name, i); 100 | } 101 | } 102 | printf("|-----------------|-------------------|-------------------|-------|" 103 | "\n"); 104 | } 105 | 106 | /** 107 | * ARPリクエストの送信 108 | * @param dev 109 | * @param search_ip 110 | */ 111 | void send_arp_request(net_device *dev, uint32_t ip_addr) { 112 | LOG_ARP("Sending arp request via %s for %s\n", dev->name, ip_htoa(ip_addr)); 113 | 114 | auto *arp_mybuf = my_buf::create(ARP_ETHERNET_PACKET_LEN); 115 | auto *arp_msg = reinterpret_cast(arp_mybuf->buffer); 116 | arp_msg->htype = htons(ARP_HTYPE_ETHERNET); // ハードウェアタイプの設定 117 | arp_msg->ptype = htons(ETHER_TYPE_IP); // プロトコルタイプの設定 118 | arp_msg->hlen = ETHERNET_ADDRESS_LEN; // ハードウェアアドレス帳の設定 119 | arp_msg->plen = IP_ADDRESS_LEN; // プロトコルアドレス長の設定 120 | arp_msg->op = 121 | htons(ARP_OPERATION_CODE_REQUEST); // オペレーションコードの設定 122 | memcpy(arp_msg->sha, dev->mac_addr, 123 | 6); // 送信者ハードウェアアドレスにデバイスのMACアドレスを設定 124 | arp_msg->spa = htonl( 125 | dev->ip_dev 126 | ->address); // 送信者プロトコルアドレスにデバイスのIPアドレスを設定 127 | arp_msg->tpa = htonl( 128 | ip_addr); // ターゲットプロトコルアドレスに、探すホストのIPアドレスを設定 129 | 130 | // イーサネットで送信する 131 | ethernet_encapsulate_output(dev, ETHERNET_ADDRESS_BROADCAST, arp_mybuf, 132 | ETHER_TYPE_ARP); 133 | } 134 | 135 | // 宣言のみ 136 | void arp_request_arrives(net_device *dev, arp_ip_to_ethernet *request); 137 | void arp_reply_arrives(net_device *dev, arp_ip_to_ethernet *reply); 138 | 139 | /** 140 | * ARPパケットの受信処理 141 | * @param input_dev 142 | * @param buffer 143 | * @param len 144 | */ 145 | void arp_input(net_device *input_dev, uint8_t *buffer, ssize_t len) { 146 | // ARPパケットの想定より短かったら 147 | if (len < sizeof(arp_ip_to_ethernet)) { 148 | LOG_ARP("Too short arp packet\n"); 149 | return; 150 | } 151 | 152 | auto *arp_msg = reinterpret_cast(buffer); 153 | uint16_t op = ntohs(arp_msg->op); 154 | 155 | switch (ntohs(arp_msg->ptype)) { 156 | case ETHER_TYPE_IP: 157 | 158 | if (arp_msg->hlen != ETHERNET_ADDRESS_LEN) { 159 | LOG_ARP("Illegal hardware address length\n"); 160 | return; 161 | } 162 | 163 | if (arp_msg->plen != IP_ADDRESS_LEN) { 164 | LOG_ARP("Illegal protocol address length\n"); 165 | return; 166 | } 167 | 168 | // オペレーションコードによって分岐 169 | if (op == ARP_OPERATION_CODE_REQUEST) { 170 | // ARPリクエストの受信 171 | arp_request_arrives(input_dev, arp_msg); 172 | return; 173 | } else if (op == ARP_OPERATION_CODE_REPLY) { 174 | // ARPリプライの受信 175 | arp_reply_arrives(input_dev, arp_msg); 176 | return; 177 | } 178 | break; 179 | } 180 | } 181 | 182 | /** 183 | * ARPリクエストパケットの受信処理 184 | * @param dev 185 | * @param request 186 | */ 187 | void arp_request_arrives(net_device *dev, arp_ip_to_ethernet *request) { 188 | if (dev->ip_dev != nullptr and 189 | dev->ip_dev->address != 190 | IP_ADDRESS( 191 | 0, 0, 0, 192 | 0)) { // IPアドレスが設定されているデバイスからの受信だったら 193 | if (dev->ip_dev->address == 194 | ntohl(request->tpa)) { // 要求されているアドレスが自分の物だったら 195 | LOG_ARP("Sending arp reply via %s\n", ip_ntoa(request->tpa)); 196 | 197 | auto *reply_mybuf = my_buf::create(ARP_ETHERNET_PACKET_LEN); 198 | 199 | auto reply_msg = 200 | reinterpret_cast(reply_mybuf->buffer); 201 | reply_msg->htype = htons(ARP_HTYPE_ETHERNET); 202 | reply_msg->ptype = htons(ETHER_TYPE_IP); 203 | reply_msg->hlen = ETHERNET_ADDRESS_LEN; // IPアドレスの長さ 204 | reply_msg->plen = IP_ADDRESS_LEN; // MACアドレスの長さ 205 | reply_msg->op = htons(ARP_OPERATION_CODE_REPLY); 206 | 207 | // 返答の情報を書き込む 208 | memcpy(reply_msg->sha, dev->mac_addr, ETHERNET_ADDRESS_LEN); 209 | reply_msg->spa = htonl(dev->ip_dev->address); 210 | memcpy(reply_msg->tha, request->sha, ETHERNET_ADDRESS_LEN); 211 | reply_msg->tpa = request->spa; 212 | 213 | ethernet_encapsulate_output(dev, request->sha, reply_mybuf, 214 | ETHER_TYPE_ARP); // イーサネットで送信 215 | add_arp_table_entry( 216 | dev, request->sha, 217 | ntohl(request->spa)); // ARPリクエストからもエントリを生成 218 | return; 219 | } 220 | } 221 | } 222 | 223 | /** 224 | * ARPリプライパケットの受信処理 225 | * @param dev 226 | * @param reply 227 | */ 228 | void arp_reply_arrives(net_device *dev, arp_ip_to_ethernet *reply) { 229 | if (dev->ip_dev != nullptr and 230 | dev->ip_dev->address != 231 | IP_ADDRESS( 232 | 0, 0, 0, 233 | 0)) { // IPアドレスが設定されているデバイスからの受信だったら 234 | LOG_ARP("Added arp table entry by arp reply (%s => %s)\n", 235 | ip_ntoa(reply->spa), mac_addr_toa(reply->sha)); 236 | add_arp_table_entry(dev, reply->sha, 237 | ntohl(reply->spa)); // ARPテーブルエントリの追加 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /arp.h: -------------------------------------------------------------------------------- 1 | #ifndef CURO_ARP_H 2 | #define CURO_ARP_H 3 | 4 | #include 5 | 6 | #define GOLDEN_RATIO_32 0x61C88647 7 | #define GOLDEN_RATIO_64 0x61C8864680B583EBull 8 | 9 | #define ARP_HTYPE_ETHERNET 0x0001 10 | 11 | #define ARP_OPERATION_CODE_REQUEST 0x0001 12 | #define ARP_OPERATION_CODE_REPLY 0x0002 13 | 14 | #define ARP_ETHERNET_PACKET_LEN 46 15 | 16 | struct arp_ip_to_ethernet { 17 | uint16_t htype; // ハードウェアタイプ 18 | uint16_t ptype; // プロトコルタイプ 19 | uint8_t hlen; // ハードウェアアドレス帳 20 | uint8_t plen; // プロトコルアドレス帳 21 | uint16_t op; // オペレーションコード 22 | uint8_t sha[6]; // 送信者のハードウェアアドレス 23 | uint32_t spa; // 送信者のプロトコルアドレス 24 | uint8_t tha[6]; // ターゲットのハードウェアアドレス 25 | uint32_t tpa; // ターゲットのプロトコルアドレス 26 | } __attribute__((packed)); 27 | 28 | #define ARP_TABLE_SIZE 1111 29 | 30 | struct net_device; 31 | 32 | struct arp_table_entry { 33 | uint8_t mac_addr[6]; 34 | uint32_t ip_addr; 35 | net_device *dev; 36 | arp_table_entry *next; 37 | }; 38 | 39 | void add_arp_table_entry(net_device *dev, uint8_t *mac_addr, uint32_t ip_addr); 40 | 41 | arp_table_entry *search_arp_table_entry(uint32_t ip_addr); 42 | 43 | void dump_arp_table_entry(); 44 | 45 | void send_arp_request(net_device *dev, uint32_t ip_addr); 46 | 47 | void arp_input(net_device *input_dev, uint8_t *buffer, ssize_t len); 48 | 49 | #endif // CURO_ARP_H 50 | -------------------------------------------------------------------------------- /binary_trie.h: -------------------------------------------------------------------------------- 1 | #ifndef CURO_BINARY_TRIE_H 2 | #define CURO_BINARY_TRIE_H 3 | 4 | #include 5 | #include 6 | 7 | #define IP_BIT_LEN 32 8 | 9 | template 10 | struct binary_trie_node { // 二分トライ木構造のノード 11 | DATA_TYPE *data; // 保持するデータ 12 | uint32_t depth; // ルートノードからの深さ 13 | binary_trie_node *parent; // 親ノード 14 | binary_trie_node *node_0; // 0側の子ノード 15 | binary_trie_node *node_1; // 1側の子ノード 16 | }; 17 | 18 | /** 19 | * 木構造にノードを作成します 20 | * @param DATA_TYPE 21 | * @param root 22 | * @param prefix 23 | * @param prefix_len 24 | * @param data 25 | */ 26 | template 27 | void binary_trie_add(binary_trie_node *root, uint32_t prefix, 28 | uint32_t prefix_len, DATA_TYPE *data) { 29 | binary_trie_node *current = root; // ルートノードから辿る 30 | // 枝を辿る 31 | for (int i = 1; i <= prefix_len; ++i) { 32 | if ((prefix >> (IP_BIT_LEN - i)) & 0x01) { // 上からiビット目が1だったら 33 | if (current->node_1 == nullptr) { // 辿る先の枝ががなかったら作る 34 | current->node_1 = (binary_trie_node *)calloc( 35 | 1, sizeof(binary_trie_node)); 36 | current->node_1->data = 0; 37 | current->node_1->depth = i; 38 | current->node_1->parent = current; 39 | } 40 | current = current->node_1; 41 | } else { // 上からiビット目が0だったら 42 | if (current->node_0 == nullptr) { // 辿る先の枝ががなかったら作る 43 | current->node_0 = (binary_trie_node *)calloc( 44 | 1, sizeof(binary_trie_node)); 45 | current->node_0->data = 0; 46 | current->node_0->depth = i; 47 | current->node_0->parent = current; 48 | } 49 | current = current->node_0; 50 | } 51 | } 52 | 53 | current->data = data; // データをセット 54 | } 55 | 56 | /** 57 | * プレフィックスからトライ木を検索します 58 | * @tparam DATA_TYPE 59 | * @param root 60 | * @param prefix 61 | * @return 62 | */ 63 | template 64 | DATA_TYPE *binary_trie_search(binary_trie_node *root, 65 | uint32_t prefix) { // 検索 66 | binary_trie_node *current = root; // ルートノードから辿る 67 | DATA_TYPE *result = nullptr; 68 | // 検索するIPアドレスと比較して1ビットずつ辿っていく 69 | for (int i = 1; i <= IP_BIT_LEN; ++i) { 70 | if (current->data != nullptr) { 71 | result = current->data; 72 | } 73 | if ((prefix >> (IP_BIT_LEN - i)) & 0x01) { // 上からiビット目が1だったら 74 | if (current->node_1 == nullptr) { 75 | return result; 76 | } 77 | current = current->node_1; 78 | } else { // 1ビット目が0だったら 79 | if (current->node_0 == nullptr) { 80 | return result; 81 | } 82 | current = current->node_0; 83 | } 84 | } 85 | return result; 86 | } 87 | 88 | /** 89 | * ノード情報から、プレフィックスを特定します 90 | * @tparam DATA_TYPE 91 | * @param target 92 | * @param root 93 | * @return 94 | */ 95 | template 96 | uint32_t 97 | locate_prefix(binary_trie_node *target, 98 | binary_trie_node *root) { // ノードから位置を特定 99 | uint8_t len = target->depth; 100 | uint32_t result = 0; 101 | while (target != root) { 102 | if (target->parent->node_1 == target) { 103 | result |= (1 << (32 - len)); 104 | } 105 | len--; 106 | target = target->parent; // 上にたどっていく 107 | } 108 | return result; 109 | } 110 | 111 | #endif // CURO_BINARY_TRIE_H 112 | -------------------------------------------------------------------------------- /config.cpp: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | 3 | #include "binary_trie.h" 4 | #include "ip.h" 5 | #include "ipv6.h" 6 | #include "log.h" 7 | #include "nat.h" 8 | #include "net.h" 9 | #include "utils.h" 10 | 11 | /** 12 | * 経路を設定 13 | * @param prefix 宛先ネットワークのプレフィックス 14 | * @param prefix_len プレフィックス長 15 | * @param next_hop 転送先 16 | */ 17 | void configure_ip_net_route(uint32_t prefix, uint32_t prefix_len, 18 | uint32_t next_hop) { 19 | // プレフィックス長とネットマスクの変換 20 | uint32_t mask = 0xffffffff; 21 | mask <<= (32 - prefix_len); 22 | 23 | // 経路エントリの生成 24 | ip_route_entry *entry; 25 | entry = (ip_route_entry *)(calloc(1, sizeof(ip_route_entry))); 26 | entry->type = network; 27 | entry->next_hop = next_hop; 28 | 29 | // 経路の登録 30 | binary_trie_add(ip_fib, prefix & mask, prefix_len, entry); 31 | } 32 | 33 | /** 34 | * デバイスにIPアドレスを設定 35 | * @param dev 36 | * @param address 37 | * @param netmask 38 | */ 39 | void configure_ip_address(net_device *dev, uint32_t address, uint32_t netmask) { 40 | if (dev == nullptr) { 41 | LOG_ERROR("Configure net dev not found\n"); 42 | exit(EXIT_FAILURE); 43 | } 44 | 45 | // IPアドレスの登録 46 | dev->ip_dev = (ip_device *)calloc(1, sizeof(ip_device)); 47 | dev->ip_dev->address = address; 48 | dev->ip_dev->netmask = netmask; 49 | dev->ip_dev->broadcast = (address & netmask) | (~netmask); 50 | 51 | printf("Set ip address to %s\n", dev->name); 52 | 53 | // IPアドレスを設定すると同時に直接接続ルートを設定する 54 | ip_route_entry *entry; 55 | entry = (ip_route_entry *)calloc(1, sizeof(ip_route_entry)); 56 | entry->type = connected; 57 | entry->dev = dev; 58 | 59 | int len = 0; // サブネットマスクとプレフィックス長の変換 60 | for (; len < 32; ++len) { 61 | if (!(netmask >> (31 - len) & 0b01)) { 62 | break; 63 | } 64 | } 65 | 66 | // 直接接続ネットワークの経路を設定 67 | binary_trie_add(ip_fib, address & netmask, len, entry); 68 | 69 | printf("Set directly connected route %s/%d device %s\n", 70 | ip_htoa(address & netmask), len, dev->name); 71 | } 72 | 73 | /** 74 | * デバイスにNATを設定 75 | * @param inside NATの内側のデバイス 76 | * @param outside NATの外側のデバイス 77 | */ 78 | void configure_ip_nat(net_device *inside, net_device *outside) { 79 | #ifdef ENABLE_NAT 80 | if (inside == nullptr or outside == nullptr or inside->ip_dev == nullptr or 81 | outside->ip_dev == nullptr) { 82 | LOG_ERROR("Failed to configure NAT %s => %s\n", inside->name, 83 | outside->name); 84 | exit(EXIT_FAILURE); // プログラムを終了 85 | } 86 | 87 | inside->ip_dev->nat_dev = (nat_device *)calloc(1, sizeof(nat_device)); 88 | inside->ip_dev->nat_dev->entries = 89 | (nat_entries *)calloc(1, sizeof(nat_entries)); 90 | inside->ip_dev->nat_dev->outside_addr = outside->ip_dev->address; 91 | #else 92 | LOG_ERROR("NAT has not been enabled for this build\n"); 93 | exit(EXIT_FAILURE); 94 | #endif 95 | } 96 | 97 | 98 | /** 99 | * デバイスにIPアドレスを設定 100 | * @param dev 101 | * @param address 102 | * @param netmask 103 | */ 104 | void configure_ipv6_address(net_device *dev, ipv6_addr address, uint32_t prefix_len) { 105 | if (dev == nullptr) { 106 | LOG_ERROR("Configure net dev not found\n"); 107 | exit(EXIT_FAILURE); 108 | } 109 | 110 | // IPアドレスの登録 111 | dev->ipv6_dev = (ipv6_device *)calloc(1, sizeof(ipv6_device)); 112 | dev->ipv6_dev->address = address; 113 | dev->ipv6_dev->prefix_len = prefix_len; 114 | dev->ipv6_dev->net_dev = dev; 115 | 116 | printf("Set ipv6 address to %s\n", ipv6toascii(address)); 117 | } -------------------------------------------------------------------------------- /config.h: -------------------------------------------------------------------------------- 1 | #ifndef CURO_CONFIG_H 2 | #define CURO_CONFIG_H 3 | 4 | #include 5 | #include 6 | 7 | /* 8 | * 各プロトコルについてデバッグレベルを設定できます 9 | * 10 | * 0 No debug 11 | * 1 Print debug message 12 | */ 13 | 14 | #define DEBUG_ETHERNET 1 15 | #define DEBUG_IP 1 16 | #define DEBUG_ARP 1 17 | #define DEBUG_ICMP 1 18 | #define DEBUG_NAT 1 19 | #define DEBUG_IPV6 1 20 | 21 | // #define ENABLE_MYBUF_NON_COPY_MODE // パケット転送時にバッファのコピーを削減するか 22 | #define ENABLE_NAT // NATを有効にするか 23 | #define ENABLE_ICMP_ERROR // ICMPエラーを送信するか 24 | #define ENABLE_COMMAND // 対話的なコマンドを有効化するか 25 | #define ENABLE_IPV6 // IPv6を有効にするか 26 | /* 27 | #define DEBUG_ETHERNET 0 28 | #define DEBUG_IP 0 29 | #define DEBUG_ARP 0 30 | #define DEBUG_ICMP 0 31 | */ 32 | 33 | struct net_device; 34 | 35 | void configure_ip_net_route(uint32_t prefix, uint32_t prefix_len, 36 | uint32_t next_hop); 37 | 38 | void configure_ip_address(net_device *dev, uint32_t address, uint32_t netmask); 39 | void configure_ip_nat(net_device *inside, net_device *outside); 40 | 41 | struct ipv6_addr; 42 | 43 | void configure_ipv6_address(net_device *dev, ipv6_addr address, uint32_t prefix_len); 44 | 45 | #endif // CURO_CONFIG_H 46 | -------------------------------------------------------------------------------- /dpdk/Makefile: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: BSD-3-Clause 2 | # Copyright(c) 2010-2014 Intel Corporation 3 | 4 | # binary name 5 | APP = curo-dpdk 6 | 7 | # all source are stored in SRCS-y 8 | SOURCES := dpdk_main.cpp 9 | 10 | LIBCURO_STATIC = ../build/libcuro.a 11 | 12 | PKGCONF ?= pkg-config 13 | 14 | # Build using pkg-config variables if possible 15 | ifneq ($(shell $(PKGCONF) --exists libdpdk && echo 0),0) 16 | $(error "no installation of DPDK found") 17 | endif 18 | 19 | all: shared 20 | .PHONY: shared static 21 | shared: build/$(APP)-shared 22 | ln -sf $(APP)-shared build/$(APP) 23 | static: build/$(APP)-static 24 | ln -sf $(APP)-static build/$(APP) 25 | 26 | PC_FILE := $(shell $(PKGCONF) --path libdpdk 2>/dev/null) 27 | CPPFLAGS += -O3 $(shell $(PKGCONF) --cflags libdpdk) 28 | LDFLAGS_SHARED = $(shell $(PKGCONF) --libs libdpdk) 29 | LDFLAGS_STATIC = $(shell $(PKGCONF) --static --libs libdpdk) 30 | 31 | ifeq ($(MAKECMDGOALS),static) 32 | # check for broken pkg-config 33 | ifeq ($(shell echo $(LDFLAGS_STATIC) | grep 'whole-archive.*l:lib.*no-whole-archive'),) 34 | $(warning "pkg-config output list does not contain drivers between 'whole-archive'/'no-whole-archive' flags.") 35 | $(error "Cannot generate statically-linked binaries with this version of pkg-config") 36 | endif 37 | endif 38 | 39 | CPPFLAGS += -DALLOW_EXPERIMENTAL_API 40 | 41 | build/$(APP)-shared: $(SOURCES) Makefile $(PC_FILE) | build 42 | $(CXX) $(CPPFLAGS) $(SOURCES) $(LIBCURO_STATIC) -o $@ $(LDFLAGS) $(LDFLAGS_SHARED) 43 | 44 | build/$(APP)-static: $(SOURCES) Makefile $(PC_FILE) | build 45 | $(CXX) $(CPPFLAGS) $(SOURCES) $(LIBCURO_STATIC) -o $@ $(LDFLAGS) $(LDFLAGS_STATIC) 46 | 47 | build: 48 | @mkdir -p $@ 49 | 50 | .PHONY: clean 51 | clean: 52 | rm -f build/$(APP) build/$(APP)-static build/$(APP)-shared 53 | test -d build && rmdir -p build || true 54 | -------------------------------------------------------------------------------- /dpdk/dpdk_main.cpp: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: BSD-3-Clause 2 | * Copyright(c) 2010-2015 Intel Corporation 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "../binary_trie.h" 14 | #include "../ethernet.h" 15 | #include "../ip.h" 16 | #include "../net.h" 17 | #include "../utils.h" 18 | 19 | #define RX_RING_SIZE 1024 20 | #define TX_RING_SIZE 1024 21 | 22 | #define NUM_MBUFS 8191 23 | #define MBUF_CACHE_SIZE 250 24 | 25 | struct rte_mempool *mbuf_pool; 26 | 27 | int net_device_transmit(struct net_device *dev, uint8_t *buffer, 28 | size_t len); // 宣言のみ 29 | int net_device_poll(net_device *dev); // 宣言のみ 30 | 31 | /** 32 | * デバイスのプラットフォーム依存のデータ 33 | */ 34 | struct net_device_data { 35 | int port; // DPDKのポート 36 | }; 37 | 38 | /* 39 | * Initializes a given port using global settings and with the RX buffers 40 | * coming from the mbuf_pool passed as a parameter. 41 | */ 42 | 43 | /* Main functional part of port initialization. 8< */ 44 | static inline int port_init(uint16_t port, struct rte_mempool *mbuf_pool) { 45 | struct rte_eth_conf port_conf; 46 | const uint16_t rx_rings = 1, tx_rings = 1; 47 | uint16_t nb_rxd = RX_RING_SIZE; 48 | uint16_t nb_txd = TX_RING_SIZE; 49 | int retval; 50 | uint16_t q; 51 | struct rte_eth_dev_info dev_info; 52 | struct rte_eth_txconf txconf; 53 | 54 | if (!rte_eth_dev_is_valid_port(port)) return -1; 55 | 56 | memset(&port_conf, 0, sizeof(struct rte_eth_conf)); 57 | 58 | retval = rte_eth_dev_info_get(port, &dev_info); 59 | if (retval != 0) { 60 | printf("Error during getting device (port %u) info: %s\n", port, 61 | strerror(-retval)); 62 | return retval; 63 | } 64 | 65 | if (dev_info.tx_offload_capa & RTE_ETH_TX_OFFLOAD_MBUF_FAST_FREE) 66 | port_conf.txmode.offloads |= RTE_ETH_TX_OFFLOAD_MBUF_FAST_FREE; 67 | 68 | /* Configure the Ethernet device. */ 69 | retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf); 70 | if (retval != 0) return retval; 71 | 72 | retval = rte_eth_dev_adjust_nb_rx_tx_desc(port, &nb_rxd, &nb_txd); 73 | if (retval != 0) return retval; 74 | 75 | /* Allocate and set up 1 RX queue per Ethernet port. */ 76 | for (q = 0; q < rx_rings; q++) { 77 | retval = rte_eth_rx_queue_setup( 78 | port, q, nb_rxd, rte_eth_dev_socket_id(port), NULL, mbuf_pool); 79 | if (retval < 0) return retval; 80 | } 81 | 82 | txconf = dev_info.default_txconf; 83 | txconf.offloads = port_conf.txmode.offloads; 84 | /* Allocate and set up 1 TX queue per Ethernet port. */ 85 | for (q = 0; q < tx_rings; q++) { 86 | retval = rte_eth_tx_queue_setup(port, q, nb_txd, 87 | rte_eth_dev_socket_id(port), &txconf); 88 | if (retval < 0) return retval; 89 | } 90 | 91 | /* Starting Ethernet port. 8< */ 92 | retval = rte_eth_dev_start(port); 93 | /* >8 End of starting of ethernet port. */ 94 | if (retval < 0) return retval; 95 | 96 | /* Display the port MAC address. */ 97 | struct rte_ether_addr addr; 98 | retval = rte_eth_macaddr_get(port, &addr); 99 | if (retval != 0) return retval; 100 | 101 | printf("Port %u MAC: %02" PRIx8 " %02" PRIx8 " %02" PRIx8 " %02" PRIx8 102 | " %02" PRIx8 " %02" PRIx8 "\n", 103 | port, RTE_ETHER_ADDR_BYTES(&addr)); 104 | 105 | // net_device構造体を作成 106 | auto *dev = (net_device *)calloc( 107 | 1, 108 | sizeof(net_device) + 109 | sizeof( 110 | net_device_data)); // net_deviceの領域と、net_device_dataの領域を確保する 111 | dev->ops.transmit = net_device_transmit; // 送信用の関数を設定 112 | dev->ops.poll = net_device_poll; // 受信用の関数を設定 113 | 114 | sprintf(dev->name, "dpdk%d", 115 | port); // net_deviceにインターフェース名(dpdkX)をセット 116 | memcpy(dev->mac_addr, addr.addr_bytes, 117 | 6); // net_deviceにMACアドレスをセット 118 | ((net_device_data *)dev->data)->port = 119 | port; // プラットフォーム依存データをセットs 120 | 121 | printf("Created dev %s port %d address %s \n", dev->name, port, 122 | mac_addr_toa(dev->mac_addr)); 123 | 124 | // net_deviceの連結リストに連結させる 125 | net_device *next; 126 | next = net_dev_list; 127 | net_dev_list = dev; 128 | dev->next = next; 129 | 130 | /* Enable RX in promiscuous mode for the Ethernet device. */ 131 | retval = rte_eth_promiscuous_enable(port); 132 | /* End of setting RX port in promiscuous mode. */ 133 | if (retval != 0) return retval; 134 | 135 | return 0; 136 | } 137 | /* >8 End of main functional part of port initialization. */ 138 | 139 | /** 140 | * インターフェース名からデバイスを探す 141 | * @param interface 142 | * @return 143 | */ 144 | net_device *get_net_device_by_name(const char *interface) { 145 | net_device *dev; 146 | for (dev = net_dev_list; dev; dev = dev->next) { 147 | if (strcmp(dev->name, interface) == 0) { 148 | return dev; 149 | } 150 | } 151 | return nullptr; 152 | } 153 | 154 | /* 155 | * The lcore main. This is the main thread that does the work, reading from 156 | * an input port and writing to an output port. 157 | */ 158 | 159 | /* Basic forwarding application lcore. 8< */ 160 | static __rte_noreturn void lcore_main(void) { 161 | uint16_t port; 162 | 163 | /* 164 | * Check that the port is on the same NUMA node as the polling thread 165 | * for best performance. 166 | */ 167 | RTE_ETH_FOREACH_DEV(port) 168 | if (rte_eth_dev_socket_id(port) >= 0 && 169 | rte_eth_dev_socket_id(port) != (int)rte_socket_id()) 170 | printf("WARNING, port %u is on remote NUMA node to " 171 | "polling thread.\n\tPerformance will " 172 | "not be optimal.\n", 173 | port); 174 | 175 | printf("\nCore %u forwarding packets. [Ctrl+C to quit]\n", rte_lcore_id()); 176 | 177 | ip_fib = (binary_trie_node *)calloc( 178 | 1, sizeof(binary_trie_node)); 179 | 180 | configure_ip_address(get_net_device_by_name("dpdk0"), 181 | IP_ADDRESS(10, 0, 0, 2), IP_ADDRESS(255, 255, 255, 0)); 182 | configure_ip_address(get_net_device_by_name("dpdk1"), 183 | IP_ADDRESS(10, 0, 1, 2), IP_ADDRESS(255, 255, 255, 0)); 184 | 185 | /* Main work of application loop. 8< */ 186 | for (;;) { 187 | 188 | // デバイスから通信を受信 189 | for (net_device *dev = net_dev_list; dev; dev = dev->next) { 190 | dev->ops.poll(dev); 191 | } 192 | } 193 | /* >8 End of loop. */ 194 | } 195 | /* >8 End Basic forwarding application lcore. */ 196 | 197 | /* 198 | * The main function, which does initialization and calls the per-lcore 199 | * functions. 200 | */ 201 | int main(int argc, char *argv[]) { 202 | unsigned nb_ports; 203 | uint16_t portid; 204 | 205 | /* Initializion the Environment Abstraction Layer (EAL). 8< */ 206 | int ret = rte_eal_init(argc, argv); 207 | if (ret < 0) rte_exit(EXIT_FAILURE, "Error with EAL initialization\n"); 208 | /* >8 End of initialization the Environment Abstraction Layer (EAL). */ 209 | 210 | argc -= ret; 211 | argv += ret; 212 | 213 | /* Check that there is an even number of ports to send/receive on. */ 214 | nb_ports = rte_eth_dev_count_avail(); 215 | if (nb_ports == 0) 216 | rte_exit(EXIT_FAILURE, "Error: no network device found\n"); 217 | 218 | /* Creates a new mempool in memory to hold the mbufs. */ 219 | 220 | /* Allocates mempool to hold the mbufs. 8< */ 221 | mbuf_pool = rte_pktmbuf_pool_create( 222 | "MBUF_POOL", NUM_MBUFS * nb_ports, MBUF_CACHE_SIZE, 0, 223 | RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id()); 224 | /* >8 End of allocating mempool to hold mbuf. */ 225 | 226 | if (mbuf_pool == NULL) rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n"); 227 | 228 | /* Initializing all ports. 8< */ 229 | RTE_ETH_FOREACH_DEV(portid) 230 | if (port_init(portid, mbuf_pool) != 0) 231 | rte_exit(EXIT_FAILURE, "Cannot init port %" PRIu16 "\n", portid); 232 | /* >8 End of initializing all ports. */ 233 | 234 | if (rte_lcore_count() > 1) 235 | printf("\nWARNING: Too many lcores enabled. Only 1 used.\n"); 236 | 237 | /* Call lcore_main on the main core only. Called on single lcore. 8< */ 238 | lcore_main(); 239 | /* >8 End of called on single lcore. */ 240 | 241 | /* clean up the EAL */ 242 | rte_eal_cleanup(); 243 | 244 | return 0; 245 | } 246 | 247 | /** 248 | * ネットデバイスの送信処理 249 | * @param dev 250 | * @param buf 251 | * @return 252 | */ 253 | int net_device_transmit(struct net_device *dev, uint8_t *buffer, size_t len) { 254 | struct rte_mbuf *mbuf; 255 | mbuf = rte_pktmbuf_alloc(mbuf_pool); 256 | uint8_t *bbb_buf = rte_pktmbuf_mtod(mbuf, uint8_t *); 257 | mbuf->pkt_len = len; 258 | mbuf->buf_len = len; 259 | mbuf->data_len = len; 260 | 261 | memcpy(bbb_buf, buffer, len); 262 | 263 | const uint16_t nb_tx = 264 | rte_eth_tx_burst(((net_device_data *)dev->data)->port, 0, &mbuf, 1); 265 | if (nb_tx < 1) { 266 | rte_pktmbuf_free(mbuf); 267 | } 268 | return 0; 269 | } 270 | 271 | /** 272 | * ネットワークデバイスの受信処理 273 | * @param dev 274 | * @return 275 | */ 276 | int net_device_poll(net_device *dev) { 277 | struct rte_mbuf *buf; 278 | const uint16_t nb_rx = 279 | rte_eth_rx_burst(((net_device_data *)dev->data)->port, 0, &buf, 1); 280 | 281 | if (unlikely(nb_rx == 0)) return 0; 282 | // 受信したデータをイーサネットに送る 283 | ethernet_input(dev, rte_pktmbuf_mtod(buf, uint8_t *), buf->data_len); 284 | return 0; 285 | } -------------------------------------------------------------------------------- /ethernet.cpp: -------------------------------------------------------------------------------- 1 | #include "ethernet.h" 2 | 3 | #include "arp.h" 4 | #include "config.h" 5 | #include "ip.h" 6 | #include "ipv6.h" 7 | #include "log.h" 8 | #include "my_buf.h" 9 | #include "utils.h" 10 | #include 11 | 12 | /** 13 | * イーサネットの受信処理 14 | * @param dev 受信したデバイス 15 | * @param buffer 受信したデータのバイト列 16 | * @param len 受信したデータの長さ 17 | */ 18 | void ethernet_input(net_device *dev, uint8_t *buffer, ssize_t len) { 19 | // 送られてきた通信をイーサネットのフレームとして解釈する 20 | auto *header = reinterpret_cast(buffer); 21 | uint16_t ether_type = ntohs( 22 | header->type); // イーサタイプを抜き出すし、ホストバイトオーダーに変換 23 | 24 | // 自分のMACアドレス宛てかブロードキャスト/マルチキャストの通信かを確認する 25 | if (memcmp(header->dest_addr, dev->mac_addr, 6) != 0 and 26 | memcmp(header->dest_addr, ETHERNET_ADDRESS_BROADCAST, 6) != 0 and 27 | memcmp(header->dest_addr, ETHERNET_ADDRESS_IPV6_MCAST_PREFIX, 2) != 0 28 | ) { 29 | return; 30 | } 31 | 32 | LOG_ETHERNET("Received ethernet frame type %04x from %s to %s\n", 33 | ether_type, mac_addr_toa(header->src_addr), 34 | mac_addr_toa(header->dest_addr)); 35 | 36 | // イーサタイプの値から上位プロトコルを特定する 37 | switch (ether_type) { 38 | case ETHER_TYPE_ARP: // イーサタイプがARPのものだったら 39 | return arp_input( 40 | dev, buffer + ETHERNET_HEADER_SIZE, 41 | len - ETHERNET_HEADER_SIZE); // Ethernetヘッダを外してARP処理へ 42 | case ETHER_TYPE_IP: // イーサタイプがIPのものだったら 43 | return ip_input( 44 | dev, buffer + ETHERNET_HEADER_SIZE, 45 | len - ETHERNET_HEADER_SIZE); // Ethernetヘッダを外してIP処理へ 46 | #ifdef ENABLE_IPV6 47 | case ETHER_TYPE_IPV6: // イーサタイプがIPのものだったら 48 | return ipv6_input( 49 | dev, buffer + ETHERNET_HEADER_SIZE, 50 | len - ETHERNET_HEADER_SIZE); // Ethernetヘッダを外してIP処理へ 51 | #endif 52 | default: // 知らないイーサタイプだったら 53 | LOG_ETHERNET("Received unhandled ether type %04x\n", ether_type); 54 | return; 55 | } 56 | } 57 | 58 | /** 59 | * イーサネットにカプセル化して送信 60 | * @param dev 送信するデバイス 61 | * @param dest_addr 宛先アドレス 62 | * @param payload_mybuf 包んで送信するmy_buf構造体の先頭 63 | * @param ether_type イーサタイプ 64 | */ 65 | void ethernet_encapsulate_output(net_device *dev, const uint8_t *dest_addr, 66 | my_buf *payload_mybuf, uint16_t ether_type) { 67 | LOG_ETHERNET("Sending ethernet frame type %04x from %s to %s\n", ether_type, 68 | mac_addr_toa(dev->mac_addr), mac_addr_toa(dest_addr)); 69 | 70 | my_buf *header_mybuf = my_buf::create( 71 | ETHERNET_HEADER_SIZE); // イーサネットヘッダ長分のバッファを確保 72 | auto *header = reinterpret_cast(header_mybuf->buffer); 73 | 74 | // イーサネットヘッダの設定 75 | memcpy(header->src_addr, dev->mac_addr, 76 | 6); // 送信元アドレスにはデバイスのアドレスを設定 77 | memcpy(header->dest_addr, dest_addr, 6); // `宛先アドレスの設定 78 | header->type = htons(ether_type); // イーサタイプの設定 79 | 80 | payload_mybuf->add_header( 81 | header_mybuf); // 上位プロトコルから受け取ったバッファにヘッダをつける 82 | 83 | #ifdef DEBUG_ETHERNET 84 | #if DEBUG_ETHERNET > 1 85 | printf("[ETHER] Sending buffer: "); 86 | for (int i = 0; i < header_mybuf->len; ++i) { 87 | printf("%02x", header_mybuf->buffer[i]); 88 | } 89 | printf("\n"); 90 | #endif 91 | #endif 92 | 93 | uint8_t send_buffer[1550]; 94 | // 全長を計算しながらメモリにバッファを展開する 95 | size_t total_len = 0; 96 | my_buf *current = header_mybuf; 97 | while (current != nullptr) { 98 | if (total_len + current->len > 99 | sizeof(send_buffer)) { // Overflowする場合 100 | LOG_ETHERNET("Frame is too long!\n"); 101 | return; 102 | } 103 | 104 | #ifdef ENABLE_MYBUF_NON_COPY_MODE 105 | if (current->buf_ptr != nullptr) { 106 | memcpy(&send_buffer[total_len], current->buf_ptr, current->len); 107 | } else { 108 | #endif 109 | memcpy(&send_buffer[total_len], current->buffer, current->len); 110 | #ifdef ENABLE_MYBUF_NON_COPY_MODE 111 | } 112 | #endif 113 | total_len += current->len; 114 | current = current->next; 115 | } 116 | 117 | // ネットワークデバイスに送信する 118 | dev->ops.transmit(dev, send_buffer, total_len); 119 | 120 | my_buf::my_buf_free(header_mybuf, true); // メモリ開放 121 | } 122 | -------------------------------------------------------------------------------- /ethernet.h: -------------------------------------------------------------------------------- 1 | #ifndef CURO_ETHERNET_H 2 | #define CURO_ETHERNET_H 3 | 4 | #include "net.h" 5 | #include 6 | 7 | #define ETHER_TYPE_IP 0x0800 8 | #define ETHER_TYPE_ARP 0x0806 9 | #define ETHER_TYPE_IPV6 0x86dd 10 | 11 | #define ETHERNET_HEADER_SIZE 14 12 | #define ETHERNET_ADDRESS_LEN 6 13 | 14 | const uint8_t ETHERNET_ADDRESS_BROADCAST[] = {0xff, 0xff, 0xff, 15 | 0xff, 0xff, 0xff}; 16 | 17 | const uint8_t ETHERNET_ADDRESS_IPV6_MCAST_PREFIX[] = {0x33, 0x33}; 18 | 19 | 20 | struct ethernet_header { 21 | uint8_t dest_addr[6]; // 宛先アドレス 22 | uint8_t src_addr[6]; // 送信元アドレス 23 | uint16_t type; // イーサタイプ 24 | } __attribute__((packed)); 25 | 26 | void ethernet_input(net_device *dev, uint8_t *buffer, ssize_t len); 27 | 28 | struct my_buf; 29 | 30 | void ethernet_encapsulate_output(net_device *dev, const uint8_t *dest_addr, 31 | my_buf *payload_mybuf, uint16_t ether_type); 32 | 33 | #endif // CURO_ETHERNET_H 34 | -------------------------------------------------------------------------------- /icmp.cpp: -------------------------------------------------------------------------------- 1 | #include "icmp.h" 2 | 3 | #include "config.h" 4 | #include "ip.h" 5 | #include "log.h" 6 | #include "my_buf.h" 7 | #include "net.h" 8 | #include "utils.h" 9 | #include 10 | 11 | /** 12 | * ICMPパケットの受信処理 13 | * @param source 14 | * @param destination 15 | * @param buffer 16 | * @param len 17 | */ 18 | void icmp_input(uint32_t source, uint32_t destination, void *buffer, 19 | size_t len) { 20 | // ICMPメッセージ長より短かったら 21 | if (len < sizeof(icmp_header)) { 22 | LOG_ICMP("Received ICMP packet too short\n"); 23 | return; 24 | } 25 | // ICMPのパケットとして解釈する 26 | auto *icmp_msg = reinterpret_cast(buffer); 27 | 28 | switch (icmp_msg->header.type) { 29 | case ICMP_TYPE_ECHO_REPLY: 30 | // ICMP Echo の最低長より短かったら 31 | if (len < sizeof(icmp_header) + sizeof(icmp_echo)) { 32 | LOG_ICMP("Received ICMP echo packet too short\n"); 33 | return; 34 | } 35 | LOG_ICMP("Received icmp echo reply id %04x seq %d\n", 36 | ntohs(icmp_msg->echo.identify), 37 | ntohs(icmp_msg->echo.sequence)); 38 | break; 39 | case ICMP_TYPE_ECHO_REQUEST: { 40 | // ICMP Echo の最低長より短かったら 41 | if (len < sizeof(icmp_header) + sizeof(icmp_echo)) { 42 | LOG_ICMP("Received ICMP echo packet too short\n"); 43 | return; 44 | } 45 | LOG_ICMP("Received icmp echo request id %04x seq %d\n", 46 | ntohs(icmp_msg->echo.identify), 47 | ntohs(icmp_msg->echo.sequence)); 48 | 49 | my_buf *reply_mybuf = my_buf::create(len); 50 | auto *reply_msg = 51 | reinterpret_cast(reply_mybuf->buffer); 52 | reply_msg->header.type = ICMP_TYPE_ECHO_REPLY; 53 | reply_msg->header.code = 0; 54 | reply_msg->header.checksum = 0; 55 | reply_msg->echo.identify = 56 | icmp_msg->echo.identify; // 識別番号をコピー 57 | reply_msg->echo.sequence = 58 | icmp_msg->echo.sequence; // シーケンス番号をコピー 59 | memcpy(&reply_msg->echo.data, &icmp_msg->echo.data, 60 | len - (sizeof(icmp_header) + 61 | sizeof(icmp_echo))); // データをコピー 62 | reply_msg->header.checksum = 63 | checksum_16(reinterpret_cast(reply_mybuf->buffer), 64 | reply_mybuf->len); // checksumの計算 65 | 66 | ip_encapsulate_output(source, destination, reply_mybuf, 67 | IP_PROTOCOL_NUM_ICMP); 68 | } break; 69 | default: 70 | LOG_ICMP("Received unhandled icmp type %d\n", 71 | icmp_msg->header.type); 72 | break; 73 | } 74 | } 75 | 76 | /** 77 | * ICMP Time exceededメッセージを送信する 78 | * @param dest_addr 79 | * @param src_addr 80 | * @param code 81 | * @param error_ip_buffer エラーになったパケット 82 | * @param len エラーになったパケットの長さ 83 | */ 84 | void send_icmp_time_exceeded(uint32_t dest_addr, uint32_t src_addr, 85 | uint8_t code, void *error_ip_buffer, size_t len) { 86 | if (len < sizeof(ip_header) + 8) { // エラーパケットが小さすぎる場合 87 | return; 88 | } 89 | 90 | // ICMPヘッダ+メッセージの領域+エラーパケット部(IPヘッダ+バイト)を確保 91 | my_buf *time_exceeded_mybuf = 92 | my_buf::create(sizeof(icmp_header) + sizeof(icmp_time_exceeded) + 93 | sizeof(ip_header) + 8); 94 | auto *time_exceeded_msg = 95 | reinterpret_cast(time_exceeded_mybuf->buffer); 96 | 97 | // 各フィールドをセット 98 | time_exceeded_msg->header.type = ICMP_TYPE_TIME_EXCEEDED; 99 | time_exceeded_msg->header.code = code; 100 | time_exceeded_msg->header.checksum = 0; 101 | time_exceeded_msg->time_exceeded.unused = 0; 102 | memcpy(time_exceeded_msg->time_exceeded.data, error_ip_buffer, 103 | sizeof(ip_header) + 8); 104 | time_exceeded_msg->header.checksum = 105 | checksum_16(reinterpret_cast(time_exceeded_mybuf->buffer), 106 | time_exceeded_mybuf->len); 107 | 108 | // IPで送信 109 | ip_encapsulate_output(dest_addr, src_addr, time_exceeded_mybuf, 110 | IP_PROTOCOL_NUM_ICMP); 111 | } 112 | 113 | /** 114 | * ICMP Destination unreachableメッセージを送信する 115 | * @param dest_addr 116 | * @param src_addr 117 | * @param code Destination unreachableのコード 118 | * @param error_ip_buffer エラーになったパケット 119 | * @param len エラーになったパケットの長さ 120 | */ 121 | void send_icmp_destination_unreachable(uint32_t dest_addr, uint32_t src_addr, 122 | uint8_t code, void *error_ip_buffer, 123 | size_t len) { 124 | if (len < sizeof(ip_header) + 8) { // エラーパケットが小さすぎる場合 125 | return; 126 | } 127 | 128 | // ICMPヘッダ+メッセージの領域+エラーパケット部(IPヘッダ+バイト)を確保 129 | my_buf *unreachable_mybuf = 130 | my_buf::create(sizeof(icmp_header) + sizeof(icmp_dest_unreachable) + 131 | sizeof(ip_header) + 8); 132 | auto *unreachable_msg = 133 | reinterpret_cast(unreachable_mybuf->buffer); 134 | 135 | // 各フィールドをセット 136 | unreachable_msg->header.type = ICMP_TYPE_DESTINATION_UNREACHABLE; 137 | unreachable_msg->header.code = code; 138 | unreachable_msg->header.checksum = 0; 139 | unreachable_msg->dest_unreachable.unused = 0; 140 | memcpy(unreachable_msg->dest_unreachable.data, error_ip_buffer, 141 | sizeof(ip_header) + 8); 142 | unreachable_msg->header.checksum = 143 | checksum_16(reinterpret_cast(unreachable_mybuf->buffer), 144 | unreachable_mybuf->len); 145 | 146 | // IPで送信 147 | ip_encapsulate_output(dest_addr, src_addr, unreachable_mybuf, 148 | IP_PROTOCOL_NUM_ICMP); 149 | } 150 | -------------------------------------------------------------------------------- /icmp.h: -------------------------------------------------------------------------------- 1 | #ifndef CURO_ICMP_H 2 | #define CURO_ICMP_H 3 | 4 | #include 5 | 6 | #define ICMP_TYPE_ECHO_REPLY 0 7 | #define ICMP_TYPE_DESTINATION_UNREACHABLE 3 8 | #define ICMP_TYPE_ECHO_REQUEST 8 9 | #define ICMP_TYPE_TIME_EXCEEDED 11 10 | 11 | struct icmp_header { 12 | uint8_t type; 13 | uint8_t code; 14 | uint16_t checksum; 15 | } __attribute__((packed)); 16 | 17 | struct icmp_echo { 18 | uint16_t identify; 19 | uint16_t sequence; 20 | uint8_t data[]; 21 | } __attribute__((packed)); 22 | 23 | struct icmp_dest_unreachable { 24 | uint32_t unused; 25 | uint8_t data[]; 26 | } __attribute__((packed)); 27 | 28 | struct icmp_time_exceeded { 29 | uint32_t unused; 30 | uint8_t data[]; 31 | } __attribute__((packed)); 32 | 33 | struct icmp_message { 34 | icmp_header header; 35 | union { 36 | icmp_echo echo; 37 | icmp_dest_unreachable dest_unreachable; 38 | icmp_time_exceeded time_exceeded; 39 | }; 40 | } __attribute__((packed)); 41 | 42 | #define ICMP_DEST_UNREACHABLE_CODE_NET_UNREACHABLE 0 43 | #define ICMP_DEST_UNREACHABLE_CODE_HOST_UNREACHABLE 1 44 | #define ICMP_DEST_UNREACHABLE_CODE_PROTOCOL_UNREACHABLE 2 45 | #define ICMP_DEST_UNREACHABLE_CODE_PORT_UNREACHABLE 3 46 | #define ICMP_DEST_UNREACHABLE_CODE_FRAGMENT_NEEDED_AND_DF_SET 4 47 | #define ICMP_DEST_UNREACHABLE_CODE_SOURCE_ROUTE_FAILED 5 48 | 49 | #define ICMP_TIME_EXCEEDED_CODE_TIME_TO_LIVE_EXCEEDED 0 50 | #define ICMP_TIME_EXCEEDED_CODE_FRAGMENT_REASSEMBLY_TIME_EXCEEDED 1 51 | 52 | void icmp_input(uint32_t source, uint32_t destination, void *buffer, 53 | size_t len); 54 | 55 | void send_icmp_time_exceeded(uint32_t dest_addr, uint32_t src_addr, 56 | uint8_t code, void *error_ip_buffer, size_t len); 57 | void send_icmp_destination_unreachable(uint32_t dest_addr, uint32_t src_addr, 58 | uint8_t code, void *error_ip_buffer, 59 | size_t len); 60 | 61 | #endif // CURO_ICMP_H 62 | -------------------------------------------------------------------------------- /icmpv6.cpp: -------------------------------------------------------------------------------- 1 | #include "icmpv6.h" 2 | 3 | #include "config.h" 4 | #include "ipv6.h" 5 | #include "log.h" 6 | #include "my_buf.h" 7 | #include "nd.h" 8 | #include "net.h" 9 | #include "utils.h" 10 | #include 11 | 12 | /** 13 | * ICMPv6パケットの受信処理 14 | * @param source 15 | * @param destination 16 | * @param buffer 17 | * @param len 18 | */ 19 | void icmpv6_input(ipv6_device* v6dev, ipv6_addr source, ipv6_addr destination, void *buffer, size_t len) { 20 | icmpv6_hdr *icmp_pkt = reinterpret_cast(buffer); 21 | LOG_IPV6("icmpv6 code=%d, type=%d\n", icmp_pkt->code, icmp_pkt->type); 22 | 23 | switch(icmp_pkt->type){ 24 | case ICMPV6_TYPE_NEIGHBOR_SOLICIATION:{ 25 | if (len < sizeof(icmpv6_na)) { 26 | LOG_IPV6("Received neighbor solicitation packet too short\n"); 27 | return; 28 | } 29 | 30 | icmpv6_na *nspkt = reinterpret_cast(buffer); 31 | 32 | LOG_IPV6("Ns target %s\n", ipv6toascii(nspkt->target_addr)); 33 | 34 | if(memcmp(&nspkt->target_addr, &v6dev->address, 16) == 0){ 35 | LOG_IPV6("Ns target math! %s\n", ipv6toascii(nspkt->target_addr)); 36 | 37 | add_nd_table_entry(v6dev->net_dev, nspkt->opt_mac_addr, source); 38 | 39 | my_buf *icmpv6_mybuf = my_buf::create(sizeof(icmpv6_na)); 40 | icmpv6_na *napkt = (icmpv6_na *) icmpv6_mybuf->buffer; 41 | 42 | napkt->hdr.code = 0; 43 | napkt->hdr.type = ICMPV6_TYPE_NEIGHBOR_ADVERTISEMENT; 44 | napkt->hdr.checksum = 0; 45 | napkt->flags = ICMPV6_NA_FLAG_SOLICITED | ICMPV6_NA_FLAG_OVERRIDE; 46 | napkt->target_addr = nspkt->target_addr; 47 | 48 | napkt->opt_type = 2; 49 | napkt->opt_length = 1; 50 | memcpy(&napkt->opt_mac_addr, v6dev->net_dev->mac_addr, 6); 51 | 52 | ipv6_pseudo_header phdr; 53 | phdr.src_addr = v6dev->address; 54 | phdr.dest_addr = source; 55 | phdr.packet_length = htonl(sizeof(icmpv6_na)); 56 | phdr.zero1 = 0; 57 | phdr.zero2 = 0; 58 | phdr.next_header = IPV6_PROTOCOL_NUM_ICMP; 59 | 60 | uint16_t psum = ~checksum_16((uint16_t*) &phdr, sizeof(ipv6_pseudo_header), 0); 61 | 62 | napkt->hdr.checksum = checksum_16((uint16_t*) napkt, sizeof(icmpv6_na), psum); 63 | 64 | ipv6_encap_dev_output(v6dev->net_dev, &nspkt->opt_mac_addr[0], source, icmpv6_mybuf, IPV6_PROTOCOL_NUM_ICMP); 65 | 66 | } 67 | } 68 | break; 69 | case ICMPV6_TYPE_ECHO_REQUEST:{ 70 | if (len < sizeof(icmpv6_echo)) { 71 | LOG_IPV6("Received echo request packet too short\n"); 72 | return; 73 | } 74 | 75 | icmpv6_echo *echo_packet = reinterpret_cast(buffer); 76 | 77 | LOG_IPV6("Received echo request id=%d seq=%d\n", ntohs(echo_packet->id), ntohs(echo_packet->seq)); 78 | 79 | uint32_t data_len = len - sizeof(icmpv6_echo); 80 | 81 | if(data_len >= 200){ // TODO modify 82 | LOG_IPV6("Echo size is too large\n"); 83 | return; 84 | } 85 | 86 | my_buf *reply_buf = my_buf::create(sizeof(icmpv6_echo) + data_len); 87 | icmpv6_echo *reply_pkt = reinterpret_cast(reply_buf->buffer); 88 | reply_pkt->hdr.type = ICMPV6_TYPE_ECHO_REPLY; 89 | reply_pkt->hdr.code = 0; 90 | reply_pkt->hdr.checksum = 0; 91 | 92 | reply_pkt->id = echo_packet->id; 93 | reply_pkt->seq = echo_packet->seq; 94 | 95 | memcpy(&reply_pkt->data[0], &echo_packet->data[0], data_len); 96 | 97 | ipv6_pseudo_header phdr; 98 | phdr.src_addr = v6dev->address; 99 | phdr.dest_addr = source; 100 | phdr.packet_length = htonl(sizeof(icmpv6_echo) + data_len); 101 | phdr.zero1 = 0; 102 | phdr.zero2 = 0; 103 | phdr.next_header = IPV6_PROTOCOL_NUM_ICMP; 104 | 105 | uint16_t psum = ~checksum_16((uint16_t*) &phdr, sizeof(ipv6_pseudo_header), 0); 106 | 107 | reply_pkt->hdr.checksum = checksum_16((uint16_t*) reply_pkt, sizeof(icmpv6_echo) + data_len, psum); 108 | 109 | ipv6_encap_output(source, v6dev->address, reply_buf, IPV6_PROTOCOL_NUM_ICMP); 110 | 111 | } 112 | break; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /icmpv6.h: -------------------------------------------------------------------------------- 1 | #ifndef CURO_ICMPV6_H 2 | #define CURO_ICMPV6_H 3 | 4 | #include 5 | #include "ipv6.h" 6 | 7 | #define ICMPV6_TYPE_ECHO_REQUEST 128 8 | #define ICMPV6_TYPE_ECHO_REPLY 129 9 | 10 | #define ICMPV6_TYPE_ROUTER_SOLICIATION 133 11 | #define ICMPV6_TYPE_NEIGHBOR_SOLICIATION 135 12 | #define ICMPV6_TYPE_NEIGHBOR_ADVERTISEMENT 136 13 | 14 | #define ICMPV6_NA_FLAG_ROUTER 0b10000000 15 | #define ICMPV6_NA_FLAG_SOLICITED 0b01000000 16 | #define ICMPV6_NA_FLAG_OVERRIDE 0b00100000 17 | 18 | struct icmpv6_hdr{ 19 | uint8_t type; 20 | uint8_t code; 21 | uint16_t checksum; 22 | } __attribute__((packed)); 23 | 24 | struct icmpv6_echo{ 25 | icmpv6_hdr hdr; 26 | uint16_t id; 27 | uint16_t seq; 28 | uint8_t data[]; 29 | } __attribute__((packed)); 30 | 31 | struct icmpv6_na{ 32 | icmpv6_hdr hdr; 33 | uint8_t flags; 34 | uint8_t reserved1; 35 | uint16_t reserved2; 36 | ipv6_addr target_addr; 37 | // options area 38 | uint8_t opt_type; 39 | uint8_t opt_length; 40 | uint8_t opt_mac_addr[6]; 41 | } __attribute__((packed)); 42 | 43 | void icmpv6_input(ipv6_device* v6dev, ipv6_addr source, ipv6_addr destination, void *buffer, size_t len); 44 | 45 | #endif // CURO_ICMPV6_H 46 | -------------------------------------------------------------------------------- /ip.cpp: -------------------------------------------------------------------------------- 1 | #include "ip.h" 2 | 3 | #include "arp.h" 4 | #include "binary_trie.h" 5 | #include "config.h" 6 | #include "ethernet.h" 7 | #include "icmp.h" 8 | #include "log.h" 9 | #include "my_buf.h" 10 | #include "nat.h" 11 | #include "utils.h" 12 | 13 | /** 14 | * IPルーティングテーブルのルートノード 15 | */ 16 | binary_trie_node *ip_fib; 17 | 18 | /** 19 | * IPルーティングテーブルの出力 20 | */ 21 | void dump_ip_fib() { 22 | binary_trie_node *current_node; 23 | std::queue *> node_queue; 24 | node_queue.push(ip_fib); 25 | 26 | while (!node_queue.empty()) { 27 | current_node = node_queue.front(); 28 | node_queue.pop(); 29 | 30 | if (current_node->data != nullptr) { 31 | if (current_node->data->type == ip_route_type::connected) { 32 | printf("%s/%d connected %s\n", 33 | ip_htoa(locate_prefix(current_node, ip_fib)), 34 | current_node->depth, current_node->data->dev->name); 35 | } else { 36 | printf("%s/%d nexthop %s\n", 37 | ip_htoa(locate_prefix(current_node, ip_fib)), 38 | current_node->depth, 39 | ip_htoa(current_node->data->next_hop)); 40 | } 41 | } 42 | 43 | if (current_node->node_0 != nullptr) { 44 | node_queue.push(current_node->node_0); 45 | } 46 | if (current_node->node_1 != nullptr) { 47 | node_queue.push(current_node->node_1); 48 | } 49 | } 50 | } 51 | 52 | /** 53 | * サブネットにIPアドレスが含まれているか比較 54 | * @param subnet_prefix 55 | * @param subnet_mask 56 | * @param target_address 57 | * @return 58 | */ 59 | bool in_subnet(uint32_t subnet_prefix, uint32_t subnet_mask, 60 | uint32_t target_address) { 61 | return ((target_address & subnet_mask) == (subnet_prefix & subnet_mask)); 62 | } 63 | 64 | /** 65 | * 自分宛のIPパケットの処理 66 | * @param input_dev 67 | * @param ip_packet 68 | * @param len 69 | */ 70 | void ip_input_to_ours(net_device *input_dev, ip_header *ip_packet, size_t len) { 71 | 72 | #ifdef ENABLE_NAT 73 | // NATの外側から内側への通信か判断 74 | for(net_device *dev = net_dev_list; dev; dev = dev->next){ 75 | if(dev->ip_dev != nullptr and dev->ip_dev->nat_dev != nullptr and 76 | dev->ip_dev->nat_dev->outside_addr == ntohl(ip_packet->dest_addr)){ 77 | bool nat_executed = false; 78 | switch(ip_packet->protocol){ 79 | case IP_PROTOCOL_NUM_UDP: 80 | if(nat_exec(ip_packet, len, 81 | dev->ip_dev->nat_dev, 82 | nat_protocol::udp, 83 | nat_direction::incoming)){ 84 | nat_executed = true; 85 | } 86 | break; 87 | case IP_PROTOCOL_NUM_TCP: 88 | if(nat_exec(ip_packet, len, 89 | dev->ip_dev->nat_dev, 90 | nat_protocol::tcp, 91 | nat_direction::incoming)){ 92 | nat_executed = true; 93 | } 94 | break; 95 | case IP_PROTOCOL_NUM_ICMP: 96 | if(nat_exec(ip_packet, len, 97 | dev->ip_dev->nat_dev, 98 | nat_protocol::icmp, 99 | nat_direction::incoming)){ 100 | nat_executed = true; 101 | } 102 | break; 103 | } 104 | if(nat_executed){ 105 | #ifdef ENABLE_MYBUF_NON_COPY_MODE 106 | my_buf *nat_fwd_mybuf = my_buf::create(0); 107 | nat_fwd_mybuf->buf_ptr = (uint8_t *)ip_packet; 108 | nat_fwd_mybuf->len = len; 109 | #else 110 | my_buf *nat_fwd_mybuf = my_buf::create(len); 111 | memcpy(nat_fwd_mybuf->buffer, ip_packet, len); 112 | nat_fwd_mybuf->len = len; 113 | #endif 114 | ip_output(ntohl(ip_packet->dest_addr), 115 | ntohl(ip_packet->src_addr), nat_fwd_mybuf); 116 | return; 117 | } 118 | } 119 | } 120 | #endif 121 | 122 | // 上位プロトコルの処理に移行 123 | switch (ip_packet->protocol) { 124 | case IP_PROTOCOL_NUM_ICMP: 125 | /* 126 | // for book chapter 3 127 | LOG_IP("ICMP received!\n"); 128 | return; 129 | */ 130 | return icmp_input( 131 | ntohl(ip_packet->src_addr), ntohl(ip_packet->dest_addr), 132 | ((uint8_t *)ip_packet) + IP_HEADER_SIZE, len - IP_HEADER_SIZE); 133 | 134 | case IP_PROTOCOL_NUM_UDP: 135 | #ifdef ENABLE_ICMP_ERROR 136 | send_icmp_destination_unreachable( 137 | ntohl(ip_packet->src_addr), input_dev->ip_dev->address, 138 | ICMP_DEST_UNREACHABLE_CODE_PORT_UNREACHABLE, ip_packet, len); 139 | #endif 140 | return; 141 | case IP_PROTOCOL_NUM_TCP: 142 | // まだこのルータにはTCPを扱う機能はない 143 | return; 144 | 145 | default: 146 | 147 | LOG_IP("Unhandled ip protocol %04x", ip_packet->protocol); 148 | return; 149 | } 150 | } 151 | 152 | /** 153 | * IPパケットの受信処理 154 | * @param input_dev 155 | * @param buffer 156 | * @param len 157 | */ 158 | void ip_input(net_device *input_dev, uint8_t *buffer, ssize_t len) { 159 | // IPアドレスのついていないインターフェースからの受信は無視 160 | if (input_dev->ip_dev == nullptr or input_dev->ip_dev->address == 0) { 161 | return; 162 | } 163 | 164 | // IPヘッダ長より短かったらドロップ 165 | if (len < sizeof(ip_header)) { 166 | LOG_IP("Received IP packet too short from %s\n", input_dev->name); 167 | return; 168 | } 169 | 170 | // 送られてきたバッファをキャストして扱う 171 | auto *ip_packet = reinterpret_cast(buffer); 172 | 173 | LOG_IP("Received IP packet type %d from %s to %s\n", ip_packet->protocol, 174 | ip_ntoa(ip_packet->src_addr), ip_ntoa(ip_packet->dest_addr)); 175 | 176 | if (ip_packet->version != 4) { 177 | LOG_IP("Incorrect IP version\n"); 178 | return; 179 | } 180 | 181 | // IPヘッダオプションがついていたらドロップ 182 | if (ip_packet->header_len != (sizeof(ip_header) >> 2)) { 183 | LOG_IP("IP header option is not supported\n"); 184 | return; 185 | } 186 | 187 | if (ip_packet->dest_addr == 188 | IP_ADDRESS_LIMITED_BROADCAST) { // 宛先アドレスがブロードキャストアドレスの場合 189 | return ip_input_to_ours(input_dev, ip_packet, 190 | len); // 自分宛の通信として処理 191 | } 192 | 193 | // 宛先IPアドレスをルータが持ってるか調べる 194 | for (net_device *dev = net_dev_list; dev; dev = dev->next) { 195 | if (dev->ip_dev != nullptr and 196 | dev->ip_dev->address != IP_ADDRESS(0, 0, 0, 0)) { 197 | // 宛先IPアドレスがルータの持っているIPアドレス or 198 | // ディレクティッド・ブロードキャストアドレスの時の処理 199 | if (dev->ip_dev->address == ntohl(ip_packet->dest_addr) or 200 | dev->ip_dev->broadcast == ntohl(ip_packet->dest_addr)) { 201 | return ip_input_to_ours(dev, ip_packet, 202 | len); // 自分宛の通信として処理 203 | } 204 | } 205 | } 206 | 207 | #ifdef ENABLE_NAT 208 | // NATの内側から外側への通信 209 | if (input_dev->ip_dev->nat_dev != nullptr) { 210 | if (ip_packet->protocol == IP_PROTOCOL_NUM_UDP) { // NATの対象 211 | if (!nat_exec(ip_packet, len, input_dev->ip_dev->nat_dev, 212 | nat_protocol::udp, nat_direction::outgoing)) { 213 | return; // NATできないパケットはドロップ 214 | } 215 | } else if (ip_packet->protocol == IP_PROTOCOL_NUM_TCP) { 216 | if (!nat_exec(ip_packet, len, input_dev->ip_dev->nat_dev, 217 | nat_protocol::tcp, nat_direction::outgoing)) { 218 | return; // NATできないパケットはドロップ 219 | } 220 | } else if (ip_packet->protocol == IP_PROTOCOL_NUM_ICMP) { 221 | if (!nat_exec(ip_packet, len, input_dev->ip_dev->nat_dev, 222 | nat_protocol::icmp, nat_direction::outgoing)) { 223 | return; // NATできないパケットはドロップ 224 | } 225 | } else { 226 | LOG_IP("NAT unimplemented packet dropped type=%d\n", 227 | ip_packet->protocol); 228 | return; // NATできないパケットはドロップ 229 | } 230 | } 231 | #endif 232 | 233 | // 宛先IPアドレスがルータの持っているIPアドレスでない場合はフォワーディングを行う 234 | ip_route_entry *route = binary_trie_search( 235 | ip_fib, 236 | ntohl(ip_packet->dest_addr)); // ルーティングテーブルをルックアップ 237 | if (route == nullptr) { // 宛先までの経路がなかったらパケットを破棄 238 | LOG_IP("No route to %s\n", ip_htoa(ntohl(ip_packet->dest_addr))); 239 | // Drop packet 240 | return; 241 | } 242 | 243 | if (ip_packet->ttl <= 1) { // TTLが1以下ならドロップ 244 | #ifdef ENABLE_ICMP_ERROR 245 | send_icmp_time_exceeded( 246 | ntohl(ip_packet->src_addr), input_dev->ip_dev->address, 247 | ICMP_TIME_EXCEEDED_CODE_TIME_TO_LIVE_EXCEEDED, buffer, len); 248 | #endif 249 | return; 250 | } 251 | 252 | // TTLを1へらす 253 | ip_packet->ttl--; 254 | 255 | // IPヘッダチェックサムの再計算 256 | ip_packet->header_checksum = 0; 257 | ip_packet->header_checksum = 258 | checksum_16(reinterpret_cast(buffer), sizeof(ip_header)); 259 | 260 | #ifdef ENABLE_MYBUF_NON_COPY_MODE 261 | my_buf *ip_fwd_mybuf = my_buf::create(0); 262 | ip_fwd_mybuf->buf_ptr = buffer; 263 | ip_fwd_mybuf->len = len; 264 | #else 265 | // my_buf構造にコピー 266 | my_buf *ip_fwd_mybuf = my_buf::create(len); 267 | memcpy(ip_fwd_mybuf->buffer, buffer, len); 268 | ip_fwd_mybuf->len = len; 269 | #endif 270 | 271 | if (route->type == connected) { // 直接接続ネットワークの経路なら 272 | ip_output_to_host(route->dev, ntohl(ip_packet->dest_addr), 273 | ntohl(ip_packet->src_addr), 274 | ip_fwd_mybuf); // hostに直接送信 275 | return; 276 | } else if (route->type == 277 | network) { // 直接接続ネットワークの経路ではなかったら 278 | ip_output_to_next_hop(route->next_hop, ip_fwd_mybuf); // next hopに送信 279 | return; 280 | } 281 | } 282 | 283 | /** 284 | * IPパケットを直接イーサネットでホストに送信 285 | * @param dev 286 | * @param dest_addr 287 | * @param src_addr 288 | * @param payload_mybuf 289 | */ 290 | void ip_output_to_host(net_device *dev, uint32_t dest_addr, uint32_t src_addr, 291 | my_buf *payload_mybuf) { 292 | arp_table_entry *entry = 293 | search_arp_table_entry(dest_addr); // ARPテーブルの検索 294 | 295 | if (!entry) { // ARPエントリが無かったら 296 | LOG_IP("Trying ip output to host, but no arp record to %s\n", 297 | ip_htoa(dest_addr)); 298 | send_arp_request(dev, dest_addr); // ARPリクエストの送信 299 | my_buf::my_buf_free(payload_mybuf, true); // Drop packet 300 | return; 301 | } else { 302 | ethernet_encapsulate_output( 303 | entry->dev, entry->mac_addr, payload_mybuf, 304 | ETHER_TYPE_IP); // イーサネットでカプセル化して送信 305 | } 306 | } 307 | 308 | /** 309 | * IPパケットをNextHopに送信 310 | * @param next_hop 311 | * @param payload_mybuf 312 | */ 313 | void ip_output_to_next_hop(uint32_t next_hop, my_buf *payload_mybuf) { 314 | arp_table_entry *entry = 315 | search_arp_table_entry(next_hop); // ARPテーブルの検索 316 | 317 | if (!entry) { // ARPエントリが無かったら 318 | LOG_IP("Trying ip output to next hop, but no arp record to %s\n", 319 | ip_htoa(next_hop)); 320 | 321 | ip_route_entry *route_to_next_hop = binary_trie_search( 322 | ip_fib, next_hop); // ルーティングテーブルのルックアップ 323 | 324 | if (route_to_next_hop == nullptr or 325 | route_to_next_hop->type != 326 | connected) { // next hopへの到達性が無かったら 327 | LOG_IP("Next hop %s is not reachable\n", ip_htoa(next_hop)); 328 | } else { 329 | send_arp_request(route_to_next_hop->dev, 330 | next_hop); // ARPリクエストを送信 331 | } 332 | my_buf::my_buf_free(payload_mybuf, true); // Drop packet 333 | return; 334 | 335 | } else { // ARPエントリがあり、MACアドレスが得られたら 336 | ethernet_encapsulate_output( 337 | entry->dev, entry->mac_addr, payload_mybuf, 338 | ETHER_TYPE_IP); // イーサネットでカプセル化して送信 339 | } 340 | } 341 | 342 | /** 343 | * IPパケットを送信 344 | * @param dest_addr 宛先IPアドレス 345 | * @param src_addr 送信元IPアドレス 346 | * @param payload_mybuf 送信するパケット 347 | */ 348 | void ip_output(uint32_t dest_addr, uint32_t src_addr, my_buf *payload_mybuf) { 349 | // 宛先IPアドレスへの経路を検索 350 | ip_route_entry *route = binary_trie_search(ip_fib, dest_addr); 351 | if (route == nullptr) { // 経路が見つからなかったら 352 | LOG_IP("No route to %s\n", ip_htoa(dest_addr)); 353 | my_buf::my_buf_free(payload_mybuf, true); // Drop packet 354 | return; 355 | } 356 | 357 | if (route->type == connected) { // 直接接続ネットワークだったら 358 | ip_output_to_host(route->dev, dest_addr, src_addr, payload_mybuf); 359 | return; 360 | } else if (route->type == 361 | network) { // 直接つながっていないネットワークだったら 362 | ip_output_to_next_hop(route->next_hop, payload_mybuf); 363 | return; 364 | } 365 | } 366 | 367 | /** 368 | * IPパケットにカプセル化して送信 369 | * @param dest_addr 送信先のIPアドレス 370 | * @param src_addr 送信元のIPアドレス 371 | * @param payload_mybuf 包んで送信するmy_buf構造体の先頭 372 | * @param protocol_num IPプロトコル番号 373 | */ 374 | void ip_encapsulate_output(uint32_t dest_addr, uint32_t src_addr, 375 | my_buf *payload_mybuf, uint8_t protocol_num) { 376 | 377 | // 連結リストをたどってIPヘッダで必要なIPパケットの全長を算出する 378 | uint16_t total_len = 0; 379 | my_buf *current = payload_mybuf; 380 | while (current != nullptr) { 381 | total_len += current->len; 382 | current = current->next; 383 | } 384 | 385 | // IPヘッダ用のバッファを確保する 386 | my_buf *ip_mybuf = my_buf::create(IP_HEADER_SIZE); 387 | payload_mybuf->add_header( 388 | ip_mybuf); // 包んで送るデータにヘッダとして連結する 389 | 390 | // IPヘッダの各項目を設定 391 | auto *ip_buf = reinterpret_cast(ip_mybuf->buffer); 392 | ip_buf->version = 4; 393 | ip_buf->header_len = sizeof(ip_header) >> 2; 394 | ip_buf->tos = 0; 395 | ip_buf->total_len = htons(sizeof(ip_header) + total_len); 396 | ip_buf->protocol = protocol_num; // 8bit 397 | 398 | static uint16_t id = 0; 399 | ip_buf->identify = id++; 400 | ip_buf->frag_offset = 0; 401 | ip_buf->ttl = 0xff; 402 | ip_buf->header_checksum = 0; 403 | ip_buf->dest_addr = htonl(dest_addr); 404 | ip_buf->src_addr = htonl(src_addr); 405 | ip_buf->header_checksum = checksum_16( 406 | reinterpret_cast(ip_mybuf->buffer), ip_mybuf->len); 407 | 408 | ip_output(dest_addr, src_addr, ip_mybuf); 409 | 410 | /* 411 | // for book chapter3 (IP 412 | ルーティング/フォワーディングが実装されてないとき用) for(net_device* dev = 413 | net_dev_list; dev; dev = dev->next){ if(dev->ip_dev == nullptr or 414 | dev->ip_dev->address == IP_ADDRESS(0, 0, 0, 0)) continue; 415 | if(in_subnet(dev->ip_dev->address, dev->ip_dev->netmask, dest_addr)){ 416 | // TODO: イーサネットアドレスを特定して送信 417 | arp_table_entry* entry; 418 | entry = search_arp_table_entry(dest_addr); 419 | if(entry == nullptr){ 420 | LOG_IP("Trying ip output, but no arp record to %s\n", 421 | ip_htoa(dest_addr)); send_arp_request(dev, dest_addr); 422 | my_buf::my_buf_free(payload_mybuf, true); 423 | return; 424 | } 425 | ethernet_encapsulate_output(dev, entry->mac_addr, ip_mybuf, 426 | ETHER_TYPE_IP); 427 | } 428 | } 429 | */ 430 | } 431 | -------------------------------------------------------------------------------- /ip.h: -------------------------------------------------------------------------------- 1 | #ifndef CURO_IP_H 2 | #define CURO_IP_H 3 | 4 | #include "config.h" 5 | #include 6 | #include 7 | 8 | #define IP_ADDRESS_LEN 4 9 | #define IP_ADDRESS(A, B, C, D) (A * 0x1000000u + B * 0x10000 + C * 0x100 + D) 10 | #define IP_ADDRESS_LIMITED_BROADCAST IP_ADDRESS(255, 255, 255, 255) 11 | 12 | #define IP_HEADER_SIZE 20 13 | 14 | #define IP_PROTOCOL_NUM_ICMP 0x01 15 | #define IP_PROTOCOL_NUM_TCP 0x06 16 | #define IP_PROTOCOL_NUM_UDP 0x11 17 | 18 | #define IP_FRAG_OFFSET_MASK_RESERVED_FLAG 0b1000000000000000 19 | #define IP_FRAG_OFFSET_MASK_DF_FLAG 0b0100000000000000 20 | #define IP_FRAG_OFFSET_MASK_MF_FLAG 0b0010000000000000 21 | #define IP_FRAG_OFFSET_MASK_OFFSET 0b0001111111111111 22 | 23 | struct ip_header { 24 | uint8_t header_len : 4; 25 | uint8_t version : 4; 26 | uint8_t tos; 27 | uint16_t total_len; 28 | uint16_t identify; 29 | uint16_t frag_offset; 30 | uint8_t ttl; 31 | uint8_t protocol; 32 | uint16_t header_checksum; 33 | uint32_t src_addr; 34 | uint32_t dest_addr; 35 | } __attribute__((packed)); 36 | 37 | struct nat_device; 38 | 39 | struct ip_device { 40 | uint32_t address = 0; // デバイスのIPアドレス 41 | uint32_t netmask = 0; // サブネットマスク 42 | uint32_t broadcast = 0; // ブロードキャストアドレス 43 | #ifdef ENABLE_NAT 44 | nat_device *nat_dev = nullptr; 45 | #endif 46 | }; 47 | 48 | enum ip_route_type { 49 | connected, // 直接接続されているネットワークの経路  50 | network 51 | }; 52 | 53 | struct net_device; 54 | 55 | struct ip_route_entry { 56 | ip_route_type type; 57 | union { 58 | net_device *dev; 59 | uint32_t next_hop; 60 | }; 61 | }; 62 | 63 | template struct binary_trie_node; 64 | 65 | extern binary_trie_node *ip_fib; 66 | 67 | void dump_ip_fib(); 68 | 69 | bool in_subnet(uint32_t subnet_prefix, uint32_t subnet_mask, 70 | uint32_t target_address); 71 | 72 | void ip_input(net_device *input_dev, uint8_t *buffer, ssize_t len); 73 | 74 | struct my_buf; 75 | 76 | void ip_output_to_host(net_device *dev, uint32_t dest_address, 77 | uint32_t src_address, my_buf *payload_mybuf); 78 | void ip_output_to_next_hop(uint32_t next_hop, my_buf *payload_mybuf); 79 | void ip_output(uint32_t dest_addr, uint32_t src_addr, my_buf *payload_mybuf); 80 | void ip_encapsulate_output(uint32_t dest_addr, uint32_t src_addr, 81 | my_buf *payload_mybuf, uint8_t protocol_num); 82 | 83 | #endif // CURO_IP_H 84 | -------------------------------------------------------------------------------- /ipv6.cpp: -------------------------------------------------------------------------------- 1 | #include "ipv6.h" 2 | 3 | #include "binary_trie.h" 4 | #include "config.h" 5 | #include "ethernet.h" 6 | #include "icmpv6.h" 7 | #include "log.h" 8 | #include "my_buf.h" 9 | #include "nat.h" 10 | #include "nd.h" 11 | #include "utils.h" 12 | 13 | /** 14 | * IPv6パケットの受信処理 15 | * @param input_dev 16 | * @param buffer 17 | * @param len 18 | */ 19 | 20 | char ipv6ascii[4*16+15+1]; 21 | char* ipv6toascii(ipv6_addr addr){ 22 | 23 | sprintf(ipv6ascii, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", ntohs(addr.per_16.int1), ntohs(addr.per_16.int2), ntohs(addr.per_16.int3), 24 | ntohs(addr.per_16.int4), ntohs(addr.per_16.int5), ntohs(addr.per_16.int6), ntohs(addr.per_16.int7), 25 | ntohs(addr.per_16.int8)); 26 | 27 | return ipv6ascii; 28 | } 29 | 30 | /** 31 | * 自分宛のIPパケットの処理 32 | * @param input_dev 33 | * @param ip_packet 34 | * @param len 35 | */ 36 | void ipv6_input_to_ours(net_device *input_dev, ipv6_header *packet, size_t len) { 37 | 38 | switch (packet->next_hdr) { 39 | case IPV6_PROTOCOL_NUM_ICMP: 40 | return icmpv6_input(input_dev->ipv6_dev, packet->src_addr, packet->dest_addr, ((uint8_t *)packet) + sizeof(ipv6_header), len - sizeof(ipv6_header)); 41 | default: 42 | break; 43 | } 44 | } 45 | 46 | void ipv6_input(net_device *input_dev, uint8_t *buffer, ssize_t len) { 47 | 48 | if(input_dev->ipv6_dev == nullptr){ 49 | LOG_IPV6("Received IPv6 packet from non ipv6 device %s\n", input_dev->name); 50 | return; 51 | } 52 | 53 | if (len < sizeof(ipv6_addr)) { 54 | LOG_IPV6("Received IPv6 packet too short from %s\n", input_dev->name); 55 | return; 56 | } 57 | 58 | // 送られてきたバッファをキャストして扱う 59 | ipv6_header *packet = reinterpret_cast(buffer); 60 | 61 | if (packet->ver_tc_fl & 0b1111 != 6) { 62 | return; 63 | } 64 | 65 | LOG_IPV6("Next header: 0x%02x\n", packet->next_hdr); 66 | 67 | LOG_IPV6("Src: %s\n", ipv6toascii(packet->src_addr)); 68 | 69 | LOG_IPV6("Dst: %s\n", ipv6toascii(packet->dest_addr)); 70 | 71 | // 宛先IPアドレスをルータが持ってるか調べる 72 | for (net_device *dev = net_dev_list; dev; dev = dev->next) { 73 | if (dev->ipv6_dev != nullptr) { 74 | // 宛先IPアドレスがルータの持っているIPアドレスの処理 75 | if (memcmp(&dev->ipv6_dev->address, &packet->dest_addr, 16) == 0) { 76 | return ipv6_input_to_ours(dev, packet, len); // 自分宛の通信として処理 77 | } 78 | } 79 | } 80 | 81 | 82 | // ルーティングのための処理 83 | } 84 | 85 | void ipv6_encap_dev_output(net_device* output_dev, const uint8_t* dest_mac_addr, ipv6_addr dest_addr, my_buf* buffer, uint8_t next_hdr_num){ 86 | 87 | // 連結リストをたどってIPヘッダで必要なIPパケットの全長を算出する 88 | uint16_t payload_len = 0; 89 | my_buf *current = buffer; 90 | while (current != nullptr) { 91 | payload_len += current->len; 92 | current = current->next; 93 | } 94 | 95 | // IPv6ヘッダ用のバッファを確保する 96 | my_buf *v6h_mybuf = my_buf::create(sizeof(ipv6_header)); 97 | buffer->add_header(v6h_mybuf); // 包んで送るデータにヘッダとして連結する 98 | 99 | // IPヘッダの各項目を設定 100 | ipv6_header *v6h_buf = reinterpret_cast(v6h_mybuf->buffer); 101 | v6h_buf->ver_tc_fl = 0x60; 102 | v6h_buf->payload_len = htons(payload_len); 103 | v6h_buf->next_hdr = next_hdr_num; 104 | v6h_buf->hop_limit = 0xff; 105 | v6h_buf->src_addr = output_dev->ipv6_dev->address; 106 | v6h_buf->dest_addr = dest_addr; 107 | 108 | ethernet_encapsulate_output(output_dev, dest_mac_addr, v6h_mybuf, ETHER_TYPE_IPV6); 109 | 110 | } 111 | 112 | void ipv6_output_to_nexthop(ipv6_addr dest_addr, ipv6_addr src_addr, my_buf* buffer){ 113 | nd_table_entry *entry = search_nd_table_entry(dest_addr); 114 | 115 | if(entry != nullptr){ 116 | LOG_IP("Found entry!\n"); 117 | 118 | ethernet_encapsulate_output(entry->dev, entry->mac_addr, buffer, ETHER_TYPE_IPV6); 119 | } 120 | } 121 | 122 | void ipv6_encap_output(ipv6_addr dest_addr, ipv6_addr src_addr, my_buf* buffer, uint8_t next_hdr_num){ 123 | 124 | // 連結リストをたどってIPヘッダで必要なIPパケットの全長を算出する 125 | uint16_t payload_len = 0; 126 | my_buf *current = buffer; 127 | while (current != nullptr) { 128 | payload_len += current->len; 129 | current = current->next; 130 | } 131 | 132 | // IPv6ヘッダ用のバッファを確保する 133 | my_buf *v6h_mybuf = my_buf::create(sizeof(ipv6_header)); 134 | buffer->add_header(v6h_mybuf); // 包んで送るデータにヘッダとして連結する 135 | 136 | // IPヘッダの各項目を設定 137 | ipv6_header *v6h_buf = reinterpret_cast(v6h_mybuf->buffer); 138 | v6h_buf->ver_tc_fl = 0x60; 139 | v6h_buf->payload_len = htons(payload_len); 140 | v6h_buf->next_hdr = next_hdr_num; 141 | v6h_buf->hop_limit = 0xff; 142 | v6h_buf->src_addr = src_addr; 143 | v6h_buf->dest_addr = dest_addr; 144 | 145 | ipv6_output_to_nexthop(dest_addr, src_addr, v6h_mybuf); 146 | 147 | } -------------------------------------------------------------------------------- /ipv6.h: -------------------------------------------------------------------------------- 1 | #ifndef CURO_IPV6_H 2 | #define CURO_IPV6_H 3 | 4 | #include "config.h" 5 | #include 6 | #include 7 | 8 | #define IPV6_PROTOCOL_NUM_ICMP 0x3a 9 | #define IPV6_PROTOCOL_NUM_NONXT 0x3b 10 | #define IPV6_PROTOCOL_NUM_OPTS 0x3c 11 | 12 | struct ipv6_addr { 13 | union{ 14 | struct{ 15 | uint64_t int1; 16 | uint64_t int2; 17 | } __attribute__((packed)) per_64; 18 | 19 | struct{ 20 | uint32_t int1; 21 | uint32_t int2; 22 | uint32_t int3; 23 | uint32_t int4; 24 | } __attribute__((packed)) per_32; 25 | 26 | struct{ 27 | uint16_t int1; 28 | uint16_t int2; 29 | uint16_t int3; 30 | uint16_t int4; 31 | uint16_t int5; 32 | uint16_t int6; 33 | uint16_t int7; 34 | uint16_t int8; 35 | } __attribute__((packed)) per_16; 36 | 37 | unsigned char chars[16]; 38 | }; 39 | 40 | } __attribute__((packed)); 41 | 42 | #define IPV6_ADDRESS(A, B, C, D, E, F, G, H) () 43 | 44 | struct ipv6_device{ 45 | ipv6_addr address; // IPv6アドレス 46 | uint32_t prefix_len; // プレフィックス長(0~128) 47 | uint8_t scope; // スコープ 48 | net_device* net_dev; // ネットワークデバイスへのポインタ 49 | }; 50 | 51 | struct ipv6_header { 52 | uint32_t ver_tc_fl; 53 | uint16_t payload_len; 54 | uint8_t next_hdr; 55 | uint8_t hop_limit; 56 | ipv6_addr src_addr; 57 | ipv6_addr dest_addr; 58 | } __attribute__((packed)); 59 | 60 | struct ipv6_pseudo_header{ 61 | ipv6_addr src_addr; 62 | ipv6_addr dest_addr; 63 | uint32_t packet_length; 64 | uint16_t zero1; 65 | uint8_t zero2; 66 | uint8_t next_header; 67 | 68 | }; 69 | 70 | char *ipv6toascii(ipv6_addr addr); 71 | 72 | void ipv6_input(net_device * input_dev, uint8_t * buffer, ssize_t len); 73 | 74 | struct my_buf; 75 | 76 | void ipv6_encap_dev_output(net_device* output_dev, const uint8_t* dest_mac_addr, ipv6_addr dest_addr, my_buf* buffer, uint8_t next_hdr_num); 77 | 78 | void ipv6_encap_output(ipv6_addr dest_addr, ipv6_addr src_addr, my_buf* buffer, uint8_t next_hdr_num); 79 | 80 | #endif // CURO_IPV6_H 81 | -------------------------------------------------------------------------------- /log.h: -------------------------------------------------------------------------------- 1 | #ifndef CURO_LOG_H 2 | #define CURO_LOG_H 3 | 4 | #include "config.h" 5 | 6 | #if DEBUG_ETHERNET > 0 7 | #define LOG_ETHERNET(...) printf("[ETHER] ");printf(__VA_ARGS__) 8 | #else 9 | #define LOG_ETHERNET(...) 10 | #endif 11 | 12 | #if DEBUG_IP > 0 13 | #define LOG_IP(...) printf("[IP] ");printf(__VA_ARGS__); 14 | #else 15 | #define LOG_IP(...) 16 | #endif 17 | 18 | #if DEBUG_ARP > 0 19 | #define LOG_ARP(...) printf("[ARP] ");printf(__VA_ARGS__); 20 | #else 21 | #define LOG_ARP(...) 22 | #endif 23 | 24 | #if DEBUG_ICMP > 0 25 | #define LOG_ICMP(...) printf("[ICMP] ");printf(__VA_ARGS__); 26 | #else 27 | #define LOG_ICMP(...) 28 | #endif 29 | 30 | #if DEBUG_NAT > 0 31 | #define LOG_NAT(...) printf("[NAT] ");printf(__VA_ARGS__); 32 | #else 33 | #define LOG_NAT(...) 34 | #endif 35 | 36 | #if DEBUG_IPV6 > 0 37 | #define LOG_IPV6(...) printf("[IPv6] ");printf(__VA_ARGS__); 38 | #else 39 | #define LOG_IPV6(...) 40 | #endif 41 | 42 | 43 | #define LOG_ERROR(...) fprintf(stderr, "[ERROR %s:%d] ", __FILE__, __LINE__);fprintf(stderr, __VA_ARGS__); 44 | 45 | #endif //CURO_LOG_H 46 | -------------------------------------------------------------------------------- /my_buf.h: -------------------------------------------------------------------------------- 1 | #ifndef CURO_MY_BUF_H 2 | #define CURO_MY_BUF_H 3 | 4 | #include "config.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | struct my_buf { 11 | my_buf *previous = nullptr; // 前のmy_buf 12 | my_buf *next = nullptr; // 後ろのmy_buf 13 | uint32_t len = 0; // my_bufに含むバッファの長さ 14 | #ifdef ENABLE_MYBUF_NON_COPY_MODE 15 | uint8_t *buf_ptr = nullptr; 16 | #endif 17 | uint8_t buffer[]; // バッファ 18 | 19 | /** 20 | * my_bufのメモリ確保 21 | * @param len 確保するバッファ長 22 | */ 23 | static my_buf *create(uint32_t len) { 24 | auto *buf = (my_buf *)calloc(1, sizeof(my_buf) + len); 25 | buf->len = len; 26 | return buf; 27 | } 28 | 29 | /** 30 | * my_bufのメモリ開放 31 | * @param buf 32 | * @param is_recursive 33 | */ 34 | static void my_buf_free(my_buf *buf, bool is_recursive = false) { 35 | if (!is_recursive) { 36 | free(buf); 37 | return; 38 | } 39 | 40 | my_buf *tail = buf->get_tail(), *tmp; 41 | while (tail != nullptr) { 42 | tmp = tail; 43 | tail = tmp->previous; 44 | free(tmp); 45 | } 46 | } 47 | 48 | /** 49 | * 連結リストの最後の項目を返す 50 | */ 51 | my_buf *get_tail() { 52 | my_buf *current = this; 53 | while (current->next != nullptr) { 54 | current = current->next; 55 | } 56 | return current; 57 | } 58 | 59 | void add_header(my_buf *buf) { 60 | this->previous = buf; 61 | buf->next = this; 62 | } 63 | }; 64 | 65 | #endif // CURO_MY_BUF_H 66 | -------------------------------------------------------------------------------- /nat.cpp: -------------------------------------------------------------------------------- 1 | #include "nat.h" 2 | 3 | #include "config.h" 4 | #include "ip.h" 5 | #include "log.h" 6 | #include "my_buf.h" 7 | #include "net.h" 8 | 9 | /** 10 | * NATテーブルを出力する 11 | */ 12 | void dump_nat_tables() { 13 | #ifdef ENABLE_NAT 14 | printf("|-PROTO-|---------LOCAL---------|--------GLOBAL---------|\n"); 15 | for (net_device *dev = net_dev_list; dev; dev = dev->next) { 16 | if (dev->ip_dev != nullptr and dev->ip_dev->nat_dev != nullptr) { 17 | for (int i = 0; i < NAT_GLOBAL_PORT_SIZE; ++i) { 18 | if (dev->ip_dev->nat_dev->entries->tcp[i].global_port != 0) { 19 | printf( 20 | "| TCP | %15s:%05d | %15s:%05d |\n", 21 | ip_htoa( 22 | dev->ip_dev->nat_dev->entries->tcp[i].local_addr), 23 | dev->ip_dev->nat_dev->entries->tcp[i].local_port, 24 | ip_htoa( 25 | dev->ip_dev->nat_dev->entries->tcp[i].global_addr), 26 | dev->ip_dev->nat_dev->entries->tcp[i].global_port); 27 | } 28 | if (dev->ip_dev->nat_dev->entries->udp[i].global_port != 0) { 29 | printf( 30 | "| UDP | %15s:%05d | %15s:%05d |\n", 31 | ip_htoa( 32 | dev->ip_dev->nat_dev->entries->udp[i].local_addr), 33 | dev->ip_dev->nat_dev->entries->udp[i].local_port, 34 | ip_htoa( 35 | dev->ip_dev->nat_dev->entries->udp[i].global_addr), 36 | dev->ip_dev->nat_dev->entries->udp[i].global_port); 37 | } 38 | } 39 | for (int i = 0; i < NAT_ICMP_ID_SIZE; ++i) { 40 | if (dev->ip_dev->nat_dev->entries->icmp[i].local_addr != 0) { 41 | printf( 42 | "| ICMP | %15s:%05d | %15s:%05d |\n", 43 | ip_htoa( 44 | dev->ip_dev->nat_dev->entries->icmp[i].local_addr), 45 | dev->ip_dev->nat_dev->entries->icmp[i].local_port, 46 | ip_htoa( 47 | dev->ip_dev->nat_dev->entries->icmp[i].global_addr), 48 | dev->ip_dev->nat_dev->entries->icmp[i].global_port); 49 | } 50 | } 51 | } 52 | } 53 | printf("|-------|-----------------------|-----------------------|\n"); 54 | #else 55 | printf("NAT has not been enabled for this build\n"); 56 | #endif 57 | } 58 | 59 | /** 60 | * NATのアドレス変換を実行する 61 | * @param ip_packet アドレス変換を行うパケット 62 | * @param len アドレス変換を行うパケットの残りの長さ 63 | * @param nat_dev NATデバイス 64 | * @param proto IPプロトコルタイプ(UDP,TCP,ICMPのみ対応) 65 | * @param direction NATの方向 66 | * @return NATが成功したかどうか 67 | */ 68 | bool nat_exec(ip_header *ip_packet, size_t len, nat_device *nat_dev, 69 | nat_protocol proto, nat_direction direction) { 70 | nat_packet_head *nat_packet; 71 | nat_packet = (nat_packet_head *)((uint8_t *)ip_packet + sizeof(ip_header)); 72 | // ICMPだったら、クエリーパケットのみNATする 73 | if (proto == nat_protocol::icmp and 74 | nat_packet->icmp.header.type != ICMP_TYPE_ECHO_REQUEST and 75 | nat_packet->icmp.header.type != ICMP_TYPE_ECHO_REPLY) { 76 | if (nat_packet->icmp.header.type == ICMP_TYPE_TIME_EXCEEDED or 77 | nat_packet->icmp.header.type == ICMP_TYPE_DESTINATION_UNREACHABLE) { 78 | proto = nat_protocol::icmp_error; 79 | } else { 80 | return false; 81 | } 82 | } 83 | 84 | nat_entry *entry; 85 | if (direction == nat_direction::incoming) { // NATの外から内の通信の時 86 | if (proto == nat_protocol::icmp) { // ICMPの場合はIDを用いる 87 | entry = get_nat_entry_by_global(nat_dev->entries, proto, 88 | ntohl(ip_packet->dest_addr), 89 | ntohs(nat_packet->icmp.identify)); 90 | } else if (proto == nat_protocol::icmp_error) { // ICMPエラーの場合、エラーパケットの中身を用いる 91 | 92 | if (len < sizeof(ip_header) + sizeof(icmp_dest_unreachable) + sizeof(ip_header) + sizeof(uint16_t) * 2) { 93 | return false; 94 | } 95 | 96 | if (nat_packet->icmp_error.error_iph.protocol == IP_PROTOCOL_NUM_UDP) { 97 | entry = get_nat_entry_by_global( 98 | nat_dev->entries, nat_protocol::udp, 99 | ntohl(nat_packet->icmp_error.error_iph.src_addr), 100 | ntohs(nat_packet->icmp_error.src_port)); 101 | } else if (nat_packet->icmp_error.error_iph.protocol == IP_PROTOCOL_NUM_TCP) { 102 | entry = get_nat_entry_by_global( 103 | nat_dev->entries, nat_protocol::tcp, 104 | ntohl(nat_packet->icmp_error.error_iph.src_addr), 105 | ntohs(nat_packet->icmp_error.src_port)); 106 | } else { // UDP/TCP以外の場合、扱えないのでfalseを返す 107 | LOG_NAT("Unsupported nat error ip packet protocol\n"); 108 | return false; 109 | } 110 | } else { // UDP/TCPの時はポート番号 111 | entry = get_nat_entry_by_global(nat_dev->entries, proto, 112 | ntohl(ip_packet->dest_addr), 113 | ntohs(nat_packet->dest_port)); 114 | } 115 | if (entry == nullptr) { // NATエントリが登録されていない場合、falseを返す 116 | return false; 117 | } 118 | } else { // NATの内から外の通信の時 119 | if (proto == nat_protocol::icmp) { // ICMP 120 | entry = get_nat_entry_by_local(nat_dev->entries, proto, 121 | ntohl(ip_packet->src_addr), 122 | ntohs(nat_packet->icmp.identify)); 123 | } else if (proto == nat_protocol::icmp_error) { 124 | LOG_NAT("Outgoing icmp error is not supported\n"); 125 | return false; 126 | } else { // TCP/UDP 127 | entry = get_nat_entry_by_local(nat_dev->entries, proto, 128 | ntohl(ip_packet->src_addr), 129 | ntohs(nat_packet->src_port)); 130 | } 131 | if (entry == nullptr) { 132 | 133 | if (proto == nat_protocol::icmp) { // ICMP 134 | entry = create_nat_entry( 135 | nat_dev->entries, proto, 136 | ntohs(nat_packet->icmp.identify)); // NATテーブルエントリの作成 137 | } else if (proto == nat_protocol::icmp_error) { 138 | return false; // ICMPエラーの場合、そのエラーの元となったパケットのエントリがないと通過できないので、エントリの新規作成は行わない 139 | } else { // TCP/UDP 140 | entry = create_nat_entry( 141 | nat_dev->entries, proto, 142 | ntohs(nat_packet->src_port)); // NATテーブルエントリの作成 143 | } 144 | 145 | if (entry == nullptr) { 146 | LOG_NAT("NAT table is full!\n"); 147 | return false; 148 | } 149 | LOG_NAT("Created new nat table entry global port %d\n", 150 | entry->global_port); 151 | entry->global_addr = nat_dev->outside_addr; 152 | entry->local_addr = ntohl(ip_packet->src_addr); 153 | if (proto == nat_protocol::icmp) { 154 | entry->local_port = ntohs(nat_packet->icmp.identify); 155 | } else { 156 | entry->local_port = ntohs(nat_packet->src_port); 157 | } 158 | } 159 | } 160 | 161 | uint32_t checksum; 162 | if (proto == nat_protocol::icmp) { 163 | checksum = nat_packet->icmp.header.checksum; 164 | checksum = ~checksum; 165 | checksum -= nat_packet->icmp.identify; 166 | if (direction == nat_direction::incoming) { 167 | checksum += htons(entry->local_port); 168 | } else { 169 | checksum += htons(entry->global_port); 170 | } 171 | }else if(proto == nat_protocol::icmp_error){ 172 | 173 | checksum = nat_packet->icmp.header.checksum; 174 | checksum = ~checksum; 175 | 176 | // only incoming 177 | checksum -= nat_packet->icmp_error.error_iph.src_addr & 0xffff; 178 | checksum -= nat_packet->icmp_error.error_iph.src_addr >> 16; 179 | checksum += htonl(entry->local_addr) & 0xffff; 180 | checksum += htonl(entry->local_addr) >> 16; 181 | 182 | checksum -= htons(entry->global_port); 183 | checksum += htons(entry->local_port); 184 | 185 | } else { // UDP/TCP 186 | if (proto == nat_protocol::udp) { // UDP 187 | checksum = nat_packet->udp.checksum; 188 | } else { // TCP 189 | checksum = nat_packet->tcp.checksum; 190 | } 191 | checksum = ~checksum; 192 | // checksumの差分の計算 193 | if (direction == nat_direction::incoming) { 194 | checksum -= ip_packet->dest_addr & 0xffff; 195 | checksum -= ip_packet->dest_addr >> 16; 196 | checksum -= nat_packet->dest_port; 197 | checksum += htonl(entry->local_addr) & 0xffff; 198 | checksum += htonl(entry->local_addr) >> 16; 199 | checksum += htons(entry->local_port); 200 | } else { 201 | checksum -= ip_packet->src_addr & 0xffff; 202 | checksum -= ip_packet->src_addr >> 16; 203 | checksum -= nat_packet->src_port; 204 | checksum += htonl(nat_dev->outside_addr) & 0xffff; 205 | checksum += htonl(nat_dev->outside_addr) >> 16; 206 | checksum += htons(entry->global_port); 207 | } 208 | } 209 | checksum = ~checksum; 210 | 211 | if (checksum > 0xffff) { 212 | checksum = (checksum & 0xffff) + (checksum >> 16); 213 | } 214 | 215 | // checksumの書き換え 216 | if (proto == nat_protocol::icmp) { // ICMP 217 | nat_packet->icmp.header.checksum = checksum; 218 | } else if (proto == nat_protocol::icmp_error) { // ICMP Error 219 | nat_packet->icmp.header.checksum = checksum; 220 | } else if (proto == nat_protocol::udp) { // UDP 221 | nat_packet->udp.checksum = checksum; 222 | } else { // TCP 223 | nat_packet->tcp.checksum = checksum; 224 | } 225 | 226 | // アドレスなどの書き換え 227 | if (direction == nat_direction::incoming) { 228 | ip_packet->dest_addr = htonl(entry->local_addr); 229 | if (proto == nat_protocol::icmp) { // ICMP 230 | nat_packet->icmp.identify = htons(entry->local_port); 231 | } else if (proto == nat_protocol::icmp_error) { // ICMPエラー 232 | nat_packet->icmp_error.error_iph.src_addr = htonl(entry->local_addr); 233 | nat_packet->icmp_error.src_port = htons(entry->local_port); 234 | // TODO エラーパケットのIPヘッダchecksumも更新した方良いかも 235 | } else { // UDP/TCP 236 | nat_packet->dest_port = htons(entry->local_port); 237 | } 238 | } else { 239 | ip_packet->src_addr = htonl(nat_dev->outside_addr); 240 | if (proto == nat_protocol::icmp) { // ICMP 241 | nat_packet->icmp.identify = htons(entry->global_port); 242 | } else if (proto == nat_protocol::icmp_error) { // ICMPエラー 243 | // Outgoinには非対応 244 | } else { // UDP/TCP 245 | nat_packet->src_port = htons(entry->global_port); 246 | } 247 | } 248 | 249 | // IPヘッダのヘッダチェックサムの再計算 250 | ip_packet->header_checksum = 0; 251 | ip_packet->header_checksum = checksum_16(reinterpret_cast(ip_packet), sizeof(ip_header)); 252 | 253 | return true; 254 | } 255 | 256 | /** 257 | * グローバルアドレスとグローバルポートからNATエントリを取得 258 | */ 259 | nat_entry *get_nat_entry_by_global(nat_entries *entries, nat_protocol proto, 260 | uint32_t addr, uint16_t port) { 261 | if (proto == nat_protocol::udp) { // UDPの場合 262 | if (entries->udp[port - NAT_GLOBAL_PORT_MIN].global_addr == addr and 263 | entries->udp[port - NAT_GLOBAL_PORT_MIN].global_port == port) { 264 | return &entries->udp[port - NAT_GLOBAL_PORT_MIN]; 265 | } 266 | } else if (proto == nat_protocol::tcp) { 267 | if (entries->tcp[port - NAT_GLOBAL_PORT_MIN].global_addr == addr and 268 | entries->tcp[port - NAT_GLOBAL_PORT_MIN].global_port == port) { 269 | return &entries->tcp[port - NAT_GLOBAL_PORT_MIN]; 270 | } 271 | } else if (proto == nat_protocol::icmp) { 272 | // NATテーブルエントリがグローバルIPアドレス、ICMPのIDが一致しているか調べる 273 | if (entries->icmp[port].global_addr == addr and 274 | entries->icmp[port].global_port == port) { 275 | return &entries->icmp[port]; 276 | } 277 | } 278 | return nullptr; 279 | } 280 | 281 | /** 282 | * ローカルアドレスとローカルポートからNATエントリを取得 283 | */ 284 | nat_entry *get_nat_entry_by_local(nat_entries *entries, nat_protocol proto, 285 | uint32_t addr, uint16_t port) { 286 | if (proto == nat_protocol::udp) { // UDPの場合 287 | // UDPのNATテーブルをローカルIPアドレス, ローカルポートで検索する 288 | for (int i = 0; i < NAT_GLOBAL_PORT_SIZE; ++i) { 289 | if (entries->udp[i].local_addr == addr and 290 | entries->udp[i].local_port == port) { 291 | return &entries->udp[i]; 292 | } 293 | } 294 | } else if (proto == nat_protocol::tcp) { // TCPの場合 295 | // TCPのNATテーブルをローカルIPアドレス, ローカルポートで検索する 296 | for (int i = 0; i < NAT_GLOBAL_PORT_SIZE; ++i) { 297 | if (entries->tcp[i].local_addr == addr and 298 | entries->tcp[i].local_port == port) { 299 | return &entries->tcp[i]; 300 | } 301 | } 302 | } else if (proto == nat_protocol::icmp) { // ICMPの場合 303 | // ICMPのNATテーブルをローカルIPアドレス、ICMPのIDで検索する 304 | for (int i = 0; i < NAT_ICMP_ID_SIZE; ++i) { 305 | if (entries->icmp[i].local_addr == addr and 306 | entries->icmp[i].local_port == port) { 307 | return &entries->icmp[i]; 308 | } 309 | } 310 | } 311 | return nullptr; // テーブルに一致するエントリがなかったらnullptrを返す 312 | } 313 | 314 | /** 315 | * 空いてるポートを探し、NATエントリを作成する 316 | */ 317 | nat_entry *create_nat_entry(nat_entries *entries, nat_protocol proto, 318 | uint16_t desired) { 319 | 320 | 321 | do { 322 | if (proto == nat_protocol::udp) { // UDPの場合 323 | 324 | if (desired < NAT_GLOBAL_PORT_MIN or 325 | NAT_GLOBAL_PORT_MAX < desired) { 326 | break; 327 | } 328 | 329 | if (entries->udp[desired - NAT_GLOBAL_PORT_MIN].global_port == 0) { 330 | entries->udp[desired - NAT_GLOBAL_PORT_MIN].global_port = 331 | desired; 332 | return &entries->udp[desired - NAT_GLOBAL_PORT_MIN]; 333 | } 334 | } else if (proto == nat_protocol::tcp) { 335 | if (desired < NAT_GLOBAL_PORT_MIN or 336 | NAT_GLOBAL_PORT_MAX < desired) { 337 | break; 338 | } 339 | 340 | if (entries->tcp[desired - NAT_GLOBAL_PORT_MIN].global_port == 0) { 341 | entries->tcp[desired - NAT_GLOBAL_PORT_MIN].global_port = 342 | desired; 343 | return &entries->tcp[desired - NAT_GLOBAL_PORT_MIN]; 344 | } 345 | 346 | } else if (proto == nat_protocol::icmp) { 347 | if (NAT_ICMP_ID_SIZE < desired) { 348 | break; 349 | } 350 | 351 | if (entries->icmp[desired].global_addr == 0) { 352 | entries->icmp[desired].global_port = desired; 353 | return &entries->icmp[desired]; 354 | } 355 | } 356 | 357 | } while (0); 358 | 359 | 360 | 361 | if (proto == nat_protocol::udp) { // UDPの場合 362 | for (int i = 0; i < NAT_GLOBAL_PORT_SIZE; 363 | ++i) { // NATテーブルのサイズ分 364 | if (entries->udp[i].global_addr == 0) { 365 | // 空いてるエントリが見つかったら、グローバルポートを設定してエントリを返す 366 | entries->udp[i].global_port = NAT_GLOBAL_PORT_MIN + i; 367 | return &entries->udp[i]; 368 | } 369 | } 370 | } else if (proto == nat_protocol::tcp) { 371 | for (int i = 0; i < NAT_GLOBAL_PORT_SIZE; ++i) { 372 | if (entries->tcp[i].global_addr == 0) { 373 | entries->tcp[i].global_port = NAT_GLOBAL_PORT_MIN + i; 374 | return &entries->tcp[i]; 375 | } 376 | } 377 | } else if (proto == nat_protocol::icmp) { 378 | for (int i = 0; i < NAT_ICMP_ID_SIZE; ++i) { 379 | if (entries->icmp[i].global_addr == 0) { 380 | entries->icmp[i].global_port = i; 381 | return &entries->icmp[i]; 382 | } 383 | } 384 | } 385 | return nullptr; // 空いているエントリがなかったら 386 | } 387 | -------------------------------------------------------------------------------- /nat.h: -------------------------------------------------------------------------------- 1 | #ifndef CURO_NAT_H 2 | #define CURO_NAT_H 3 | 4 | #include "icmp.h" 5 | #include "ip.h" 6 | #include "utils.h" 7 | #include 8 | 9 | #define NAT_GLOBAL_PORT_MIN 20000 10 | #define NAT_GLOBAL_PORT_MAX 59999 11 | 12 | #define NAT_GLOBAL_PORT_SIZE (NAT_GLOBAL_PORT_MAX - NAT_GLOBAL_PORT_MIN + 1) 13 | 14 | #define NAT_ICMP_ID_SIZE 0xffff 15 | 16 | // NATの方向 17 | enum class nat_direction { outgoing, incoming }; 18 | 19 | // NATに対応しているプロトコル 20 | enum class nat_protocol { udp, tcp, icmp, icmp_error }; 21 | 22 | struct nat_packet_head { 23 | union { 24 | struct { // tcp, udp 25 | uint16_t src_port; 26 | uint16_t dest_port; 27 | union { 28 | struct { 29 | uint16_t len; 30 | uint16_t checksum; 31 | } udp; 32 | struct { 33 | uint32_t seq; 34 | uint32_t ack_seq; 35 | uint8_t offset; 36 | uint8_t flag; 37 | uint16_t window; 38 | uint16_t checksum; 39 | uint16_t urg_ptr; 40 | } tcp; 41 | }; 42 | }; 43 | struct { // icmp 44 | icmp_header header; 45 | uint16_t identify; 46 | uint16_t sequence; 47 | } icmp; 48 | struct { // icmp error 49 | icmp_header header; 50 | uint32_t unused; 51 | ip_header error_iph; 52 | uint16_t src_port; 53 | uint16_t dest_port; 54 | } icmp_error; 55 | }; 56 | }; 57 | 58 | struct nat_entry { 59 | uint32_t global_addr; 60 | uint32_t local_addr; 61 | uint16_t global_port; 62 | uint16_t local_port; 63 | }; 64 | 65 | // ICMP, UDP, TCPのNATテーブルのセット 66 | struct nat_entries { 67 | nat_entry icmp[NAT_ICMP_ID_SIZE]; 68 | nat_entry udp[NAT_GLOBAL_PORT_SIZE]; 69 | nat_entry tcp[NAT_GLOBAL_PORT_SIZE]; 70 | }; 71 | 72 | // NATの内側のip_deviceが持つNATデバイス 73 | struct nat_device { 74 | uint32_t outside_addr; // 変換先のIPアドレス 75 | nat_entries *entries; // NATテーブル 76 | }; 77 | 78 | void dump_nat_tables(); 79 | 80 | bool nat_exec(ip_header *ip_packet, size_t len, nat_device *nat_dev, 81 | nat_protocol proto, nat_direction direction); 82 | 83 | nat_entry *get_nat_entry_by_global(nat_entries *entries, nat_protocol proto, 84 | uint32_t addr, uint16_t port); 85 | nat_entry *get_nat_entry_by_local(nat_entries *entries, nat_protocol proto, 86 | uint32_t addr, uint16_t port); 87 | nat_entry *create_nat_entry(nat_entries *entries, nat_protocol proto, 88 | uint16_t desired); 89 | 90 | #endif // CURO_NAT_H 91 | -------------------------------------------------------------------------------- /nd.cpp: -------------------------------------------------------------------------------- 1 | #include "nd.h" 2 | 3 | #include "net.h" 4 | #include "utils.h" 5 | 6 | // TODO タイマーを追加 7 | 8 | /** 9 | * NDテーブル 10 | * グローバル変数にテーブルを保持 11 | */ 12 | nd_table_entry nd_table[ND_TABLE_SIZE]; 13 | 14 | /** 15 | * NDテーブルにエントリの追加と更新 16 | * @param dev 17 | * @param mac_addr 18 | * @param ip_addr 19 | */ 20 | void add_nd_table_entry(net_device *dev, uint8_t *mac_addr, ipv6_addr v6_addr) { 21 | // 候補の場所は、HashテーブルのIPアドレスのハッシュがindexのもの 22 | const uint32_t index = (v6_addr.per_64.int1 +v6_addr.per_64.int2) % ND_TABLE_SIZE; 23 | nd_table_entry *candidate = &nd_table[index]; 24 | 25 | // テーブルに入れられるか確認 26 | if (candidate->v6addr.per_64.int1 + candidate->v6addr.per_64.int2 == 0 or 27 | candidate->v6addr.per_64.int1 == v6_addr.per_64.int1 and 28 | candidate->v6addr.per_64.int2 == v6_addr.per_64.int2) { // 初めの候補の場所に入れられるとき 29 | // エントリをセット 30 | memcpy(candidate->mac_addr, mac_addr, 6); 31 | candidate->v6addr = v6_addr; 32 | candidate->dev = dev; 33 | return; 34 | } 35 | 36 | // 入れられなかった場合は、その候補にあるエントリに連結する 37 | while (candidate->next != nullptr) { // 連結リストの末尾までたどる 38 | candidate = candidate->next; 39 | // 途中で同じIPアドレスのエントリがあったら、そのエントリを更新する 40 | if (candidate->v6addr.per_64.int1 == v6_addr.per_64.int1 and 41 | candidate->v6addr.per_64.int2 == v6_addr.per_64.int2) { 42 | memcpy(candidate->mac_addr, mac_addr, 6); 43 | candidate->v6addr = v6_addr; 44 | candidate->dev = dev; 45 | return; 46 | } 47 | } 48 | // 連結リストの末尾に新しくエントリを作成 49 | candidate->next = (nd_table_entry *)calloc(1, sizeof(nd_table_entry)); 50 | memcpy(candidate->next->mac_addr, mac_addr, 6); 51 | candidate->next->v6addr = v6_addr; 52 | candidate->next->dev = dev; 53 | } 54 | 55 | /** 56 | * NDテーブルの検索 57 | * @param ip_addr 58 | */ 59 | nd_table_entry *search_nd_table_entry(ipv6_addr v6_addr) { 60 | // 初めの候補の場所は、HashテーブルのIPアドレスのハッシュがindexのもの 61 | nd_table_entry *candidate = &nd_table[(v6_addr.per_64.int1 +v6_addr.per_64.int2) % ND_TABLE_SIZE]; 62 | 63 | if (candidate->v6addr.per_64.int1 == v6_addr.per_64.int1 and 64 | candidate->v6addr.per_64.int2 == v6_addr.per_64.int2) { // 候補のエントリが検索しているIPアドレスの物だったら 65 | return candidate; 66 | } else if (candidate->v6addr.per_64.int1 + candidate->v6addr.per_64.int2 == 67 | 0) { // 候補のエントリが登録されていなかったら 68 | return nullptr; 69 | } 70 | // 候補のエントリが検索しているIPアドレスの物でなかった場合、そのエントリの連結リストを調べる 71 | while (candidate->next != nullptr) { 72 | candidate = candidate->next; 73 | if (candidate->v6addr.per_64.int1 == v6_addr.per_64.int1 and 74 | candidate->v6addr.per_64.int2 == v6_addr.per_64.int2) { // 連結リストの中に検索しているIPアドレスの物があったら 75 | return candidate; 76 | } 77 | } 78 | 79 | // 連結リストの中に見つからなかったら 80 | return nullptr; 81 | } 82 | 83 | /** 84 | * NDテーブルの出力 85 | */ 86 | void dump_nd_table_entry() { 87 | printf("|--------------IPv6 ADDRESS---------------|----MAC " 88 | "ADDRESS----|----DEVICE-----|-INDEX-|\n"); 89 | for (int i = 0; i < ND_TABLE_SIZE; ++i) { 90 | if (nd_table[i].v6addr.per_64.int1+nd_table[i].v6addr.per_64.int2 == 0) { 91 | continue; 92 | } 93 | // エントリの連結リストを順に出力する 94 | for (nd_table_entry *entry = &nd_table[i]; entry; 95 | entry = entry->next) { 96 | printf("| %37s | %14s | %13s | %04d |\n", ipv6toascii(entry->v6addr), 97 | mac_addr_toa(entry->mac_addr), entry->dev->name, i); 98 | } 99 | } 100 | printf("|-----------------------------------------|-------------------|---------------|-------|" 101 | "\n"); 102 | } -------------------------------------------------------------------------------- /nd.h: -------------------------------------------------------------------------------- 1 | #ifndef CURO_ND_H 2 | #define CURO_ND_H 3 | 4 | #include 5 | #include "ipv6.h" 6 | 7 | #define ND_TABLE_SIZE 1111 8 | 9 | struct net_device; 10 | 11 | struct nd_table_entry { 12 | uint8_t mac_addr[6]; 13 | ipv6_addr v6addr; 14 | net_device *dev; 15 | nd_table_entry *next; 16 | }; 17 | 18 | void add_nd_table_entry(net_device *dev, uint8_t *mac_addr, ipv6_addr v6_addr); 19 | 20 | nd_table_entry *search_nd_table_entry(ipv6_addr v6_addr); 21 | 22 | void dump_nd_table_entry(); 23 | 24 | 25 | #endif -------------------------------------------------------------------------------- /net.cpp: -------------------------------------------------------------------------------- 1 | #include "net.h" 2 | 3 | /** 4 | * net_deviceの連結リストの先頭 5 | */ 6 | net_device *net_dev_list; 7 | -------------------------------------------------------------------------------- /net.h: -------------------------------------------------------------------------------- 1 | #ifndef CURO_NET_H 2 | #define CURO_NET_H 3 | 4 | #include 5 | #include 6 | #include "config.h" 7 | 8 | struct net_device; 9 | 10 | struct net_device_ops { 11 | int (*transmit)(net_device *dev, uint8_t *buffer, size_t len); 12 | int (*poll)(net_device *dev); 13 | }; 14 | 15 | struct ip_device; 16 | struct ipv6_device; 17 | 18 | struct net_device { 19 | char name[32]; // インターフェース名 20 | uint8_t mac_addr[6]; 21 | net_device_ops ops; 22 | ip_device *ip_dev; 23 | #ifdef ENABLE_IPV6 24 | ipv6_device *ipv6_dev; 25 | #endif 26 | net_device *next; 27 | uint8_t data[]; 28 | }; 29 | 30 | extern net_device *net_dev_list; // net_deviceの連結リストの先頭 31 | 32 | // #define FOR_EACH_NET_DEV(dev) for (dev = net_dev_list; dev; dev = dev->next) 33 | 34 | #endif // CURO_NET_H 35 | -------------------------------------------------------------------------------- /pf_packet/Makefile: -------------------------------------------------------------------------------- 1 | #!/bin/make 2 | OUTDIR = ./build 3 | TARGET = $(OUTDIR)/curo 4 | SOURCES = $(wildcard *.cpp) 5 | OBJECTS = $(addprefix $(OUTDIR)/, $(SOURCES:.cpp=.o)) 6 | LIBCURO_SHARED = ../build/libcuro.so 7 | 8 | CFLAGS = -I../ 9 | 10 | .PHONY: all 11 | all: $(TARGET) 12 | 13 | .PHONY: clean 14 | clean: 15 | $(RM) $(OBJECTS) $(TARGET) 16 | 17 | .PHONY: run 18 | run: $(TARGET) 19 | ./build/curo 20 | 21 | $(TARGET): $(OBJECTS) Makefile 22 | $(CXX) $(CFLAGS) -o $(TARGET) $(OBJECTS) $(LIBCURO_SHARED) 23 | 24 | $(OUTDIR)/%.o: %.cpp Makefile 25 | mkdir -p build 26 | $(CXX) $(CFLAGS) -o $@ -c $< 27 | -------------------------------------------------------------------------------- /pf_packet/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "arp.h" 13 | #include "binary_trie.h" 14 | #include "config.h" 15 | #include "ethernet.h" 16 | #include "ip.h" 17 | #include "ipv6.h" 18 | #include "log.h" 19 | #include "nat.h" 20 | #include "nd.h" 21 | #include "net.h" 22 | #include "utils.h" 23 | 24 | /** 25 | * 無視するネットワークインターフェースたち 26 | * 中にはMACアドレスを持たないものなど、 27 | * このプログラムで使うとエラーを引き起こすものもある 28 | */ 29 | #define IGNORE_INTERFACES \ 30 | { "lo", "bond0", "dummy0", "tunl0", "sit0" } 31 | 32 | /** 33 | * 無視するデバイスかどうかを返す 34 | * @param ifname 35 | * @return IGNORE_INTERFACESに含まれているかどうか 36 | */ 37 | bool is_ignore_interface(const char *ifname) { 38 | char ignore_interfaces[][IF_NAMESIZE] = IGNORE_INTERFACES; 39 | for (int i = 0; i < sizeof(ignore_interfaces) / IF_NAMESIZE; i++) { 40 | if (strcmp(ignore_interfaces[i], ifname) == 0) { 41 | return true; 42 | } 43 | } 44 | return false; 45 | } 46 | 47 | /** 48 | * インターフェース名からデバイスを探す 49 | * @param name デバイス名 50 | * @return 51 | */ 52 | net_device *get_net_device_by_name(const char *name) { 53 | net_device *dev; 54 | for (dev = net_dev_list; dev; dev = dev->next) { 55 | if (strcmp(dev->name, name) == 0) { 56 | return dev; 57 | } 58 | } 59 | return nullptr; 60 | } 61 | 62 | /** 63 | * 設定する 64 | */ 65 | void configure() { 66 | // for chapter 3 67 | /* 68 | configure_ip_address( 69 | get_net_device_by_name("router1-host1"), 70 | IP_ADDRESS(192, 168, 1, 1), 71 | IP_ADDRESS(255, 255, 255, 0)); 72 | configure_ip_address( 73 | get_net_device_by_name("router1-router2"), 74 | IP_ADDRESS(192, 168, 0, 1), 75 | IP_ADDRESS(255, 255, 255, 0)); 76 | configure_ip_net_route( 77 | IP_ADDRESS(192, 168, 2, 0), 24, 78 | IP_ADDRESS(192, 168, 0, 2)); 79 | */ 80 | // for chapter4 81 | /* 82 | configure_ip_address(get_net_device_by_name("router1-host1"), 83 | IP_ADDRESS(192, 168, 0, 1), IP_ADDRESS(255, 255, 255, 0)); 84 | configure_ip_address(get_net_device_by_name("router1-router2"), 85 | IP_ADDRESS(192, 168, 1, 1), IP_ADDRESS(255, 255, 255, 0)); 86 | configure_ip_address(get_net_device_by_name("router1-router4"), 87 | IP_ADDRESS(192, 168, 4, 2), IP_ADDRESS(255, 255, 255, 0)); 88 | //configure_net_route(IP_ADDRESS(192, 168, 5, 0), 24, IP_ADDRESS(192, 168, 89 | 1, 2)); configure_ip_net_route(IP_ADDRESS(192, 168, 5, 0), 24, 90 | IP_ADDRESS(192, 168, 4, 1)); 91 | */ 92 | 93 | // for chapter 5 94 | /* 95 | configure_ip_address(get_net_device_by_name("router1-host1"), 96 | IP_ADDRESS(192, 168, 1, 1), IP_ADDRESS(255, 255, 255, 0)); 97 | configure_ip_address(get_net_device_by_name("router1-router2"), 98 | IP_ADDRESS(192, 168, 0, 1), IP_ADDRESS(255, 255, 255, 0)); 99 | configure_ip_address(get_net_device_by_name("router1-router3"), 100 | IP_ADDRESS(192, 168, 3, 1), IP_ADDRESS(255, 255, 255, 0)); 101 | configure_ip_net_route(IP_ADDRESS(192, 168, 2, 0), 24, IP_ADDRESS(192, 168, 102 | 0, 2)); configure_ip_net_route(IP_ADDRESS(192, 168, 4, 0), 24, 103 | IP_ADDRESS(192, 168, 3, 2)); 104 | */ 105 | 106 | // for chapter 6 107 | /* 108 | configure_ip_address(get_net_device_by_name("router1-br0"), 109 | IP_ADDRESS(192, 168, 1, 1), 110 | IP_ADDRESS(255, 255, 255, 0)); 111 | configure_ip_address(get_net_device_by_name("router1-router2"), 112 | IP_ADDRESS(192, 168, 0, 1), 113 | IP_ADDRESS(255, 255, 255, 0)); 114 | configure_ip_net_route(IP_ADDRESS(192, 168, 2, 0), 24, 115 | IP_ADDRESS(192, 168, 0, 2)); 116 | configure_ip_nat(get_net_device_by_name("router1-br0"), 117 | get_net_device_by_name("router1-router2")); 118 | */ 119 | 120 | /* 121 | // for wsl 122 | configure_ip_address(get_net_device_by_name("eth0"), 123 | IP_ADDRESS(172, 22, 21, 166), 124 | IP_ADDRESS(255, 255, 240, 0)); 125 | */ 126 | 127 | configure_ip_address(get_net_device_by_name("router1-host1"), IP_ADDRESS(192, 168, 1, 1), IP_ADDRESS(255, 255, 255, 0)); 128 | configure_ip_address(get_net_device_by_name("router1-router2"), IP_ADDRESS(192, 168, 0, 1), IP_ADDRESS(255, 255, 255, 0)); 129 | 130 | ipv6_addr addr6; 131 | addr6.per_16.int1 = htons(0x2001); 132 | addr6.per_16.int2 = htons(0x0db8); 133 | addr6.per_16.int3 = htons(0x0001); 134 | addr6.per_16.int8 = htons(0x0001); 135 | 136 | configure_ipv6_address(get_net_device_by_name("router1-host1"), addr6, 64); 137 | configure_ip_net_route(IP_ADDRESS(192, 168, 2, 0), 24, IP_ADDRESS(192, 168, 0, 2)); 138 | 139 | } 140 | 141 | // 宣言のみ 142 | int net_device_transmit(struct net_device *dev, uint8_t *buffer, size_t len); 143 | int net_device_poll(net_device *dev); 144 | 145 | /** 146 | * デバイスのプラットフォーム依存のデータ 147 | */ 148 | struct net_device_data { 149 | int fd; // socketのFile descriptor 150 | }; 151 | 152 | /** 153 | * エントリーポイント 154 | */ 155 | int main() { 156 | struct ifreq ifr {}; 157 | struct ifaddrs *addrs; 158 | 159 | // ネットワークインターフェースを情報を取得 160 | getifaddrs(&addrs); 161 | 162 | for (ifaddrs *tmp = addrs; tmp; tmp = tmp->ifa_next) { 163 | if (tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_PACKET) { 164 | // ioctlでコントロールするインターフェースを設定 165 | memset(&ifr, 0, sizeof(ifr)); 166 | strcpy(ifr.ifr_name, tmp->ifa_name); 167 | 168 | // 無視するインターフェースか確認 169 | if (is_ignore_interface(tmp->ifa_name)) { 170 | printf("Skipped to enable interface %s\n", tmp->ifa_name); 171 | continue; 172 | } 173 | 174 | // socketをオープン 175 | int sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); 176 | if (sock == -1) { 177 | LOG_ERROR("socket open failed: %s\n", strerror(errno)); 178 | exit(EXIT_FAILURE); 179 | } 180 | 181 | // インターフェースのインデックスを取得 182 | if (ioctl(sock, SIOCGIFINDEX, &ifr) == -1) { 183 | LOG_ERROR("ioctl SIOCGIFINDEX failed: %s\n", strerror(errno)); 184 | close(sock); 185 | exit(EXIT_FAILURE); 186 | } 187 | 188 | // socketにインターフェースをbindする 189 | sockaddr_ll addr{}; 190 | memset(&addr, 0x00, sizeof(addr)); 191 | addr.sll_family = AF_PACKET; 192 | addr.sll_protocol = htons(ETH_P_ALL); 193 | addr.sll_ifindex = ifr.ifr_ifindex; 194 | if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) { 195 | LOG_ERROR("bind failed: %s\n", strerror(errno)); 196 | close(sock); 197 | exit(EXIT_FAILURE); 198 | } 199 | 200 | // インターフェースのMACアドレスを取得 201 | if (ioctl(sock, SIOCGIFHWADDR, &ifr) != 0) { 202 | LOG_ERROR("ioctl SIOCGIFHWADDR failed %s\n", strerror(errno)); 203 | close(sock); 204 | continue; 205 | } 206 | 207 | // net_device構造体を作成 208 | auto *dev = (net_device *)calloc( 209 | 1, 210 | sizeof(net_device) + 211 | sizeof( 212 | net_device_data)); // net_deviceの領域と、net_device_dataの領域を確保する 213 | dev->ops.transmit = net_device_transmit; // 送信用の関数を設定 214 | dev->ops.poll = net_device_poll; // 受信用の関数を設定 215 | 216 | strcpy(dev->name, 217 | tmp->ifa_name); // net_deviceにインターフェース名をセット 218 | memcpy(dev->mac_addr, &ifr.ifr_hwaddr.sa_data[0], 219 | 6); // net_deviceにMACアドレスをセット 220 | ((net_device_data *)dev->data)->fd = sock; 221 | 222 | printf("Created device %s socket %d address %s \n", dev->name, sock, 223 | mac_addr_toa(dev->mac_addr)); 224 | 225 | // net_deviceの連結リストに連結させる 226 | net_device *next; 227 | next = net_dev_list; 228 | net_dev_list = dev; 229 | dev->next = next; 230 | 231 | // ノンブロッキングに設定 232 | int val = fcntl(sock, F_GETFL, 0); // File descriptorのFlagを取得 233 | fcntl(sock, F_SETFL, 234 | val | O_NONBLOCK); // Non blockingのビットをセット 235 | } 236 | } 237 | // 確保されていたメモリを解放 238 | freeifaddrs(addrs); 239 | 240 | // 1つも有効化されたインターフェースをが無かったら終了 241 | if (net_dev_list == nullptr) { 242 | LOG_ERROR("No interface is enabled!\n"); 243 | exit(EXIT_FAILURE); 244 | } 245 | 246 | // IPルーティングテーブルの木構造のrootノードを作成 247 | ip_fib = (binary_trie_node *)calloc( 248 | 1, sizeof(binary_trie_node)); 249 | 250 | // ネットワーク設定の投入 251 | configure(); 252 | 253 | #ifdef ENABLE_COMMAND 254 | // 入力時にバッファリングせずにすぐ受け取る設定 255 | termios attr{}; 256 | tcgetattr(0, &attr); 257 | attr.c_lflag &= ~ICANON; 258 | attr.c_cc[VTIME] = 0; 259 | attr.c_cc[VMIN] = 1; 260 | tcsetattr(0, TCSANOW, &attr); 261 | fcntl(0, F_SETFL, O_NONBLOCK); // 標準入力にノンブロッキングの設定 262 | #endif 263 | while (true) { 264 | #ifdef ENABLE_COMMAND 265 | int input = getchar(); // 入力を受け取る 266 | if (input != -1) { // 入力があったら 267 | printf("\n"); 268 | if (input == 'a'){ 269 | dump_arp_table_entry(); 270 | #ifdef ENABLE_IPV6 271 | dump_nd_table_entry(); 272 | #endif 273 | } 274 | else if (input == 'r') 275 | dump_ip_fib(); 276 | else if (input == 'q') 277 | break; 278 | #ifdef ENABLE_NAT 279 | else if (input == 'n') 280 | dump_nat_tables(); 281 | #endif 282 | } 283 | #endif 284 | // デバイスから通信を受信 285 | for (net_device *dev = net_dev_list; dev; dev = dev->next) { 286 | dev->ops.poll(dev); 287 | } 288 | } 289 | printf("Goodbye!\n"); 290 | return 0; 291 | } 292 | 293 | /** 294 | * ネットデバイスの送信処理 295 | * @param dev 送信に使用するデバイス 296 | * @param buffer 送信するバッファ 297 | * @param len バッファの長さ 298 | */ 299 | int net_device_transmit(struct net_device *dev, uint8_t *buffer, size_t len) { 300 | // socketを通して送信 301 | send(((net_device_data *)dev->data)->fd, buffer, len, 0); 302 | return 0; 303 | } 304 | 305 | /** 306 | * ネットワークデバイスの受信処理 307 | * @param dev 受信を試みるデバイス 308 | */ 309 | int net_device_poll(net_device *dev) { 310 | uint8_t recv_buffer[1550]; 311 | // socketから受信 312 | ssize_t n = recv(((net_device_data *)dev->data)->fd, recv_buffer, 313 | sizeof(recv_buffer), 0); 314 | if (n == -1) { 315 | if (errno == EAGAIN) { // 受け取るデータが無い場合 316 | return 0; 317 | } else { 318 | return -1; // 他のエラーなら 319 | } 320 | } 321 | // 受信したデータをイーサネットに送る 322 | ethernet_input(dev, recv_buffer, n); 323 | /* 324 | // for book chapter 2 325 | printf("Received %lu bytes from %s: ", 326 | n, dev->name); 327 | for(int i = 0; i < n; ++i){ 328 | printf("%02x", recv_buffer[i]); 329 | } 330 | printf("\n"); 331 | */ 332 | return 0; 333 | } 334 | -------------------------------------------------------------------------------- /utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | 5 | /** 6 | * 16ビットでバイトオーダーを入れ替える 7 | */ 8 | uint16_t swap_byte_order_16(uint16_t v) { 9 | return (v & 0x00ff) << 8 | (v & 0xff00) >> 8; 10 | } 11 | 12 | /** 13 | * 32ビットでバイトオーダーを入れ替える 14 | */ 15 | uint32_t swap_byte_order_32(uint32_t v) { 16 | return (v & 0x000000ff) << 24 | (v & 0x0000ff00) << 8 | 17 | (v & 0x00ff0000) >> 8 | (v & 0xff000000) >> 24; 18 | } 19 | 20 | uint16_t ntohs(uint16_t v) { return swap_byte_order_16(v); } 21 | 22 | uint16_t htons(uint16_t v) { return swap_byte_order_16(v); } 23 | 24 | uint32_t ntohl(uint32_t v) { return swap_byte_order_32(v); } 25 | 26 | uint32_t htonl(uint32_t v) { return swap_byte_order_32(v); } 27 | 28 | uint8_t ip_string_pool_index = 0; 29 | char ip_string_pool[4] 30 | [16]; // 16バイト(xxx.xxx.xxx.xxxの文字数+1)の領域を4つ確保 31 | 32 | /** 33 | * IPアドレスから文字列に変換 34 | * @param in 35 | * @return 36 | */ 37 | const char *ip_ntoa(uint32_t in) { 38 | uint8_t a = in & 0x000000ff; 39 | uint8_t b = in >> 8 & 0x000000ff; 40 | uint8_t c = in >> 16 & 0x000000ff; 41 | uint8_t d = in >> 24 & 0x000000ff; 42 | ip_string_pool_index++; 43 | ip_string_pool_index %= 4; 44 | sprintf(ip_string_pool[ip_string_pool_index], "%d.%d.%d.%d", a, b, c, d); 45 | return ip_string_pool[ip_string_pool_index]; 46 | } 47 | 48 | // ホストバイトオーダーのIPアドレスから文字列に変換 49 | const char *ip_htoa(uint32_t in) { return ip_ntoa(htonl(in)); } 50 | 51 | uint8_t mac_addr_string_pool_index = 0; 52 | char mac_addr_string_pool 53 | [4][18]; // 18バイト(xxx.xxx.xxx.xxxの文字数+1)の領域を4つ確保 54 | 55 | /** 56 | * MACアドレスから文字列に変換 57 | * @param addr 58 | */ 59 | const char *mac_addr_toa(const uint8_t *addr) { 60 | mac_addr_string_pool_index++; 61 | mac_addr_string_pool_index %= 4; 62 | sprintf(mac_addr_string_pool[mac_addr_string_pool_index], 63 | "%02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1], addr[2], addr[3], 64 | addr[4], addr[5]); 65 | return mac_addr_string_pool[mac_addr_string_pool_index]; 66 | } 67 | 68 | /** 69 | * Checksumの計算 70 | * @param buffer 計算する対象のデータ 71 | * @param count 計算するデータの長さ 72 | * @param start 計算する初期値 73 | * @return 計算されたチェックサム 74 | */ 75 | uint16_t checksum_16(uint16_t *buffer, size_t count, uint16_t start) { 76 | uint32_t sum = start; 77 | // まず16ビット毎に足す 78 | while (count > 1) { 79 | sum += *buffer++; 80 | count -= 2; 81 | } 82 | // もし1バイト余ってたら足す 83 | if (count > 0) sum += *(uint8_t *)buffer; 84 | // あふれた桁を折り返して足す 85 | while (sum >> 16) 86 | sum = (sum & 0xffff) + (sum >> 16); 87 | 88 | return ~sum; // 論理否定(NOT)をとる 89 | } 90 | -------------------------------------------------------------------------------- /utils.h: -------------------------------------------------------------------------------- 1 | #ifndef CURO_UTILS_H 2 | #define CURO_UTILS_H 3 | 4 | #include 5 | #include 6 | 7 | uint16_t ntohs(uint16_t v); 8 | uint16_t htons(uint16_t v); 9 | 10 | uint32_t ntohl(uint32_t v); 11 | uint32_t htonl(uint32_t v); 12 | 13 | const char *ip_ntoa(uint32_t in); 14 | const char *ip_htoa(uint32_t in); 15 | const char *mac_addr_toa(const uint8_t *addr); 16 | 17 | uint16_t checksum_16(uint16_t *buffer, size_t count, uint16_t start = 0); 18 | 19 | #endif //CURO_UTILS_H 20 | --------------------------------------------------------------------------------