blob: a2d608b4dba12e16ab583ea2e71fa30358b87960 [file] [log] [blame]
Taoyu Liaa6238b2019-09-06 17:38:52 +09001// 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 Evans3388a032020-03-24 11:25:55 +09005#ifndef PATCHPANEL_NDPROXY_H_
6#define PATCHPANEL_NDPROXY_H_
Taoyu Liaa6238b2019-09-06 17:38:52 +09007
8#include <stdint.h>
9
Taoyu Li18041fe2019-11-13 15:49:31 +090010#include <net/ethernet.h>
Taoyu Liaa6238b2019-09-06 17:38:52 +090011#include <netinet/icmp6.h>
12#include <netinet/ip.h>
13#include <netinet/ip6.h>
14
15#include <map>
Taoyu Lif0370c92019-09-18 15:04:37 +090016#include <memory>
Taoyu Liaa6238b2019-09-06 17:38:52 +090017#include <set>
18#include <string>
Taoyu Lia0727dc2020-09-24 19:54:59 +090019#include <vector>
Taoyu Liaa6238b2019-09-06 17:38:52 +090020
21#include <base/files/scoped_file.h>
22#include <base/macros.h>
Taoyu Lif0370c92019-09-18 15:04:37 +090023#include <brillo/daemons/daemon.h>
Taoyu Lie47df4a2019-11-08 12:48:57 +090024#include <gtest/gtest_prod.h> // for FRIEND_TEST
Taoyu Lif0370c92019-09-18 15:04:37 +090025
Garrick Evans3388a032020-03-24 11:25:55 +090026#include "patchpanel/mac_address_generator.h"
27#include "patchpanel/message_dispatcher.h"
Taoyu Liaa6238b2019-09-06 17:38:52 +090028
Garrick Evans3388a032020-03-24 11:25:55 +090029namespace patchpanel {
Taoyu Liaa6238b2019-09-06 17:38:52 +090030
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 Lif0370c92019-09-18 15:04:37 +090035// NS/NA will be proxied.
Taoyu Lie47df4a2019-11-08 12:48:57 +090036class NDProxy {
Taoyu Liaa6238b2019-09-06 17:38:52 +090037 public:
Taoyu Lie47df4a2019-11-08 12:48:57 +090038 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 Liaa6238b2019-09-06 17:38:52 +090042
Taoyu Lie47df4a2019-11-08 12:48:57 +090043 NDProxy();
Qijiang Fan6bc59e12020-11-11 02:51:06 +090044 NDProxy(const NDProxy&) = delete;
45 NDProxy& operator=(const NDProxy&) = delete;
46
Taoyu Lie47df4a2019-11-08 12:48:57 +090047 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 Li18041fe2019-11-13 15:49:31 +090053
Taoyu Lia0727dc2020-09-24 19:54:59 +090054 static const nd_opt_prefix_info* GetPrefixInfoOption(const uint8_t* in_frame,
55 ssize_t frame_len);
56
Taoyu Li18041fe2019-11-13 15:49:31 +090057 // 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 Liaa6238b2019-09-06 17:38:52 +090068
Taoyu Liba04fe32020-01-14 13:19:35 +090069 // Helper function to create a AF_PACKET socket suitable for frame read/write.
70 static base::ScopedFD PreparePacketSocket();
71
Taoyu Lie47df4a2019-11-08 12:48:57 +090072 // 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 Lia0727dc2020-09-24 19:54:59 +090086 // 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 Liaa6238b2019-09-06 17:38:52 +090091 // 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 Li7dca19a2020-03-16 16:27:07 +090093 bool AddInterfacePair(const std::string& ifname_physical,
94 const std::string& ifname_guest);
Taoyu Liaa6238b2019-09-06 17:38:52 +090095
Taoyu Li7dca19a2020-03-16 16:27:07 +090096 // Remove a proxy interface pair.
97 bool RemoveInterfacePair(const std::string& ifname_physical,
98 const std::string& ifname_guest);
Taoyu Liaa6238b2019-09-06 17:38:52 +090099
100 // Remove all proxy interface pair with ifindex.
101 bool RemoveInterface(const std::string& ifname);
102
Taoyu Lia0727dc2020-09-24 19:54:59 +0900103 // 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 Liaa6238b2019-09-06 17:38:52 +0900108 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 Lie47df4a2019-11-08 12:48:57 +0900114 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 Lif0370c92019-09-18 15:04:37 +0900127
Taoyu Liaa6238b2019-09-06 17:38:52 +0900128 interface_mapping* MapForType(uint8_t type);
Taoyu Liaf944c92019-10-01 12:22:31 +0900129 bool IsGuestInterface(int ifindex);
Taoyu Lia0727dc2020-09-24 19:54:59 +0900130 bool IsRouterInterface(int ifindex);
Taoyu Liaa6238b2019-09-06 17:38:52 +0900131
Taoyu Lie47df4a2019-11-08 12:48:57 +0900132 // 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 Li43f66882019-10-25 16:59:34 +0900136
Taoyu Li18041fe2019-11-13 15:49:31 +0900137 // 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 Liaa6238b2019-09-06 17:38:52 +0900143
144 interface_mapping if_map_rs_;
145 interface_mapping if_map_ra_;
146 interface_mapping if_map_ns_na_;
147
Taoyu Lie47df4a2019-11-08 12:48:57 +0900148 base::Callback<void(const std::string&, const std::string&)>
149 guest_discovery_handler_;
Taoyu Lia0727dc2020-09-24 19:54:59 +0900150 base::Callback<void(const std::string&, const std::string&)>
151 router_discovery_handler_;
Taoyu Lie47df4a2019-11-08 12:48:57 +0900152
Taoyu Lif0370c92019-09-18 15:04:37 +0900153 base::WeakPtrFactory<NDProxy> weak_factory_{this};
154
Taoyu Lie47df4a2019-11-08 12:48:57 +0900155 FRIEND_TEST(NDProxyTest, TranslateFrame);
Taoyu Liaa6238b2019-09-06 17:38:52 +0900156};
157
Taoyu Lie47df4a2019-11-08 12:48:57 +0900158// A wrapper class for running NDProxy in a daemon process. Control messages and
159// guest IP discovery messages are passed through |control_fd|.
160class NDProxyDaemon : public brillo::Daemon {
161 public:
162 explicit NDProxyDaemon(base::ScopedFD control_fd);
Qijiang Fan6bc59e12020-11-11 02:51:06 +0900163 NDProxyDaemon(const NDProxyDaemon&) = delete;
164 NDProxyDaemon& operator=(const NDProxyDaemon&) = delete;
165
Taoyu Lie47df4a2019-11-08 12:48:57 +0900166 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 Lia0727dc2020-09-24 19:54:59 +0900181 // 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 Lie47df4a2019-11-08 12:48:57 +0900191 // 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 Lie47df4a2019-11-08 12:48:57 +0900201};
202
Garrick Evans3388a032020-03-24 11:25:55 +0900203} // namespace patchpanel
Taoyu Liaa6238b2019-09-06 17:38:52 +0900204
Garrick Evans3388a032020-03-24 11:25:55 +0900205#endif // PATCHPANEL_NDPROXY_H_