| 69 | static constexpr ssize_t NETLINK_MAX_RESPONSE_SIZE{1'048'576}; |
| 70 | |
| 71 | std::optional<CNetAddr> QueryDefaultGatewayImpl(sa_family_t family) |
| 72 | { |
| 73 | // Create a netlink socket. |
| 74 | auto sock{CreateSock(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)}; |
| 75 | if (!sock) { |
| 76 | LogError("socket(AF_NETLINK): %s\n", NetworkErrorString(errno)); |
| 77 | return std::nullopt; |
| 78 | } |
| 79 | |
| 80 | // Send request. |
| 81 | struct { |
| 82 | nlmsghdr hdr; ///< Request header. |
| 83 | rtmsg data; ///< Request data, a "route message". |
| 84 | nlattr dst_hdr; ///< One attribute, conveying the route destination address. |
| 85 | char dst_data[16]; ///< Route destination address. To query the default route we use 0.0.0.0/0 or [::]/0. For IPv4 the first 4 bytes are used. |
| 86 | } request{}; |
| 87 | |
| 88 | // Whether to use the first 4 or 16 bytes from request.dst_data. |
| 89 | const size_t dst_data_len = family == AF_INET ? 4 : 16; |
| 90 | |
| 91 | request.hdr.nlmsg_type = RTM_GETROUTE; |
| 92 | request.hdr.nlmsg_flags = NLM_F_REQUEST; |
| 93 | #ifdef __linux__ |
| 94 | // Linux IPv4 / IPv6 - this must be present, otherwise no gateway is found |
| 95 | // FreeBSD IPv4 - does not matter, the gateway is found with or without this |
| 96 | // FreeBSD IPv6 - this must be absent, otherwise no gateway is found |
| 97 | request.hdr.nlmsg_flags |= NLM_F_DUMP; |
| 98 | #endif |
| 99 | request.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(rtmsg) + sizeof(nlattr) + dst_data_len); |
| 100 | request.hdr.nlmsg_seq = 0; // Sequence number, used to match which reply is to which request. Irrelevant for us because we send just one request. |
| 101 | request.data.rtm_family = family; |
| 102 | request.data.rtm_dst_len = 0; // Prefix length. |
| 103 | #ifdef __FreeBSD__ |
| 104 | // Linux IPv4 / IPv6 this must be absent, otherwise no gateway is found |
| 105 | // FreeBSD IPv4 - does not matter, the gateway is found with or without this |
| 106 | // FreeBSD IPv6 - this must be present, otherwise no gateway is found |
| 107 | request.data.rtm_flags = RTM_F_PREFIX; |
| 108 | #endif |
| 109 | request.dst_hdr.nla_type = RTA_DST; |
| 110 | request.dst_hdr.nla_len = sizeof(nlattr) + dst_data_len; |
| 111 | |
| 112 | if (sock->Send(&request, request.hdr.nlmsg_len, 0) != static_cast<ssize_t>(request.hdr.nlmsg_len)) { |
| 113 | LogError("send() to netlink socket: %s\n", NetworkErrorString(errno)); |
| 114 | return std::nullopt; |
| 115 | } |
| 116 | |
| 117 | // Receive response. |
| 118 | char response[4096]; |
| 119 | ssize_t total_bytes_read{0}; |
| 120 | bool done{false}; |
| 121 | while (!done) { |
| 122 | int64_t recv_result; |
| 123 | do { |
| 124 | recv_result = sock->Recv(response, sizeof(response), 0); |
| 125 | } while (recv_result < 0 && (errno == EINTR || errno == EAGAIN)); |
| 126 | if (recv_result < 0) { |
| 127 | LogError("recv() from netlink socket: %s\n", NetworkErrorString(errno)); |
| 128 | return std::nullopt; |
no test coverage detected