├── .gitignore ├── CMakeLists.txt ├── README.md ├── bgp.cpp ├── bgp.h ├── bgp_client.cpp ├── bgp_client.h ├── bgp_rib.cpp ├── bgp_rib.h ├── command.cpp ├── command.h ├── config.json.example ├── logger.h ├── main.cpp ├── tcp_socket.cpp ├── tcp_socket.h ├── tree.cpp └── tree.h /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | cmake-build-* 3 | config.json 4 | *.log -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(my_awesome_bgp) 2 | 3 | cmake_minimum_required(VERSION 3.16) 4 | 5 | set(CMAKE_CXX_STANDARD 17) 6 | 7 | include(FetchContent) 8 | FetchContent_Populate( 9 | json 10 | URL https://github.com/nlohmann/json/releases/download/v3.10.2/include.zip 11 | ) 12 | 13 | include_directories(${json_SOURCE_DIR}/single_include) 14 | 15 | add_executable(my_awesome_bgp main.cpp logger.h bgp_client.cpp bgp_client.h tcp_socket.cpp tcp_socket.h tree.cpp tree.h command.cpp command.h bgp.cpp bgp.h bgp_rib.cpp bgp_rib.h) 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # My Awesome BGP 2 | My Awesome BGPはSeccamp2021で開発しているBGPのソフトウェアです。 3 | 4 | ## 特徴 5 | - シングルスレッド 6 | - バイナリツリーによるBGPテーブル 7 | - 便利なコマンド 8 | 9 | ## 機能 10 | - [x] クライアント機能 11 | - [ ] サーバー機能 12 | - [x] 無制限のピア 13 | - [x] フルルートの受信 14 | - [x] 設定された経路の広告 15 | - [ ] 受信した経路の再広告 16 | - [x] 経路の取り消しの受信 17 | - [x] 4オクテットAS番号 18 | - [ ] プレフィックスリスト 19 | 20 | ## コマンド 21 | プログラムは起動するとログモードとなります。ログモードの状態で「c」と入力することでコマンドモードに移行できます。 22 | 23 | |コマンド|内容| 24 | |---|---| 25 | |exit|コマンドモードを終了| 26 | |break|ブレークを発生させる| 27 | |uptime|起動してからの秒数を表示する| 28 | |shutdown|プログラムを安全に終了させる| 29 | |test|試験的に設定された経路を広告する| 30 | |bgp statistics|統計情報を表示| 31 | |bgp route IP|BGPテーブルの経路情報を表示| 32 | |bgp neighbor PEER route IP|ピアの経路情報を表示| -------------------------------------------------------------------------------- /bgp.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "bgp.h" 10 | #include "bgp_rib.h" 11 | #include "logger.h" 12 | 13 | #define BGP_VERSION 4 14 | #define BGP_HOLD_TIME 180 15 | 16 | #define BGP_HEADER_SIZE 19 17 | #define BGP_OPEN_MESSAGE_SIZE 29 18 | 19 | void hex_dump(unsigned char* buffer, int len, bool is_separate){ 20 | if(console_mode == 1){ 21 | return; 22 | } 23 | if(is_separate) printf("|"); 24 | for(int i = 0; i < len; ++i){ 25 | if(is_separate){ 26 | printf("%02x|", buffer[i]); 27 | }else{ 28 | printf("%02x", buffer[i]); 29 | } 30 | } 31 | printf("\n"); 32 | } 33 | 34 | bool send_notification(bgp_peer* peer, int8_t error, uint16_t error_sub){ 35 | bgp_notification notification; 36 | memset(notification.header.maker, 0xff, 16); 37 | notification.header.length = htons(19); 38 | notification.header.type = NOTIFICATION; 39 | notification.error = error; 40 | notification.error_sub = htons(error_sub); 41 | return peer->send(¬ification, 19); 42 | } 43 | 44 | size_t encode_bgp_path_attributes_to_buffer(bgp_peer* peer, attributes* attr, unsigned char* buffer, size_t start_point){ 45 | size_t pointer = start_point; 46 | uint8_t flag = 0; 47 | flag |= TRANSITIVE; 48 | 49 | /** Origin **/ 50 | buffer[pointer++] = flag; 51 | buffer[pointer++] = ORIGIN; 52 | buffer[pointer++] = 1; 53 | buffer[pointer++] = attr->origin; 54 | 55 | /** AS Path **/ 56 | flag = 0; 57 | flag |= TRANSITIVE; 58 | flag |= EXTENDED_LENGTH; 59 | buffer[pointer++] = flag; 60 | buffer[pointer++] = AS_PATH; 61 | uint16_t ex_len; 62 | if(attr->as_path_length == 0){ 63 | ex_len = 0; 64 | memcpy(&buffer[pointer], &ex_len, 2); 65 | pointer += 2; 66 | }else{ 67 | ex_len = htons(attr->as_path_length * ((peer->is_4_octet_as_supported) ? 4 : 2) + 2); 68 | memcpy(&buffer[pointer], &ex_len, 2); 69 | pointer += 2; 70 | buffer[pointer++] = AS_SEQUENCE; 71 | buffer[pointer++] = attr->as_path_length; 72 | if(peer->is_4_octet_as_supported){ 73 | for(int i = 0; i < attr->as_path_length; ++i){ 74 | uint32_t asn = htonl(attr->as_path[i]); 75 | memcpy(&buffer[pointer], &asn, 4); 76 | pointer += 4; 77 | } 78 | }else{ 79 | for(int i = 0; i < attr->as_path_length; ++i){ 80 | uint16_t asn = htons(attr->as_path[i]); 81 | memcpy(&buffer[pointer], &asn, 2); 82 | pointer += 2; 83 | } 84 | } 85 | } 86 | 87 | flag = 0; 88 | flag |= TRANSITIVE; 89 | buffer[pointer++] = flag; 90 | buffer[pointer++] = NEXT_HOP; 91 | buffer[pointer++] = 4; 92 | memcpy(&buffer[pointer], &attr->next_hop, 4); 93 | pointer += 4; 94 | 95 | flag = 0; 96 | flag |= OPTIONAL; 97 | buffer[pointer++] = flag; 98 | buffer[pointer++] = MULTI_EXIT_DISC; 99 | buffer[pointer++] = 4; 100 | uint32_t med = htonl(attr->med); 101 | memcpy(&buffer[pointer], &med, 4); 102 | pointer += 4; 103 | 104 | flag = 0; 105 | flag |= TRANSITIVE; 106 | buffer[pointer++] = flag; 107 | buffer[pointer++] = LOCAL_PREF; 108 | buffer[pointer++] = 4; 109 | uint32_t local_pref = htonl(attr->local_pref); 110 | memcpy(&buffer[pointer], &local_pref, 4); 111 | pointer += 4; 112 | 113 | return pointer - start_point; 114 | } 115 | 116 | bool send_update_with_nlri(bgp_peer* peer, attributes* attr, uint32_t prefix, uint8_t prefix_len){ 117 | unsigned char buffer[1000]; 118 | memset(buffer, 0, 1000); 119 | uint16_t pointer = BGP_HEADER_SIZE; 120 | 121 | buffer[pointer++] = 0; // withdraw routes length (2byte) 122 | buffer[pointer++] = 0; 123 | 124 | pointer += 2; 125 | size_t attr_size = encode_bgp_path_attributes_to_buffer(peer, attr, buffer, pointer); 126 | size_t attr_size_ordered = htons(attr_size); 127 | memcpy(&buffer[pointer-2], &attr_size_ordered, 2); 128 | pointer += attr_size; 129 | 130 | buffer[pointer++] = prefix_len; 131 | if(prefix_len <= 8){ 132 | memcpy(&buffer[pointer], &prefix, 1); 133 | pointer += 1; 134 | }else if(prefix_len <= 16){ 135 | memcpy(&buffer[pointer], &prefix, 2); 136 | pointer += 2; 137 | }else if(prefix_len <= 24){ 138 | memcpy(&buffer[pointer], &prefix, 3); 139 | pointer += 3; 140 | }else if(prefix_len <= 32){ 141 | memcpy(&buffer[pointer], &prefix, 4); 142 | pointer += 4; 143 | } 144 | 145 | bgp_header header; 146 | memset(header.maker, 0xff, 16); 147 | header.type = UPDATE; 148 | header.length = htons(pointer); 149 | memcpy(&buffer[0], &header, BGP_HEADER_SIZE); 150 | //hex_dump(buffer, pointer); 151 | return peer->send(buffer, pointer); 152 | } 153 | 154 | size_t encode_bgp_capabilities_to_buffer(unsigned char* buffer, size_t start_point){ 155 | size_t pointer = start_point; 156 | 157 | /** MP BGP **/ 158 | buffer[pointer++] = CAPABILITIES; 159 | buffer[pointer++] = 6; 160 | buffer[pointer++] = MULTIPROTOCOL_EXTENSION_FOR_BGP4; 161 | buffer[pointer++] = 4; 162 | uint16_t afi = htons(afi::IPV4); 163 | memcpy(&buffer[pointer], &afi, 2); 164 | pointer += 2; 165 | buffer[pointer++] = 0; // Reserved 166 | buffer[pointer++] = safi::UNICAST; 167 | 168 | /** 4-octet AS number **/ 169 | buffer[pointer++] = CAPABILITIES; 170 | buffer[pointer++] = 6; 171 | buffer[pointer++] = SUPPORT_FOR_4_OCTET_AS_NUMBER_CAPABILITY; 172 | buffer[pointer++] = 4; 173 | uint32_t asn = htonl(my_as); 174 | memcpy(&buffer[pointer], &asn, 4); 175 | pointer += 4; 176 | 177 | return pointer - start_point; 178 | } 179 | 180 | bool send_open(bgp_peer* peer){ 181 | 182 | unsigned char buffer[1000]; 183 | memset(buffer, 0, 1000); 184 | uint16_t pointer = BGP_OPEN_MESSAGE_SIZE; 185 | 186 | uint16_t opt_len = encode_bgp_capabilities_to_buffer(buffer, pointer); 187 | pointer += opt_len; 188 | 189 | bgp_open open; 190 | memset(open.header.maker, 0xff, 16); 191 | open.header.length = htons(pointer); 192 | open.header.type = OPEN; 193 | open.version = BGP_VERSION; 194 | open.my_as = htons(my_as); 195 | open.hold_time = htons(BGP_HOLD_TIME); 196 | open.bgp_id = router_id; 197 | open.opt_length = opt_len; 198 | 199 | memcpy(&buffer[0], &open, BGP_OPEN_MESSAGE_SIZE); 200 | return peer->send(buffer, pointer); 201 | } 202 | 203 | bool bgp_update_handle_unfeasible_prefix(bgp_peer* peer, const unsigned char* buff, uint16_t unfeasible_routes_length){ 204 | 205 | uint32_t read_length = 21; // 19+2 206 | if(unfeasible_routes_length != 0){ 207 | uint32_t unfeasible_prefix; 208 | while(read_length < 19 + 2 + unfeasible_routes_length){ 209 | int prefix_len = buff[read_length]; 210 | if(prefix_len <= 8){ 211 | unfeasible_prefix = buff[read_length + 1]*256*256*256; 212 | read_length += 2; 213 | }else if(prefix_len <= 16){ 214 | unfeasible_prefix = buff[read_length + 1]*256*256*256 + buff[read_length + 2]*256*256; 215 | read_length += 3; 216 | }else if(prefix_len <= 24){ 217 | unfeasible_prefix = buff[read_length + 1]*256*256*256 + buff[read_length + 2]*256*256 + buff[read_length + 3]*256; 218 | read_length += 4; 219 | }else if(prefix_len <= 32){ 220 | unfeasible_prefix = buff[read_length + 1]*256*256*256 + buff[read_length + 2]*256*256 + buff[read_length + 3]*256 + buff[read_length + 4]; 221 | read_length += 5; 222 | }else{ 223 | log(log_level::ERROR, "Invalid packet"); 224 | abort(); // TODO 安定してきたら、return falseにする 225 | } 226 | log(log_level::DEBUG, "Unfeasible %s/%d", inet_ntoa(in_addr{.s_addr=ntohl(unfeasible_prefix)}), prefix_len); 227 | node* unfeasible_prefix_node = search_prefix(peer->adj_ribs_in, unfeasible_prefix, prefix_len, true); 228 | if(unfeasible_prefix_node != nullptr){ 229 | if(unfeasible_prefix_node->data->installed_loc_rib_node != nullptr){ 230 | delete_prefix(unfeasible_prefix_node->data->installed_loc_rib_node); 231 | unfeasible_prefix_node->data->installed_loc_rib_node = nullptr; 232 | log(log_level::DEBUG, "Withdrawn from loc_rib!"); 233 | } 234 | delete_prefix(unfeasible_prefix_node); 235 | log(log_level::DEBUG, "Withdraw success!"); 236 | peer->route_count--; 237 | }else{ 238 | log(log_level::ERROR, "Failed to withdraw %s/%d", inet_ntoa(in_addr{.s_addr = htonl(unfeasible_prefix)}), prefix_len); 239 | abort(); 240 | } 241 | } 242 | } 243 | return true; 244 | } 245 | 246 | bool bgp_update_handle_path_attribute(bgp_peer* peer, const unsigned char* buff, uint16_t unfeasible_routes_length, uint16_t total_path_attribute_length, adj_ribs_in_data& route_data){ 247 | 248 | int read_length = 19 + 2 + unfeasible_routes_length + 2; 249 | while(read_length < 19 + 2 + unfeasible_routes_length + 2 + total_path_attribute_length){ 250 | uint8_t flag = buff[read_length]; 251 | uint8_t type = buff[read_length + 1]; 252 | read_length += 2; 253 | uint16_t attribute_len; 254 | if(!(flag & EXTENDED_LENGTH)){ 255 | attribute_len = buff[read_length]; 256 | read_length += 1; 257 | }else{ 258 | memcpy(&attribute_len, &buff[read_length], 2); 259 | attribute_len = ntohs(attribute_len); 260 | read_length += 2; 261 | } 262 | switch(type){ 263 | case ORIGIN:{ 264 | uint8_t origin = buff[read_length]; 265 | route_data.path_attr.origin = origin; 266 | log(log_level::INFO, "Origin %d", origin); 267 | } 268 | break; 269 | case AS_PATH:{ 270 | uint8_t segment_type = buff[read_length]; 271 | uint8_t segment_length = buff[read_length + 1]; 272 | if(segment_type == bgp_path_attribute_as_path_segment_type::AS_SEQUENCE){ 273 | std::string as_list_str; 274 | bool is_overflowed = false; 275 | for(int i = 0; i < segment_length; i++){ 276 | std::ostringstream oss; 277 | as_list_str.append(" "); 278 | if(peer->is_4_octet_as_supported){ 279 | uint32_t asn; 280 | memcpy(&asn, &buff[read_length + 2 + i * 4], 4); 281 | asn = ntohl(asn); 282 | oss << asn; 283 | if(route_data.path_attr.as_path_length < 64){ 284 | route_data.path_attr.as_path[route_data.path_attr.as_path_length++] = asn; 285 | }else{ 286 | is_overflowed = true; 287 | } 288 | }else{ 289 | uint16_t asn; 290 | memcpy(&asn, &buff[read_length + 2 + i * 2], 2); 291 | asn = ntohs(asn); 292 | oss << asn; 293 | if(route_data.path_attr.as_path_length < 64){ 294 | route_data.path_attr.as_path[route_data.path_attr.as_path_length++] = asn; 295 | }else{ 296 | is_overflowed = true; 297 | } 298 | } 299 | as_list_str.append(oss.str()); 300 | } 301 | if(is_overflowed){ 302 | log(log_level::WARNING, "Overflowed AS PATH %d", segment_length); 303 | 304 | } 305 | log(log_level::INFO, "AS Path%s", as_list_str.c_str()); 306 | }else{ 307 | log(log_level::DEBUG, "Unable to interpret segment type %d", segment_type); 308 | } 309 | } 310 | break; 311 | case NEXT_HOP:{ 312 | assert(attribute_len == 4); 313 | route_data.path_attr.next_hop = buff[read_length]*256*256*256 + buff[read_length + 1]*256*256 + buff[read_length + 2]*256 + buff[read_length + 3]; 314 | log(log_level::INFO, "Next Hop %s", inet_ntoa(in_addr{.s_addr = ntohl(route_data.path_attr.next_hop)})); 315 | } 316 | break; 317 | case MULTI_EXIT_DISC: 318 | uint32_t med; 319 | memcpy(&med, &buff[read_length], 4); 320 | med = ntohl(med); 321 | route_data.path_attr.med = med; 322 | log(log_level::INFO, "MED %d", med); 323 | break; 324 | case LOCAL_PREF:{ 325 | uint32_t local_pref; 326 | memcpy(&local_pref, &buff[read_length], 4); 327 | local_pref = ntohl(local_pref); 328 | route_data.path_attr.local_pref = local_pref; 329 | log(log_level::INFO, "Local Pref %d", local_pref); 330 | } 331 | break; 332 | case ATOMIC_AGGREGATE: 333 | log(log_level::INFO, "Atomic Aggregate"); 334 | break; 335 | case AGGREGATOR: 336 | { 337 | uint32_t asn; 338 | if(peer->is_4_octet_as_supported){ 339 | memcpy(&asn, &buff[read_length], 4); 340 | read_length += 4; 341 | }else{ 342 | uint16_t asn_2; 343 | memcpy(&asn_2, &buff[read_length], 2); 344 | read_length += 2; 345 | asn = asn_2; 346 | } 347 | uint32_t address; 348 | memcpy(&address, &buff[read_length], 4); 349 | log(log_level::INFO, "Aggregator %d, %s", asn, inet_ntoa(in_addr{.s_addr = address})); 350 | } 351 | break; 352 | case AS4_PATH: 353 | { 354 | uint8_t segment_type = buff[read_length]; 355 | uint8_t segment_length = buff[read_length + 1]; 356 | if(segment_type == bgp_path_attribute_as_path_segment_type::AS_SEQUENCE){ 357 | std::string as_list_str; 358 | for(int i = 0; i < segment_length; i++){ 359 | uint32_t asn; 360 | memcpy(&asn, &buff[read_length + 2 + i * 4], 4); 361 | asn = ntohl(asn); 362 | std::ostringstream oss; 363 | oss << asn; 364 | as_list_str.append(" "); 365 | as_list_str.append(oss.str()); 366 | /* 367 | if(route_data.path_attr.as4_path_length < 64){ 368 | route_data.path_attr.as4_path[route_data.path_attr.as_path_length++] = asn; 369 | }else{ 370 | log(log_level::WARNING, "Overflowed AS4 PATH %d", segment_length); 371 | }*/ 372 | } 373 | log(log_level::INFO, "AS4 Path%s", as_list_str.c_str()); 374 | }else{ 375 | log(log_level::DEBUG, "Unable to interpret segment type %d", segment_type); 376 | } 377 | } 378 | break; 379 | default: 380 | log(log_level::INFO, "Unhandled path attributes type %d", type); 381 | break; 382 | } 383 | read_length += attribute_len; 384 | } 385 | return true; 386 | } 387 | 388 | bool bgp_update(bgp_peer* peer, unsigned char* buff, int entire_length){ 389 | uint16_t unfeasible_routes_length; 390 | memcpy(&unfeasible_routes_length, &buff[19], 2); 391 | unfeasible_routes_length = ntohs(unfeasible_routes_length); 392 | int read_length; 393 | 394 | bgp_update_handle_unfeasible_prefix(peer, buff, unfeasible_routes_length); 395 | 396 | adj_ribs_in_data route_data; 397 | uint16_t total_path_attribute_length; 398 | memcpy(&total_path_attribute_length, &buff[19 + 2 + unfeasible_routes_length], 2); 399 | total_path_attribute_length = ntohs(total_path_attribute_length); 400 | 401 | bgp_update_handle_path_attribute(peer, buff, unfeasible_routes_length, total_path_attribute_length, route_data); 402 | 403 | read_length = 19 + 2 + unfeasible_routes_length + 2 + total_path_attribute_length; 404 | 405 | while(read_length < entire_length){ 406 | uint32_t prefix; 407 | int prefix_len = buff[read_length]; 408 | if(prefix_len <= 8){ 409 | prefix = buff[read_length + 1]*256*256*256; 410 | read_length += 2; 411 | }else if(prefix_len <= 16){ 412 | prefix = buff[read_length + 1]*256*256*256 + buff[read_length + 2]*256*256; 413 | read_length += 3; 414 | }else if(prefix_len <= 24){ 415 | prefix = buff[read_length + 1]*256*256*256 + buff[read_length + 2]*256*256 + buff[read_length + 3]*256; 416 | read_length += 4; 417 | }else if(prefix_len <= 32){ 418 | prefix = buff[read_length + 1]*256*256*256 + buff[read_length + 2]*256*256 + buff[read_length + 3]*256 + buff[read_length + 4]; 419 | read_length += 5; 420 | }else{ 421 | log(log_level::ERROR, "Invalid packet"); 422 | break; 423 | } 424 | log(log_level::DEBUG, "%s/%d", inet_ntoa(in_addr{.s_addr=ntohl(prefix)}), prefix_len); 425 | bool is_updated; 426 | node* added = add_prefix(peer->adj_ribs_in, prefix, prefix_len, route_data, &is_updated); 427 | if(!is_updated){ 428 | peer->route_count++; 429 | }else{ 430 | log(log_level::DEBUG, "Route updated!"); 431 | } 432 | attempt_to_install_bgp_loc_rib(peer, added); 433 | } 434 | return true; 435 | } -------------------------------------------------------------------------------- /bgp.h: -------------------------------------------------------------------------------- 1 | #ifndef MY_AWESOME_BGP_BGP_H 2 | #define MY_AWESOME_BGP_BGP_H 3 | 4 | #include "tree.h" 5 | 6 | extern uint32_t my_as; 7 | extern uint32_t router_id; 8 | 9 | struct adj_ribs_in_data; // from bgp_rib.h 10 | 11 | class bgp_peer{ 12 | public: 13 | uint8_t index = 0; 14 | uint8_t state = 0; 15 | bool is_4_octet_as_supported = false; 16 | node* adj_ribs_in = nullptr; 17 | uint32_t bgp_id = 0; 18 | uint32_t route_count = 0; 19 | virtual bool send(void* buffer, size_t length) = 0; 20 | }; 21 | 22 | enum bgp_peer_state{ 23 | IDLE, 24 | CONNECT, 25 | ACTIVE, 26 | OPEN_SENT, 27 | OPEN_CONFIRM, 28 | ESTABLISHED, 29 | }; 30 | 31 | enum afi{ 32 | IPV4 = 1, 33 | IPV6 = 2, 34 | }; 35 | 36 | enum safi{ 37 | UNICAST = 1, 38 | MULTICAST = 2, 39 | }; 40 | 41 | // https://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml 42 | 43 | enum bgp_message_type{ 44 | OPEN = 1, 45 | UPDATE = 2, 46 | NOTIFICATION = 3, 47 | KEEPALIVE = 4 48 | }; 49 | 50 | enum bgp_path_attribute_type{ 51 | ORIGIN = 1, 52 | AS_PATH = 2, 53 | NEXT_HOP = 3, 54 | MULTI_EXIT_DISC = 4, 55 | LOCAL_PREF = 5, 56 | ATOMIC_AGGREGATE = 6, 57 | AGGREGATOR = 7, 58 | COMMUNITY = 8, 59 | ORIGINATOR_ID = 9, 60 | CLUSTER_LIST = 10, 61 | DPA = 11, 62 | ADVERTISER = 12, 63 | RCID_PATH = 13 , 64 | CLUSTER_ID = 13, 65 | MP_REACH_NLRI = 14, 66 | MP_UNREACH_NLRI = 15, 67 | EXTENDED = 16, 68 | AS4_PATH = 17, 69 | AS4_AGGREGATOR = 18, 70 | }; 71 | 72 | enum bgp_path_attribute_origin{ 73 | IGP = 0, 74 | EGP = 1, 75 | INCOMPLETE = 2 76 | }; 77 | 78 | enum bgp_error_code{ 79 | MESSAGE_HEADER_ERROR = 1, 80 | OPEN_MESSAGE_ERROR = 2, 81 | UPDATE_MESSAGE_ERROR = 3, 82 | HOLD_TIMER_EXPIRED = 4, 83 | FINITE_STATE_MACHINE_ERROR = 5, 84 | CEASE = 6, 85 | ROUTE_REFRESH_MESSAGE_ERROR = 7 86 | }; 87 | 88 | enum bgp_error_sub_code_open{ 89 | UNSPESIFIC = 0, 90 | UNSUPPORTED_VERSION_NUMBER = 1, 91 | BAD_PEER_AS = 2, 92 | // ... 93 | 94 | }; 95 | 96 | enum bgp_open_optional_parameter_type{ 97 | AUTHENTICATION = 1, 98 | CAPABILITIES = 2 99 | }; 100 | 101 | enum bgp_capability_code{ 102 | MULTIPROTOCOL_EXTENSION_FOR_BGP4 = 1, 103 | ROUTE_REFRESH_CAPABILITY_FOR_BGP4 = 2, 104 | OUTBOUND_ROUTE_FILTERING_CAPABILITY = 3, 105 | SUPPORT_FOR_4_OCTET_AS_NUMBER_CAPABILITY = 65 106 | }; 107 | 108 | enum bgp_path_attribute_flag{ 109 | EXTENDED_LENGTH = 1 << 4, 110 | PARTIAL = 1 << 5, 111 | TRANSITIVE = 1 << 6, 112 | OPTIONAL = 1 << 7 113 | }; 114 | 115 | enum bgp_path_attribute_as_path_segment_type{ 116 | AS_SET = 1, 117 | AS_SEQUENCE = 2, 118 | AS_CONFED_SEQUENCE = 3, 119 | AS_CONFED_SET = 4 120 | }; 121 | 122 | struct bgp_header{ 123 | unsigned char maker[16]; 124 | uint16_t length; 125 | uint8_t type; 126 | } __attribute__((packed)); 127 | 128 | struct bgp_open{ 129 | bgp_header header; 130 | uint8_t version; 131 | uint16_t my_as; 132 | uint16_t hold_time; 133 | uint32_t bgp_id; 134 | uint8_t opt_length; 135 | unsigned char option[]; 136 | } __attribute__((packed)); 137 | 138 | struct bgp_notification{ 139 | bgp_header header; 140 | uint8_t error; 141 | uint8_t error_sub; 142 | unsigned char data[]; 143 | } __attribute__((packed)); 144 | 145 | struct attributes; 146 | 147 | void hex_dump(unsigned char* buffer, int len, bool is_separate = false); 148 | 149 | bool send_update_with_nlri(bgp_peer* peer, attributes* attr, uint32_t prefix, uint8_t prefix_len); 150 | bool send_notification(bgp_peer* peer, int8_t error, uint16_t error_sub); 151 | bool send_open(bgp_peer* peer); 152 | 153 | bool bgp_update(bgp_peer* peer, unsigned char* buff, int entire_length); 154 | 155 | #endif //MY_AWESOME_BGP_BGP_H 156 | -------------------------------------------------------------------------------- /bgp_client.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "bgp_client.h" 8 | #include "bgp_rib.h" 9 | #include "logger.h" 10 | #include "tcp_socket.h" 11 | 12 | #define CONNECT_TIMEOUT_MS 1000 13 | 14 | #define COOL_LOOP_TIME_CONNECTION_REFUSED 100000 15 | #define COOL_LOOP_TIME_PEER_DOWN 10000 16 | 17 | bool bgp_client_peer::send(void* buffer, size_t length){ 18 | if(::send(this->sock, buffer, length, 0) <= 0){ 19 | return false; 20 | } 21 | return true; 22 | } 23 | 24 | bool try_to_connect(bgp_client_peer* peer){ 25 | peer->server_address.sin_port = getservbyname("bgp", "tcp")->s_port; 26 | if((peer->sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0){ 27 | log(log_level::ERROR, "Failed to create socket"); 28 | return false; 29 | } 30 | if(connect_with_timeout(peer->sock, (struct sockaddr*) &peer->server_address, sizeof(peer->server_address), CONNECT_TIMEOUT_MS) < 0){ 31 | if(errno == EINTR){ 32 | log(log_level::ERROR, "Timeout to connect server"); 33 | }else{ 34 | log(log_level::ERROR, "Failed to connect server"); 35 | } 36 | close(peer->sock); 37 | return false; 38 | } 39 | 40 | set_nonblocking(peer->sock); 41 | log(log_level::INFO, "Connected to %s", inet_ntoa(peer->server_address.sin_addr)); 42 | 43 | // send_open(peer); 44 | return true; 45 | } 46 | 47 | void close_client_peer(bgp_client_peer* peer){ 48 | if(peer->adj_ribs_in != nullptr){ // おそらくalways true 49 | log(log_level::DEBUG, "Cleaned table sock %d", peer->sock); 50 | delete_prefix(peer->adj_ribs_in, true); // TODO bgp_loc_ribから経路を削除する 51 | // peer->adj_ribs_in = nullptr; 52 | } 53 | close(peer->sock); 54 | } 55 | 56 | bool loop_established(bgp_client_peer* peer){ 57 | int len; 58 | unsigned char buff[4096]; 59 | //printf("\e[m"); 60 | memset(buff, 0x00, 4096); 61 | len = recv(peer->sock, &buff, 19, 0); 62 | if(len <= 0){ 63 | return true; 64 | } 65 | auto* bgphp = reinterpret_cast(buff); 66 | 67 | // hex_dump(buff, 29); // dump header 68 | int entire_length = ntohs(bgphp->length); 69 | log(log_level::TRACE, "Receiving %d bytes", entire_length); 70 | 71 | int append_len, remain_byte; 72 | uint16_t error = 0; 73 | while(len < entire_length){ 74 | remain_byte = entire_length - len; 75 | log(log_level::TRACE, "%d bytes remain", remain_byte); 76 | append_len = recv(peer->sock, &buff[len], std::min(remain_byte, 1000), 0); 77 | if(append_len <= 0){ 78 | if(++error > 10000){ 79 | log(log_level::ERROR, "Failed to receive packet"); 80 | abort(); 81 | } 82 | continue; 83 | } 84 | log(log_level::TRACE, "New %d bytes received", append_len); 85 | len += append_len; 86 | } 87 | 88 | switch(bgphp->type){ 89 | case OPEN:{ 90 | log(log_level::INFO, "Open Received"); 91 | auto* bgpopp = reinterpret_cast(buff); 92 | log(log_level::INFO, "Version: %d", bgpopp->version); 93 | log(log_level::INFO, "My AS: %d", ntohs(bgpopp->my_as)); 94 | log(log_level::INFO, "Hold Time: %d", ntohs(bgpopp->hold_time)); 95 | log(log_level::INFO, "BGP Id: %d", ntohl(bgpopp->bgp_id)); 96 | log(log_level::INFO, "Opt Length: %d", bgpopp->opt_length); 97 | 98 | if(ntohs(bgpopp->my_as) != peer->remote_as){ 99 | send_notification(peer, bgp_error_code::OPEN_MESSAGE_ERROR, bgp_error_sub_code_open::BAD_PEER_AS); 100 | return false; 101 | } 102 | 103 | int read_length = 29; 104 | while(read_length < 29 + bgpopp->opt_length){ 105 | int option_type = buff[read_length]; 106 | read_length++; 107 | int option_length = buff[read_length]; 108 | read_length++; 109 | switch(option_type){ 110 | case bgp_open_optional_parameter_type::CAPABILITIES:{ 111 | uint8_t capability_type = buff[read_length]; 112 | read_length++; 113 | uint8_t capability_length = buff[read_length]; 114 | read_length++; 115 | read_length += capability_length; 116 | log(log_level::INFO, "Capability type : %d", capability_type); 117 | switch(capability_type){ 118 | case bgp_capability_code::MULTIPROTOCOL_EXTENSION_FOR_BGP4: 119 | break; 120 | case bgp_capability_code::SUPPORT_FOR_4_OCTET_AS_NUMBER_CAPABILITY: 121 | peer->is_4_octet_as_supported = true; 122 | log(log_level::INFO, "Supported 4-octet AS number!"); 123 | break; 124 | } 125 | } 126 | break; 127 | default: 128 | log(log_level::INFO, "Option type : %d", option_type); 129 | break; 130 | } 131 | } 132 | peer->bgp_id = ntohl(bgpopp->bgp_id); 133 | 134 | if(!send_open(peer)){ 135 | log(log_level::ERROR, "Failed to send packet"); 136 | return false; 137 | } 138 | } 139 | break; 140 | case UPDATE:{ 141 | log(log_level::INFO, "Update received"); 142 | bgp_update(peer, buff, entire_length); 143 | } 144 | break; 145 | case NOTIFICATION:{ 146 | log(log_level::NOTICE, "Notification received"); 147 | auto* bgpntp = reinterpret_cast(buff); 148 | log(log_level::NOTICE, "Error: %d", bgpntp->error); 149 | log(log_level::NOTICE, "Sub: %d", bgpntp->error_sub); 150 | return false; 151 | } 152 | case KEEPALIVE:{ 153 | if(peer->state == OPEN_CONFIRM){ 154 | peer->adj_ribs_in->is_prefix = true; 155 | peer->adj_ribs_in->prefix = 0; 156 | peer->adj_ribs_in->prefix_len = 0; 157 | peer->adj_ribs_in->data = nullptr; 158 | peer->adj_ribs_in->parent = nullptr; 159 | peer->adj_ribs_in->node_0 = nullptr; 160 | peer->adj_ribs_in->node_1 = nullptr; 161 | peer->state = ESTABLISHED; 162 | } 163 | log(log_level::INFO, "Keepalive Received"); 164 | bgp_header header; 165 | memset(header.maker, 0xff, 16); 166 | header.length = htons(19); 167 | header.type = KEEPALIVE; 168 | if(!peer->send(&header, len)){ 169 | log(log_level::ERROR, "Failed to send packet"); 170 | return false; 171 | } 172 | } 173 | break; 174 | default: 175 | log(log_level::ERROR, "Unknown type received %d", bgphp->type); 176 | break; 177 | } 178 | return true; 179 | } 180 | 181 | bool bgp_client_loop(bgp_client_peer* peer){ 182 | switch(peer->state){ 183 | case IDLE: 184 | if(peer->connect_cool_loop_time == 0){ 185 | if(try_to_connect(peer)){ 186 | peer->state = OPEN_CONFIRM; 187 | }else{ 188 | peer->connect_cool_loop_time = COOL_LOOP_TIME_CONNECTION_REFUSED; 189 | } 190 | peer->connect_cool_loop_time = COOL_LOOP_TIME_PEER_DOWN; 191 | }else{ 192 | peer->connect_cool_loop_time--; 193 | } 194 | break; 195 | case OPEN_CONFIRM: 196 | case ESTABLISHED: 197 | if(!loop_established(peer)){ 198 | peer->state = IDLE; 199 | close_client_peer(peer); 200 | } 201 | break; 202 | } 203 | return true; 204 | } -------------------------------------------------------------------------------- /bgp_client.h: -------------------------------------------------------------------------------- 1 | #ifndef MY_AWESOME_BGP_BGP_CLIENT_H 2 | #define MY_AWESOME_BGP_BGP_CLIENT_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "bgp.h" 9 | 10 | extern uint8_t console_mode; 11 | 12 | class bgp_client_peer : public bgp_peer{ 13 | public: 14 | int sock = 0; 15 | struct sockaddr_in server_address = {}; 16 | uint32_t remote_as = 0; 17 | uint32_t connect_cool_loop_time = 0; 18 | bool send(void* buffer, size_t length) override; 19 | }; 20 | 21 | void close_client_peer(bgp_client_peer* peer); 22 | bool bgp_client_loop(bgp_client_peer* peer); 23 | 24 | #endif //MY_AWESOME_BGP_BGP_CLIENT_H 25 | -------------------------------------------------------------------------------- /bgp_rib.cpp: -------------------------------------------------------------------------------- 1 | #include "bgp.h" 2 | #include "bgp_rib.h" 3 | #include "logger.h" 4 | 5 | bool best_path_selection_battle(bgp_peer* my_peer, node* me, node* opponent){ // TODO 古い自分のピアとの経路との対決でエラーになりそう 6 | if(me->data->path_attr.local_pref != opponent->data->path_attr->local_pref){ 7 | if(me->data->path_attr.local_pref > opponent->data->path_attr->local_pref){ 8 | return true; 9 | } 10 | return false; 11 | } 12 | if(me->data->path_attr.as_path_length != opponent->data->path_attr->as_path_length){ 13 | if(me->data->path_attr.as_path_length < opponent->data->path_attr->as_path_length){ 14 | return true; 15 | } 16 | return false; 17 | } 18 | if(me->data->path_attr.origin != opponent->data->path_attr->origin){ 19 | if(me->data->path_attr.origin < opponent->data->path_attr->origin){ 20 | return true; 21 | } 22 | return false; 23 | } 24 | if(me->data->path_attr.med != opponent->data->path_attr->med){ 25 | if(me->data->path_attr.med < opponent->data->path_attr->med){ 26 | return true; 27 | } 28 | return false; 29 | } 30 | if(my_peer->bgp_id != opponent->data->peer->bgp_id){ 31 | if(my_peer->bgp_id < opponent->data->peer->bgp_id){ 32 | return true; 33 | } 34 | return false; 35 | } 36 | return true; 37 | } 38 | 39 | bool attempt_to_install_bgp_loc_rib(bgp_peer* my_peer, node* route){ 40 | node* installed_route = search_prefix(bgp_loc_rib, route->prefix, route->prefix_len, true); 41 | if(installed_route == nullptr or best_path_selection_battle(my_peer, route, installed_route)){ 42 | if(installed_route != nullptr){ 43 | installed_route->data->source_adj_ribs_in_node->data->installed_loc_rib_node = nullptr; // adj_ribs_inに、経路が上書きされてあなたのとこの情報はもはや利用されてないことを伝える 44 | delete_prefix(installed_route); 45 | // log(log_level::INFO, "Loc_rib updated"); 46 | } 47 | loc_rib_data data; 48 | data.peer = my_peer; 49 | data.source_adj_ribs_in_node = route; 50 | data.path_attr = &route->data->path_attr; 51 | node* res = add_prefix(bgp_loc_rib, route->prefix, route->prefix_len, data); 52 | route->data->installed_loc_rib_node = res; 53 | // log(log_level::INFO, "New route installed to loc_rib"); 54 | return true; 55 | } 56 | return false; 57 | } -------------------------------------------------------------------------------- /bgp_rib.h: -------------------------------------------------------------------------------- 1 | #ifndef MY_AWESOME_BGP_BGP_RIB_H 2 | #define MY_AWESOME_BGP_BGP_RIB_H 3 | 4 | #include "tree.h" 5 | 6 | struct bgp_peer; // from bgp.h 7 | struct loc_rib_data; 8 | struct adj_ribs_out_data; 9 | 10 | struct attributes{ 11 | uint8_t origin = 0; 12 | uint8_t as_path_length = 0; 13 | uint32_t as_path[64]{}; // TODO 可変長のが良い 14 | uint32_t next_hop = 0; 15 | uint32_t med = 0; 16 | uint32_t local_pref = 0; 17 | // attributes* next; // TODO 複数の属性を持たせられるように 18 | }; 19 | 20 | struct adj_ribs_in_data{ 21 | attributes path_attr; 22 | node* installed_loc_rib_node = nullptr; 23 | }; 24 | 25 | struct loc_rib_data{ 26 | bgp_peer* peer = nullptr; 27 | attributes* path_attr = nullptr; 28 | node* source_adj_ribs_in_node = nullptr; 29 | node* installed_adj_ribs_out_node = nullptr; 30 | }; 31 | 32 | struct adj_ribs_out_data{ 33 | attributes* path_attr = nullptr; 34 | node* source_loc_rib_node = nullptr; 35 | }; 36 | 37 | extern node* bgp_loc_rib; 38 | 39 | bool best_path_selection_battle(bgp_peer* my_peer, attributes* my_attr, attributes* opponent_attr); 40 | bool attempt_to_install_bgp_loc_rib(bgp_peer* my_peer, node* route); 41 | 42 | #endif //MY_AWESOME_BGP_BGP_RIB_H 43 | -------------------------------------------------------------------------------- /command.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "bgp_client.h" 8 | #include "bgp_rib.h" 9 | #include "command.h" 10 | #include "logger.h" 11 | 12 | bgp_peer* get_peer_by_string(const std::string& search){ 13 | for(auto & peer : peers){ 14 | if(search == inet_ntoa(peer.server_address.sin_addr)){ 15 | return &peer; 16 | } 17 | } 18 | return nullptr; 19 | } 20 | 21 | std::string as_path_to_string(uint32_t* as_path_list, uint8_t as_path_length){ 22 | std::string as_path_str; 23 | for(int j = 0; j < as_path_length; ++j){ 24 | std::ostringstream oss; 25 | oss << as_path_list[j]; 26 | as_path_str.append(oss.str()); 27 | if(j != as_path_length-1){ 28 | as_path_str.append(" "); 29 | } 30 | } 31 | return as_path_str; 32 | } 33 | 34 | command_result_status bgp_statistic_command(){ 35 | for(int i = 0; i < peers.size(); ++i){ 36 | console("Peer %d received %d routes", i, peers[i].route_count); 37 | } 38 | return command_result_status::SUCCESS; 39 | } 40 | 41 | command_result_status bgp_route_command(const std::vector& command_params){ 42 | size_t param_count = command_params.size(); 43 | if(param_count < 3){ 44 | return command_result_status::INVALID_PARAMS; 45 | } 46 | 47 | node* res = search_prefix(bgp_loc_rib, ntohl(inet_addr(command_params[2].c_str()))); // TODO 入力値検証 48 | char prefix[16]; 49 | char next_hop[16]; 50 | if(res->is_prefix and res->data != nullptr){ 51 | memcpy(&prefix, inet_ntoa(in_addr{.s_addr = htonl(res->prefix)}), 16); 52 | memcpy(&next_hop, inet_ntoa(in_addr{.s_addr = htonl(res->data->path_attr->next_hop)}), 16); 53 | console("%s/%d origin %d, nexthop %s, med %d, local-pref %d, loc_rib", prefix, res->prefix_len, 54 | res->data->path_attr->origin, next_hop, res->data->path_attr->med, 55 | res->data->path_attr->local_pref); 56 | console("as-path %s, length %d", as_path_to_string(res->data->path_attr->as_path, res->data->path_attr->as_path_length).c_str(), res->data->path_attr->as_path_length); 57 | } 58 | 59 | return command_result_status::SUCCESS; 60 | } 61 | 62 | command_result_status bgp_neighbor_route_command(const std::vector& command_params, bgp_peer* peer){ 63 | size_t param_count = command_params.size(); 64 | if(param_count < 5){ 65 | return command_result_status::INVALID_PARAMS; 66 | } 67 | 68 | node* res = search_prefix(peer->adj_ribs_in, ntohl(inet_addr(command_params[4].c_str()))); 69 | char prefix[16]; 70 | 71 | char next_hop[16]; 72 | if(res->is_prefix and res->data != nullptr){ // TODO is_prefixがtrueでdataがnullptrの時がどのような場合かよく考える 73 | memcpy(&prefix, inet_ntoa(in_addr{.s_addr = htonl(res->prefix)}), 16); 74 | memcpy(&next_hop, inet_ntoa(in_addr{.s_addr = htonl(res->data->path_attr.next_hop)}), 16); 75 | console("%s/%d origin %d, nexthop %s, med %d, local-pref %d, peer %d", prefix, res->prefix_len, 76 | res->data->path_attr.origin, next_hop, res->data->path_attr.med, 77 | res->data->path_attr.local_pref, peer->index); 78 | console("as-path %s, length %d", as_path_to_string(res->data->path_attr.as_path, res->data->path_attr.as_path_length).c_str(), res->data->path_attr.as_path_length); 79 | } 80 | 81 | return command_result_status::SUCCESS; 82 | } 83 | 84 | command_result_status bgp_neighbor_command(const std::vector& command_params){ 85 | size_t param_count = command_params.size(); 86 | if(param_count < 3){ 87 | return command_result_status::INVALID_PARAMS; 88 | } 89 | auto* neighbor_peer = get_peer_by_string(command_params[2]); 90 | if(neighbor_peer == nullptr){ 91 | console("Neighbor not found"); 92 | return command_result_status::INVALID_PARAMS; 93 | } 94 | 95 | if(param_count < 4){ 96 | return command_result_status::INVALID_PARAMS; 97 | } 98 | 99 | if(command_params[3] == "route"){ // bgp neighbor x.x.x.x route x.x.x.x 100 | return bgp_neighbor_route_command(command_params, neighbor_peer); 101 | } 102 | 103 | return command_result_status::SUCCESS; 104 | } 105 | 106 | command_result_status bgp_command(const std::vector& command_params){ 107 | size_t param_count = command_params.size(); 108 | if(param_count < 2){ 109 | return command_result_status::INVALID_PARAMS; 110 | } 111 | if(command_params[1] == "route"){ // bgp route 112 | return bgp_route_command(command_params); 113 | }else if(command_params[1] == "statistics"){ // bgp statistics 114 | return bgp_statistic_command(); 115 | }else if(command_params[1] == "neighbor"){ // bgp neighbor 116 | return bgp_neighbor_command(command_params); 117 | } 118 | 119 | return command_result_status::INVALID_PARAMS; 120 | } 121 | 122 | command_result_status execute_command(const std::string& command){ 123 | auto command_params = std::vector(); 124 | size_t offset = 0; 125 | while(true){ 126 | size_t separator = command.find(' ', offset); 127 | if (separator == std::string::npos) { 128 | command_params.push_back(command.substr(offset)); 129 | break; 130 | } 131 | command_params.push_back(command.substr(offset, separator - offset)); 132 | offset = separator + 1; 133 | } 134 | size_t param_count = command_params.size(); 135 | 136 | if(command_params[0] == "bgp"){ 137 | return bgp_command(command_params); 138 | } 139 | return command_result_status::NOT_FOUND; 140 | } 141 | -------------------------------------------------------------------------------- /command.h: -------------------------------------------------------------------------------- 1 | #ifndef MY_AWESOME_BGP_COMMAND_H 2 | #define MY_AWESOME_BGP_COMMAND_H 3 | 4 | #include 5 | 6 | #include "tree.h" 7 | 8 | struct bgp_client_peer; // from bgp_client.h 9 | struct loc_rib_data; // from bgp_rib.h 10 | 11 | extern std::vector peers; 12 | extern node* bgp_loc_rib; 13 | 14 | enum class command_result_status{ 15 | SUCCESS, 16 | NOT_FOUND, 17 | INVALID_PARAMS, 18 | }; 19 | 20 | command_result_status execute_command(const std::string& command); 21 | 22 | #endif //MY_AWESOME_BGP_COMMAND_H 23 | -------------------------------------------------------------------------------- /config.json.example: -------------------------------------------------------------------------------- 1 | { 2 | "my_as": 64496, 3 | "router-id": "172.16.1.2", 4 | "neighbors": [ 5 | { 6 | "address": "172.16.1.1", 7 | "remote-as": 64497 8 | }, 9 | { 10 | "address": "172.16.1.3", 11 | "remote-as": 64498 12 | } 13 | ], 14 | "networks": [ 15 | { 16 | "prefix": "192.0.2.0", 17 | "prefix-length": 24 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /logger.h: -------------------------------------------------------------------------------- 1 | #ifndef MY_AWESOME_BGP_LOGGER_H 2 | #define MY_AWESOME_BGP_LOGGER_H 3 | 4 | #include 5 | 6 | extern uint8_t log_id; 7 | extern uint8_t console_mode; 8 | 9 | enum class log_level{ 10 | ERROR, 11 | WARNING, 12 | NOTICE, 13 | INFO, 14 | DEBUG, 15 | TRACE 16 | }; 17 | 18 | template 19 | void console(const char *format, Args const & ... args){ 20 | if(console_mode == 0){ 21 | return; 22 | } 23 | printf("\e[m"); 24 | printf("[CONSOLE] "); 25 | printf(format, args ...); 26 | printf("\n"); 27 | } 28 | 29 | template 30 | void log(log_level level, const char *format, Args const & ... args){ 31 | if(console_mode == 1 and level > log_level::NOTICE){ 32 | return; 33 | } 34 | if(level > log_level::TRACE){ 35 | return; 36 | } 37 | if(level == log_level::ERROR){ 38 | if(log_id == 0xff){ 39 | fprintf(stderr, "\e[31m[ERROR] "); 40 | 41 | }else{ 42 | fprintf(stderr, "\e[31m[ERROR][%d] ", log_id); 43 | } 44 | fprintf(stderr, format, args...); 45 | fprintf(stderr, "\n"); 46 | return; 47 | } 48 | switch(level){ 49 | case log_level::WARNING: 50 | printf("\e[33m"); 51 | printf("[WARNING]"); 52 | break; 53 | case log_level::NOTICE: 54 | printf("\e[35m"); 55 | printf("[NOTICE]"); 56 | break; 57 | case log_level::INFO: 58 | printf("\e[36m"); 59 | printf("[INFO]"); 60 | break; 61 | case log_level::DEBUG: 62 | printf("\e[32m"); 63 | printf("[DEBUG]"); 64 | break; 65 | case log_level::TRACE: 66 | printf("\e[34m"); 67 | printf("[TRACE]"); 68 | 69 | break; 70 | case log_level::ERROR: 71 | break; 72 | } 73 | if(log_id != 0xff){ 74 | printf("[%d] ", log_id); 75 | } 76 | printf(format, args ...); 77 | printf("\n"); 78 | } 79 | 80 | #endif //MY_AWESOME_BGP_LOGGER_H 81 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "bgp.h" 15 | #include "bgp_client.h" 16 | #include "bgp_rib.h" 17 | #include "command.h" 18 | #include "logger.h" 19 | #include "tree.h" 20 | 21 | #define CONFIG_PATH "../config.json" 22 | #define LOOP_MINIMUM_US 100 // 0.0001秒 23 | 24 | std::vector peers; 25 | 26 | uint32_t my_as; 27 | uint32_t router_id; 28 | uint8_t log_id; 29 | uint8_t console_mode = 0; 30 | node* bgp_loc_rib; 31 | 32 | void signal_handler(int sig){ 33 | } 34 | 35 | int main(){ 36 | std::chrono::system_clock::time_point up = std::chrono::system_clock::now(); 37 | 38 | struct sigaction act, old_act; 39 | 40 | act.sa_handler = signal_handler; 41 | sigemptyset(&act.sa_mask); 42 | act.sa_flags = 0; 43 | act.sa_flags |= SA_INTERRUPT; 44 | 45 | if(sigaction(SIGALRM, &act, &old_act) < 0){ 46 | fprintf(stderr, "Error registering signal disposition\n"); 47 | exit(EXIT_FAILURE); 48 | } 49 | log_id = 0; 50 | log(log_level::INFO, "Hello BGP!!"); 51 | fcntl(0, F_SETFL, O_NONBLOCK); 52 | 53 | std::ifstream conf_file(CONFIG_PATH, std::ios::in); 54 | if(!conf_file.is_open()){ 55 | log(log_level::ERROR, "Failed to open config"); 56 | exit(EXIT_FAILURE); 57 | } 58 | nlohmann::json conf_json; 59 | 60 | try{ 61 | conf_file >> conf_json; 62 | }catch(nlohmann::detail::exception &e){ 63 | log(log_level::ERROR, "Failed to load config"); 64 | exit(EXIT_FAILURE); 65 | } 66 | conf_file.close(); 67 | 68 | log(log_level::INFO, "Succeed to load config"); 69 | 70 | my_as = conf_json.at("my_as").get(); 71 | log(log_level::INFO, "My AS: %d", my_as); 72 | 73 | router_id = inet_addr(conf_json.at("router-id").get().c_str()); 74 | log(log_level::INFO, "Router-ID: %s", inet_ntoa(in_addr{.s_addr = router_id})); 75 | 76 | bgp_loc_rib = (node*) malloc(sizeof(node)); 77 | if(bgp_loc_rib == nullptr){ 78 | log(log_level::ERROR, "Failed to allocate memory for loc_rib root"); 79 | exit(EXIT_FAILURE); 80 | } 81 | bgp_loc_rib->is_prefix = false; 82 | bgp_loc_rib->prefix = 0; 83 | bgp_loc_rib->prefix_len = 0; 84 | bgp_loc_rib->data = nullptr; 85 | bgp_loc_rib->parent = nullptr; 86 | bgp_loc_rib->node_0 = nullptr; 87 | bgp_loc_rib->node_1 = nullptr; 88 | 89 | for(int i=0;i().c_str(), &peer.server_address.sin_addr) == 0){ 99 | log(log_level::ERROR, "Invalid IP address in config"); 100 | exit(EXIT_FAILURE); 101 | } 102 | peer.state = IDLE; 103 | 104 | auto* root = (node*) malloc(sizeof(node)); 105 | if(root == nullptr){ 106 | log(log_level::ERROR, "Failed to allocate memory for adj_ribs_in root"); 107 | exit(EXIT_FAILURE); 108 | } 109 | root->is_prefix = false; 110 | root->parent = nullptr; 111 | root->node_0 = nullptr; 112 | root->node_1 = nullptr; 113 | peer.adj_ribs_in = root; 114 | peer.connect_cool_loop_time = 0; 115 | peers.push_back(peer); // これ、よく見るとブロック抜けたらpeerのメモリ解放されそうだけど、今のところこれで問題が起きたことがないのはなぜだろう 116 | } 117 | 118 | std::chrono::system_clock::time_point start, now; 119 | uint64_t real_time; 120 | bool is_input_continuous = false; // ログモードの時に長いテキストを間違えてペーストしてその中にcやbが含まれていると止まってしまうので、連続で入力された場合は無視するために前回のループで入力があったかを保持する 121 | while(true){ 122 | start = std::chrono::system_clock::now(); 123 | char input = getchar(); // TODO charの範囲外かもしれない 124 | if(console_mode == 0){ 125 | if(input == -1){ 126 | is_input_continuous = false; 127 | }else{ 128 | if(!is_input_continuous){ 129 | if(input == 'q'){ // TODO ログモードからのプログラム終了は廃止したい 130 | log(log_level::INFO, "Good bye"); 131 | break; 132 | }else if(input == 'b'){ 133 | raise(SIGINT); 134 | }else if(input == 'c'){ 135 | console_mode = 1; 136 | for(int i = 0; i < 1000; ++i){ 137 | getchar(); // 連続入力の対策はしているが、もしかすると連続する文字列がcから始まるかもしれない. その場合、対策をすり抜けてしまうのでへんなコマンドが実行されないようにここでバッファをクリアする 138 | } 139 | printf("Switched to command mode\n"); 140 | printf("> "); 141 | } 142 | } 143 | is_input_continuous = true; 144 | } 145 | }else{ 146 | static std::string cmd_str; 147 | if(input != -1){ 148 | if(input >= 0x20 and input <= 0x7e){ 149 | cmd_str.push_back(input); 150 | }else if(input == 0x0a){ 151 | if(cmd_str.empty()){ 152 | }else if(cmd_str == "exit"){ 153 | console_mode = 0; 154 | printf("Switched to log mode\n"); 155 | }else if(cmd_str == "break"){ 156 | raise(SIGINT); 157 | }else if(cmd_str == "uptime"){ 158 | now = std::chrono::system_clock::now(); 159 | console("Uptime %d seconds", std::chrono::duration_cast(now-up).count()); 160 | }else if(cmd_str == "shutdown"){ 161 | console("Good bye"); 162 | break; 163 | }else if(cmd_str == "test"){ 164 | for(int i = 0; i < peers.size(); ++i){ 165 | for(auto &network: conf_json.at("networks")){ 166 | in_addr address = {}; 167 | if(inet_aton(network.at("prefix").get().c_str(), &address) == 0){ 168 | log(log_level::ERROR, "Invalid IP address in config"); 169 | exit(EXIT_FAILURE); 170 | } 171 | attributes a; 172 | a.origin = IGP; 173 | a.as_path_length = 0; 174 | a.next_hop = inet_addr("172.16.3.0"); 175 | a.med = 100; 176 | a.local_pref = 0; 177 | send_update_with_nlri(&peers[i], &a, address.s_addr, network.at("prefix-length")); 178 | } 179 | } 180 | }else{ 181 | command_result_status command_result = execute_command(cmd_str); 182 | if(command_result == command_result_status::NOT_FOUND){ 183 | console("Command not found"); 184 | }else if(command_result == command_result_status::INVALID_PARAMS){ 185 | console("Invalid parameters"); 186 | } 187 | } 188 | cmd_str.clear(); 189 | if(console_mode == 1){ // Exitされていないなら 190 | printf("> "); 191 | } 192 | } 193 | } 194 | } 195 | 196 | for(int i = 0; i < peers.size(); ++i){ 197 | log_id = i; 198 | if(!bgp_client_loop(&peers[i])){ 199 | } 200 | } 201 | log_id = 0xff; 202 | 203 | now = std::chrono::system_clock::now(); 204 | real_time = std::chrono::duration_cast(now-start).count(); 205 | if(LOOP_MINIMUM_US > real_time){ // もしこのループにかかった時間がLOOP_MINIMUM_US未満なら 206 | usleep(LOOP_MINIMUM_US - real_time); // LOOP_MINIMUM_USに満たない時間分ループが終わるのを待つ 207 | } 208 | } 209 | 210 | for(auto & peer : peers){ 211 | close_client_peer(&peer); 212 | } 213 | log(log_level::INFO, "Closed all peers"); 214 | 215 | for(auto & peer : peers){ 216 | free(peer.adj_ribs_in); // root自体はまだ解放されていないのでここで 217 | } 218 | delete_prefix(bgp_loc_rib); 219 | printf("Safety exited\n"); 220 | return EXIT_SUCCESS; 221 | } -------------------------------------------------------------------------------- /tcp_socket.cpp: -------------------------------------------------------------------------------- 1 | #include "tcp_socket.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | void set_nonblocking(int sockfd){ 8 | int val; 9 | val = fcntl(sockfd, F_GETFL, 0); 10 | fcntl(sockfd, F_SETFL, val | O_NONBLOCK); 11 | } 12 | 13 | // https://stackoverflow.com/questions/2597608/c-socket-connection-timeout 14 | 15 | int connect_with_timeout(int sockfd, const struct sockaddr* addr, socklen_t addrlen, unsigned int timeout_ms){ 16 | int rc = 0; 17 | // Set O_NONBLOCK 18 | int sockfd_flags_before; 19 | if((sockfd_flags_before = fcntl(sockfd, F_GETFL, 0) < 0)) return -1; 20 | if(fcntl(sockfd, F_SETFL, sockfd_flags_before | O_NONBLOCK) < 0) return -1; 21 | // Start connecting (asynchronously) 22 | do{ 23 | if(connect(sockfd, addr, addrlen) < 0){ 24 | // Did connect return an error? If so, we'll fail. 25 | if((errno != EWOULDBLOCK) && (errno != EINPROGRESS)){ 26 | rc = -1; 27 | } 28 | // Otherwise, we'll wait for it to complete. 29 | else{ 30 | // Set a deadline timestamp 'timeout' ms from now (needed b/c poll can be interrupted) 31 | struct timespec now; 32 | if(clock_gettime(CLOCK_MONOTONIC, &now) < 0){ 33 | rc = -1; 34 | break; 35 | } 36 | struct timespec deadline = {.tv_sec = now.tv_sec, 37 | .tv_nsec = now.tv_nsec + timeout_ms * 1000000l}; 38 | // Wait for the connection to complete. 39 | do{ 40 | // Calculate how long until the deadline 41 | if(clock_gettime(CLOCK_MONOTONIC, &now) < 0){ 42 | rc = -1; 43 | break; 44 | } 45 | int ms_until_deadline = (int) ((deadline.tv_sec - now.tv_sec) * 1000l 46 | + (deadline.tv_nsec - now.tv_nsec) / 1000000l); 47 | if(ms_until_deadline < 0){ 48 | rc = 0; 49 | break; 50 | } 51 | // Wait for connect to complete (or for the timeout deadline) 52 | struct pollfd pfds[] = {{.fd = sockfd, .events = POLLOUT}}; 53 | rc = poll(pfds, 1, ms_until_deadline); 54 | // If poll 'succeeded', make sure it *really* succeeded 55 | if(rc > 0){ 56 | int error = 0; 57 | socklen_t len = sizeof(error); 58 | int retval = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len); 59 | if(retval == 0) errno = error; 60 | if(error != 0) rc = -1; 61 | } 62 | } 63 | // If poll was interrupted, try again. 64 | while(rc == -1 && errno == EINTR); 65 | // Did poll timeout? If so, fail. 66 | if(rc == 0){ 67 | errno = ETIMEDOUT; 68 | rc = -1; 69 | } 70 | } 71 | } 72 | }while(0); 73 | // Restore original O_NONBLOCK state 74 | if(fcntl(sockfd, F_SETFL, sockfd_flags_before) < 0) return -1; 75 | // Success 76 | return rc; 77 | } 78 | -------------------------------------------------------------------------------- /tcp_socket.h: -------------------------------------------------------------------------------- 1 | #ifndef MY_AWESOME_BGP_TCP_SOCKET_H 2 | #define MY_AWESOME_BGP_TCP_SOCKET_H 3 | 4 | #include 5 | 6 | void set_nonblocking(int sockfd); 7 | 8 | int connect_with_timeout(int sockfd, const struct sockaddr *addr, socklen_t addrlen, unsigned int timeout_ms); 9 | 10 | #endif //MY_AWESOME_BGP_TCP_SOCKET_H 11 | -------------------------------------------------------------------------------- /tree.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "tree.h" 5 | 6 | bool check_bit(uint32_t addr, uint8_t n){ 7 | return (addr >> (31 - n)) & 0b01; 8 | } 9 | 10 | void print_address_binary(uint32_t addr){ 11 | for(int i = 31; i >= 0; --i){ 12 | if(check_bit(addr, i)){ 13 | printf("1"); 14 | }else{ 15 | printf("0"); 16 | } 17 | } 18 | printf("\n"); 19 | } -------------------------------------------------------------------------------- /tree.h: -------------------------------------------------------------------------------- 1 | #ifndef TEST_RIB_TREE_TREE_H 2 | #define TEST_RIB_TREE_TREE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | template 11 | struct node{ 12 | bool is_prefix = false; 13 | uint32_t prefix = 0; 14 | uint8_t prefix_len = 0; 15 | DATA_TYPE* data = nullptr; 16 | node* parent = nullptr; 17 | node* node_0 = nullptr; 18 | node* node_1 = nullptr; 19 | }; 20 | 21 | bool check_bit(uint32_t addr, uint8_t n); 22 | void print_address_binary(uint32_t addr); 23 | 24 | template 25 | void assert_tree(node* node){ 26 | if(node->node_0 != nullptr){ 27 | if(node->node_0->prefix_len != node->prefix_len + 1){ 28 | printf("Invalid prefix length %d and %d\n", node->node_0->prefix_len, node->prefix_len); 29 | } 30 | assert_tree(node->node_0); 31 | } 32 | if(node->node_1 != nullptr){ 33 | if(node->node_1->prefix_len != node->prefix_len + 1){ 34 | printf("Invalid prefix length %d and %d\n", node->node_1->prefix_len, node->prefix_len); 35 | } 36 | assert_tree(node->node_1); 37 | } 38 | } 39 | 40 | /** 41 | * 42 | * @param prefix 削除するノード 43 | * @param is_delete_child_prefix Trueの場合は子要素をすべて削除する 44 | */ 45 | template 46 | void delete_prefix(node* prefix, bool is_delete_child_prefix = false){ 47 | prefix->is_prefix = false; // 削除対象のプレフィックスはプレフィックス扱いしない 48 | if(prefix->data == nullptr){ 49 | free(prefix->data); 50 | prefix->data = nullptr; 51 | } 52 | if(!is_delete_child_prefix and (prefix->node_1 != nullptr or prefix->node_0 != nullptr)){ 53 | return; 54 | } 55 | if(is_delete_child_prefix and prefix->node_1 != nullptr and prefix->node_0 == nullptr){ 56 | return delete_prefix(prefix->node_1, true); 57 | } 58 | if(is_delete_child_prefix and prefix->node_0 != nullptr and prefix->node_1 == nullptr){ 59 | return delete_prefix(prefix->node_0, true); 60 | } 61 | node* tmp1; 62 | if(is_delete_child_prefix and prefix->node_1 != nullptr and prefix->node_0 != nullptr){ 63 | tmp1 = prefix->node_0; 64 | tmp1->parent = nullptr; 65 | prefix->node_0 = nullptr; 66 | delete_prefix(prefix, true); 67 | return delete_prefix(tmp1, true); 68 | } 69 | node* tmp2; 70 | node* current = prefix; 71 | while(!current->is_prefix and current->parent != nullptr and (current->parent->node_0 == nullptr or current->parent->node_1 == nullptr)){ 72 | #ifdef TEST_RIB_TREE_TEST_TREE 73 | printf("Release: %s/%d, %p\n", inet_ntoa(in_addr{.s_addr = htonl(current->prefix)}), current->prefix_len, current); 74 | #endif 75 | tmp2 = current->parent; 76 | if(current->parent->node_1 == current){ 77 | current->parent->node_1 = nullptr; 78 | }else{ 79 | current->parent->node_0 = nullptr; 80 | } 81 | free(current); 82 | current = tmp2; 83 | } 84 | } 85 | 86 | /** 87 | * 88 | * @param root 検索を行う木構造の根 89 | * @param address 検索するアドレス 90 | * @param max_prefix_len 検索するノードの最大プレフィックス長 91 | * @param is_prefix_strict Trueの場合はmax_prefix_lenのプレフィックス長のノードが見つからなければnullptrを返す 92 | * @return  93 | */ 94 | template 95 | node* search_prefix(node* root, uint32_t address, uint8_t max_prefix_len = 32, bool is_prefix_strict = false){ 96 | node* current = root; 97 | node* next = nullptr; 98 | node* match_node = root; 99 | uint8_t i = 0; 100 | while(i < max_prefix_len){ 101 | next = (check_bit(address, i) ? current->node_1 : current->node_0); 102 | if(next == nullptr){ 103 | if(is_prefix_strict){ 104 | return nullptr; 105 | } 106 | return match_node; 107 | } 108 | if(next->is_prefix){ 109 | match_node = next; 110 | } 111 | current = next; 112 | i++; 113 | } 114 | if(is_prefix_strict and max_prefix_len != 0 and match_node->prefix_len == 0){ 115 | return nullptr; 116 | } 117 | return match_node; 118 | } 119 | 120 | /** 121 | * 122 | * @param root 追加を行う木構造の根 123 | * @param prefix 追加するアドレスプレフィックス 124 | * @param prefix_len 追加するアドレスプレフィックスのプレフィックス長 125 | * @param data 経路につける情報 126 | * @param is_updated 呼び出し元に経路の更新だったのか追加だったのかを知らせる 127 | * @return 128 | */ 129 | template 130 | node* add_prefix(node* root, uint32_t prefix, uint8_t prefix_len, DATA_TYPE data, bool* is_updated = nullptr){ 131 | node* current = search_prefix(root, prefix, prefix_len-1); 132 | uint8_t current_prefix_len = current->prefix_len; 133 | #ifdef TEST_RIB_TREE_TEST_TREE 134 | printf("Get prefix: %d\n", current_prefix_len); 135 | #endif 136 | node** growth_address_ptr; 137 | while(current_prefix_len < prefix_len - 1){ // 枝を伸ばす 138 | if(check_bit(prefix, current_prefix_len)){ 139 | growth_address_ptr = ¤t->node_1; 140 | }else{ 141 | growth_address_ptr = ¤t->node_0; 142 | } 143 | if((*growth_address_ptr) != nullptr){ 144 | current = *growth_address_ptr; 145 | }else{ 146 | auto* growth_node = (node*) malloc(sizeof(node)); 147 | if(growth_node == nullptr){ 148 | printf("Failed to allocate memory\n"); 149 | exit(EXIT_FAILURE); 150 | } 151 | growth_node->is_prefix = false; 152 | growth_node->prefix = current->prefix; 153 | growth_node->prefix_len = current_prefix_len+1; 154 | growth_node->parent = current; 155 | growth_node->node_0 = nullptr; 156 | growth_node->node_1 = nullptr; 157 | if(check_bit(prefix, current_prefix_len)){ 158 | growth_node->prefix |= (0b01 << (32 - (current_prefix_len + 1))); 159 | current->node_1 = growth_node; 160 | }else{ 161 | current->node_0 = growth_node; 162 | } 163 | #ifdef TEST_RIB_TREE_TEST_TREE 164 | printf("Create: %s/%d, %p\n", inet_ntoa(in_addr{.s_addr = htonl(growth_node->prefix)}), current_prefix_len+1, growth_node); 165 | #endif 166 | current = growth_node; 167 | } 168 | current_prefix_len++; 169 | } 170 | 171 | if(check_bit(prefix, prefix_len - 1)){ 172 | growth_address_ptr = ¤t->node_1; 173 | }else{ 174 | growth_address_ptr = ¤t->node_0; 175 | } 176 | if((*growth_address_ptr) == nullptr){ 177 | auto* new_prefix = (node*) malloc(sizeof(node)); 178 | if(new_prefix == nullptr){ 179 | printf("Failed to allocate memory\n"); 180 | exit(EXIT_FAILURE); 181 | } 182 | new_prefix->is_prefix = true; 183 | new_prefix->prefix = current->prefix; 184 | if(check_bit(prefix, prefix_len-1)){ 185 | new_prefix->prefix |= (0b01 << (32 - prefix_len)); 186 | } 187 | #ifdef TEST_RIB_TREE_TEST_TREE 188 | printf("Create Prefix: %s/%d, %p\n", inet_ntoa(in_addr{.s_addr = htonl(new_prefix->prefix)}), prefix_len, new_prefix); 189 | #endif 190 | new_prefix->prefix_len = prefix_len; 191 | new_prefix->data = (DATA_TYPE*) malloc(sizeof(DATA_TYPE)); 192 | if(new_prefix->data == nullptr){ 193 | printf("Failed to allocate memory\n"); 194 | exit(EXIT_FAILURE); 195 | } 196 | memcpy(new_prefix->data, &data, sizeof(DATA_TYPE)); 197 | new_prefix->parent = current; 198 | new_prefix->node_0 = nullptr; 199 | new_prefix->node_1 = nullptr; 200 | *growth_address_ptr = new_prefix; 201 | if(is_updated != nullptr){ 202 | *is_updated = false; 203 | } 204 | }else{ 205 | #ifdef TEST_RIB_TREE_TEST_TREE 206 | printf("Exist: %s/%d, %p\n", inet_ntoa(in_addr{.s_addr = htonl(prefix)}), prefix_len, (*growth_address_ptr)); 207 | #endif 208 | if((*growth_address_ptr)->is_prefix){ 209 | if(is_updated != nullptr){ 210 | *is_updated = true; // 書き込み先のノードがもともとプレフィックスだったら、それは経路の更新である 211 | } 212 | }else{ 213 | (*growth_address_ptr)->is_prefix = true; 214 | if(is_updated != nullptr){ 215 | *is_updated = false; // 書き込み先のノードがもともとプレフィックスでなかったら、それは経路の追加である 216 | } 217 | } 218 | if((*growth_address_ptr)->data == nullptr){ 219 | (*growth_address_ptr)->data = (DATA_TYPE*) malloc(sizeof(DATA_TYPE)); 220 | if((*growth_address_ptr)->data == nullptr){ 221 | printf("Failed to allocate memory\n"); 222 | exit(EXIT_FAILURE); 223 | } 224 | } 225 | memcpy((*growth_address_ptr)->data, &data, sizeof(DATA_TYPE)); 226 | } 227 | 228 | return *growth_address_ptr; 229 | } 230 | 231 | #endif //TEST_RIB_TREE_TREE_H 232 | --------------------------------------------------------------------------------