Taoyu Li | aa6238b | 2019-09-06 17:38:52 +0900 | [diff] [blame] | 1 | // Copyright 2019 The Chromium OS Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
Garrick Evans | 3388a03 | 2020-03-24 11:25:55 +0900 | [diff] [blame] | 5 | #ifndef PATCHPANEL_NDPROXY_H_ |
| 6 | #define PATCHPANEL_NDPROXY_H_ |
Taoyu Li | aa6238b | 2019-09-06 17:38:52 +0900 | [diff] [blame] | 7 | |
| 8 | #include <stdint.h> |
| 9 | |
Taoyu Li | 18041fe | 2019-11-13 15:49:31 +0900 | [diff] [blame] | 10 | #include <net/ethernet.h> |
Taoyu Li | aa6238b | 2019-09-06 17:38:52 +0900 | [diff] [blame] | 11 | #include <netinet/icmp6.h> |
| 12 | #include <netinet/ip.h> |
| 13 | #include <netinet/ip6.h> |
| 14 | |
| 15 | #include <map> |
Taoyu Li | f0370c9 | 2019-09-18 15:04:37 +0900 | [diff] [blame] | 16 | #include <memory> |
Taoyu Li | aa6238b | 2019-09-06 17:38:52 +0900 | [diff] [blame] | 17 | #include <set> |
| 18 | #include <string> |
Taoyu Li | a0727dc | 2020-09-24 19:54:59 +0900 | [diff] [blame] | 19 | #include <vector> |
Taoyu Li | aa6238b | 2019-09-06 17:38:52 +0900 | [diff] [blame] | 20 | |
| 21 | #include <base/files/scoped_file.h> |
| 22 | #include <base/macros.h> |
Taoyu Li | f0370c9 | 2019-09-18 15:04:37 +0900 | [diff] [blame] | 23 | #include <brillo/daemons/daemon.h> |
Taoyu Li | e47df4a | 2019-11-08 12:48:57 +0900 | [diff] [blame] | 24 | #include <gtest/gtest_prod.h> // for FRIEND_TEST |
Taoyu Li | f0370c9 | 2019-09-18 15:04:37 +0900 | [diff] [blame] | 25 | |
Garrick Evans | 3388a03 | 2020-03-24 11:25:55 +0900 | [diff] [blame] | 26 | #include "patchpanel/mac_address_generator.h" |
| 27 | #include "patchpanel/message_dispatcher.h" |
Taoyu Li | aa6238b | 2019-09-06 17:38:52 +0900 | [diff] [blame] | 28 | |
Garrick Evans | 3388a03 | 2020-03-24 11:25:55 +0900 | [diff] [blame] | 29 | namespace patchpanel { |
Taoyu Li | aa6238b | 2019-09-06 17:38:52 +0900 | [diff] [blame] | 30 | |
| 31 | // Forward ICMPv6 RS/RA/NS/NA mssages between network interfaces according to |
| 32 | // RFC 4389. Support asymmetric proxy that RS will be proxied one-way from |
| 33 | // guest interface to physical interface ('Outbound') and RA the other way back |
| 34 | // ('Inbound'), as well as symmetric proxy among guest interfaces that only |
Taoyu Li | f0370c9 | 2019-09-18 15:04:37 +0900 | [diff] [blame] | 35 | // NS/NA will be proxied. |
Taoyu Li | e47df4a | 2019-11-08 12:48:57 +0900 | [diff] [blame] | 36 | class NDProxy { |
Taoyu Li | aa6238b | 2019-09-06 17:38:52 +0900 | [diff] [blame] | 37 | public: |
Taoyu Li | e47df4a | 2019-11-08 12:48:57 +0900 | [diff] [blame] | 38 | static constexpr ssize_t kTranslateErrorNotICMPv6Frame = -1; |
| 39 | static constexpr ssize_t kTranslateErrorNotNDFrame = -2; |
| 40 | static constexpr ssize_t kTranslateErrorInsufficientLength = -3; |
| 41 | static constexpr ssize_t kTranslateErrorBufferMisaligned = -4; |
Taoyu Li | aa6238b | 2019-09-06 17:38:52 +0900 | [diff] [blame] | 42 | |
Taoyu Li | e47df4a | 2019-11-08 12:48:57 +0900 | [diff] [blame] | 43 | NDProxy(); |
Qijiang Fan | 6bc59e1 | 2020-11-11 02:51:06 +0900 | [diff] [blame^] | 44 | NDProxy(const NDProxy&) = delete; |
| 45 | NDProxy& operator=(const NDProxy&) = delete; |
| 46 | |
Taoyu Li | e47df4a | 2019-11-08 12:48:57 +0900 | [diff] [blame] | 47 | virtual ~NDProxy() = default; |
| 48 | |
| 49 | ssize_t TranslateNDFrame(const uint8_t* in_frame, |
| 50 | ssize_t frame_len, |
| 51 | const MacAddress& local_mac_addr, |
| 52 | uint8_t* out_frame); |
Taoyu Li | 18041fe | 2019-11-13 15:49:31 +0900 | [diff] [blame] | 53 | |
Taoyu Li | a0727dc | 2020-09-24 19:54:59 +0900 | [diff] [blame] | 54 | static const nd_opt_prefix_info* GetPrefixInfoOption(const uint8_t* in_frame, |
| 55 | ssize_t frame_len); |
| 56 | |
Taoyu Li | 18041fe | 2019-11-13 15:49:31 +0900 | [diff] [blame] | 57 | // Given an extended |buffer|, find a proper frame buffer pointer so that |
| 58 | // pt > buffer, and start of IP header (pt + ETH_H_LEN) is 4-bytes aligned. |
| 59 | // In the worst case the size of usable buffer will be original size minus 3. |
| 60 | // 4x => 4x+2; 4x+1 => 4x+2; 4x+2 => 4x+2; 4x+3 => 4x+6 |
| 61 | static const uint8_t* AlignFrameBuffer(const uint8_t* buffer) { |
| 62 | return buffer + 3 - (reinterpret_cast<uintptr_t>(buffer + 1) & 0x3); |
| 63 | } |
| 64 | |
| 65 | static uint8_t* AlignFrameBuffer(uint8_t* buffer) { |
| 66 | return buffer + 3 - (reinterpret_cast<uintptr_t>(buffer + 1) & 0x3); |
| 67 | } |
Taoyu Li | aa6238b | 2019-09-06 17:38:52 +0900 | [diff] [blame] | 68 | |
Taoyu Li | ba04fe3 | 2020-01-14 13:19:35 +0900 | [diff] [blame] | 69 | // Helper function to create a AF_PACKET socket suitable for frame read/write. |
| 70 | static base::ScopedFD PreparePacketSocket(); |
| 71 | |
Taoyu Li | e47df4a | 2019-11-08 12:48:57 +0900 | [diff] [blame] | 72 | // Initialize the resources needed such as rtnl socket and dummy socket for |
| 73 | // ioctl. Return false if failed. |
| 74 | bool Init(); |
| 75 | |
| 76 | // Read one frame from AF_PACKET socket |fd| and process it. If proxying is |
| 77 | // needed, translated frame is sent out through the same socket. |
| 78 | void ReadAndProcessOneFrame(int fd); |
| 79 | |
| 80 | // NDProxy can trigger a callback upon receiving NA frame with unicast IPv6 |
| 81 | // address from guest OS interface. |
| 82 | void RegisterOnGuestIpDiscoveryHandler( |
| 83 | const base::Callback<void(const std::string&, const std::string&)>& |
| 84 | handler); |
| 85 | |
Taoyu Li | a0727dc | 2020-09-24 19:54:59 +0900 | [diff] [blame] | 86 | // Callback upon receiving prefix information from RA frame. |
| 87 | void RegisterOnRouterDiscoveryHandler( |
| 88 | const base::Callback<void(const std::string&, const std::string&)>& |
| 89 | handler); |
| 90 | |
Taoyu Li | aa6238b | 2019-09-06 17:38:52 +0900 | [diff] [blame] | 91 | // To proxy between upstream interface and guest OS interface (eth0-arc_eth0) |
| 92 | // Outbound RS, inbound RA, and bidirectional NS/NA will be proxied. |
Taoyu Li | 7dca19a | 2020-03-16 16:27:07 +0900 | [diff] [blame] | 93 | bool AddInterfacePair(const std::string& ifname_physical, |
| 94 | const std::string& ifname_guest); |
Taoyu Li | aa6238b | 2019-09-06 17:38:52 +0900 | [diff] [blame] | 95 | |
Taoyu Li | 7dca19a | 2020-03-16 16:27:07 +0900 | [diff] [blame] | 96 | // Remove a proxy interface pair. |
| 97 | bool RemoveInterfacePair(const std::string& ifname_physical, |
| 98 | const std::string& ifname_guest); |
Taoyu Li | aa6238b | 2019-09-06 17:38:52 +0900 | [diff] [blame] | 99 | |
| 100 | // Remove all proxy interface pair with ifindex. |
| 101 | bool RemoveInterface(const std::string& ifname); |
| 102 | |
Taoyu Li | a0727dc | 2020-09-24 19:54:59 +0900 | [diff] [blame] | 103 | // Utility to get a list of guest interfaces names that are currently being |
| 104 | // proxied with a specific physical interface. |
| 105 | std::vector<std::string> GetGuestInterfaces( |
| 106 | const std::string& ifname_physical); |
| 107 | |
Taoyu Li | aa6238b | 2019-09-06 17:38:52 +0900 | [diff] [blame] | 108 | private: |
| 109 | // Data structure to store interface mapping for a certain kind of packet to |
| 110 | // be proxied. For example, {1: {2}, 2: {1}} means that packet from interfaces |
| 111 | // 1 and 2 will be proxied to each other. |
| 112 | using interface_mapping = std::map<int, std::set<int>>; |
| 113 | |
Taoyu Li | e47df4a | 2019-11-08 12:48:57 +0900 | [diff] [blame] | 114 | static void ReplaceMacInIcmpOption(uint8_t* frame, |
| 115 | ssize_t frame_len, |
| 116 | size_t nd_hdr_len, |
| 117 | uint8_t opt_type, |
| 118 | const MacAddress& target_mac); |
| 119 | |
| 120 | // Get MAC address on a local interface through ioctl(). |
| 121 | // Returns false upon failure. |
| 122 | virtual bool GetLocalMac(int if_id, MacAddress* mac_addr); |
| 123 | |
| 124 | // Query kernel NDP table and get the MAC address of a certain IPv6 neighbor. |
| 125 | // Returns false when neighbor entry is not found. |
| 126 | virtual bool GetNeighborMac(const in6_addr& ipv6_addr, MacAddress* mac_addr); |
Taoyu Li | f0370c9 | 2019-09-18 15:04:37 +0900 | [diff] [blame] | 127 | |
Taoyu Li | aa6238b | 2019-09-06 17:38:52 +0900 | [diff] [blame] | 128 | interface_mapping* MapForType(uint8_t type); |
Taoyu Li | af944c9 | 2019-10-01 12:22:31 +0900 | [diff] [blame] | 129 | bool IsGuestInterface(int ifindex); |
Taoyu Li | a0727dc | 2020-09-24 19:54:59 +0900 | [diff] [blame] | 130 | bool IsRouterInterface(int ifindex); |
Taoyu Li | aa6238b | 2019-09-06 17:38:52 +0900 | [diff] [blame] | 131 | |
Taoyu Li | e47df4a | 2019-11-08 12:48:57 +0900 | [diff] [blame] | 132 | // Socket used to communicate with kernel through ioctl. No real packet data |
| 133 | // goes through this socket. |
| 134 | base::ScopedFD dummy_fd_; |
| 135 | base::ScopedFD rtnl_fd_; |
Taoyu Li | 43f6688 | 2019-10-25 16:59:34 +0900 | [diff] [blame] | 136 | |
Taoyu Li | 18041fe | 2019-11-13 15:49:31 +0900 | [diff] [blame] | 137 | // Allocate slightly more space and adjust the buffer start location to |
| 138 | // make sure IP header is 4-bytes aligned. |
| 139 | uint8_t in_frame_buffer_extended_[IP_MAXPACKET + ETH_HLEN + 4]; |
| 140 | uint8_t out_frame_buffer_extended_[IP_MAXPACKET + ETH_HLEN + 4]; |
| 141 | uint8_t* in_frame_buffer_; |
| 142 | uint8_t* out_frame_buffer_; |
Taoyu Li | aa6238b | 2019-09-06 17:38:52 +0900 | [diff] [blame] | 143 | |
| 144 | interface_mapping if_map_rs_; |
| 145 | interface_mapping if_map_ra_; |
| 146 | interface_mapping if_map_ns_na_; |
| 147 | |
Taoyu Li | e47df4a | 2019-11-08 12:48:57 +0900 | [diff] [blame] | 148 | base::Callback<void(const std::string&, const std::string&)> |
| 149 | guest_discovery_handler_; |
Taoyu Li | a0727dc | 2020-09-24 19:54:59 +0900 | [diff] [blame] | 150 | base::Callback<void(const std::string&, const std::string&)> |
| 151 | router_discovery_handler_; |
Taoyu Li | e47df4a | 2019-11-08 12:48:57 +0900 | [diff] [blame] | 152 | |
Taoyu Li | f0370c9 | 2019-09-18 15:04:37 +0900 | [diff] [blame] | 153 | base::WeakPtrFactory<NDProxy> weak_factory_{this}; |
| 154 | |
Taoyu Li | e47df4a | 2019-11-08 12:48:57 +0900 | [diff] [blame] | 155 | FRIEND_TEST(NDProxyTest, TranslateFrame); |
Taoyu Li | aa6238b | 2019-09-06 17:38:52 +0900 | [diff] [blame] | 156 | }; |
| 157 | |
Taoyu Li | e47df4a | 2019-11-08 12:48:57 +0900 | [diff] [blame] | 158 | // A wrapper class for running NDProxy in a daemon process. Control messages and |
| 159 | // guest IP discovery messages are passed through |control_fd|. |
| 160 | class NDProxyDaemon : public brillo::Daemon { |
| 161 | public: |
| 162 | explicit NDProxyDaemon(base::ScopedFD control_fd); |
Qijiang Fan | 6bc59e1 | 2020-11-11 02:51:06 +0900 | [diff] [blame^] | 163 | NDProxyDaemon(const NDProxyDaemon&) = delete; |
| 164 | NDProxyDaemon& operator=(const NDProxyDaemon&) = delete; |
| 165 | |
Taoyu Li | e47df4a | 2019-11-08 12:48:57 +0900 | [diff] [blame] | 166 | virtual ~NDProxyDaemon(); |
| 167 | |
| 168 | private: |
| 169 | // Overrides Daemon init callback. Returns 0 on success and < 0 on error. |
| 170 | int OnInit() override; |
| 171 | // FileDescriptorWatcher callbacks for new data on fd_. |
| 172 | void OnDataSocketReadReady(); |
| 173 | // Callbacks to be registered to msg_dispatcher to handle control messages. |
| 174 | void OnParentProcessExit(); |
| 175 | void OnDeviceMessage(const DeviceMessage& msg); |
| 176 | |
| 177 | // Callback from NDProxy core when receive NA from guest |
| 178 | void OnGuestIpDiscovery(const std::string& ifname, |
| 179 | const std::string& ip6addr); |
| 180 | |
Taoyu Li | a0727dc | 2020-09-24 19:54:59 +0900 | [diff] [blame] | 181 | // Callback from NDProxy core when receive prefix info from router |
| 182 | void OnRouterDiscovery(const std::string& ifname, const std::string& ip6addr); |
| 183 | |
| 184 | void SendMessage(NDProxyMessage::NDProxyEventType type, |
| 185 | const std::string& ifname, |
| 186 | const std::string& ip6addr); |
| 187 | |
| 188 | // Map from guest-facing ifname to eui address we assigned |
| 189 | std::map<std::string, std::string> guest_if_addrs_; |
| 190 | |
Taoyu Li | e47df4a | 2019-11-08 12:48:57 +0900 | [diff] [blame] | 191 | // Utilize MessageDispatcher to watch control fd |
| 192 | std::unique_ptr<MessageDispatcher> msg_dispatcher_; |
| 193 | |
| 194 | // Data fd and its watcher |
| 195 | base::ScopedFD fd_; |
| 196 | std::unique_ptr<base::FileDescriptorWatcher::Controller> watcher_; |
| 197 | |
| 198 | NDProxy proxy_; |
| 199 | |
| 200 | base::WeakPtrFactory<NDProxyDaemon> weak_factory_{this}; |
Taoyu Li | e47df4a | 2019-11-08 12:48:57 +0900 | [diff] [blame] | 201 | }; |
| 202 | |
Garrick Evans | 3388a03 | 2020-03-24 11:25:55 +0900 | [diff] [blame] | 203 | } // namespace patchpanel |
Taoyu Li | aa6238b | 2019-09-06 17:38:52 +0900 | [diff] [blame] | 204 | |
Garrick Evans | 3388a03 | 2020-03-24 11:25:55 +0900 | [diff] [blame] | 205 | #endif // PATCHPANEL_NDPROXY_H_ |