blob: 38571691064bf50665343ff9b2658a10cb3fd9a6 [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();
44 virtual ~NDProxy() = default;
45
46 ssize_t TranslateNDFrame(const uint8_t* in_frame,
47 ssize_t frame_len,
48 const MacAddress& local_mac_addr,
49 uint8_t* out_frame);
Taoyu Li18041fe2019-11-13 15:49:31 +090050
Taoyu Lia0727dc2020-09-24 19:54:59 +090051 static const nd_opt_prefix_info* GetPrefixInfoOption(const uint8_t* in_frame,
52 ssize_t frame_len);
53
Taoyu Li18041fe2019-11-13 15:49:31 +090054 // Given an extended |buffer|, find a proper frame buffer pointer so that
55 // pt > buffer, and start of IP header (pt + ETH_H_LEN) is 4-bytes aligned.
56 // In the worst case the size of usable buffer will be original size minus 3.
57 // 4x => 4x+2; 4x+1 => 4x+2; 4x+2 => 4x+2; 4x+3 => 4x+6
58 static const uint8_t* AlignFrameBuffer(const uint8_t* buffer) {
59 return buffer + 3 - (reinterpret_cast<uintptr_t>(buffer + 1) & 0x3);
60 }
61
62 static uint8_t* AlignFrameBuffer(uint8_t* buffer) {
63 return buffer + 3 - (reinterpret_cast<uintptr_t>(buffer + 1) & 0x3);
64 }
Taoyu Liaa6238b2019-09-06 17:38:52 +090065
Taoyu Liba04fe32020-01-14 13:19:35 +090066 // Helper function to create a AF_PACKET socket suitable for frame read/write.
67 static base::ScopedFD PreparePacketSocket();
68
Taoyu Lie47df4a2019-11-08 12:48:57 +090069 // Initialize the resources needed such as rtnl socket and dummy socket for
70 // ioctl. Return false if failed.
71 bool Init();
72
73 // Read one frame from AF_PACKET socket |fd| and process it. If proxying is
74 // needed, translated frame is sent out through the same socket.
75 void ReadAndProcessOneFrame(int fd);
76
77 // NDProxy can trigger a callback upon receiving NA frame with unicast IPv6
78 // address from guest OS interface.
79 void RegisterOnGuestIpDiscoveryHandler(
80 const base::Callback<void(const std::string&, const std::string&)>&
81 handler);
82
Taoyu Lia0727dc2020-09-24 19:54:59 +090083 // Callback upon receiving prefix information from RA frame.
84 void RegisterOnRouterDiscoveryHandler(
85 const base::Callback<void(const std::string&, const std::string&)>&
86 handler);
87
Taoyu Liaa6238b2019-09-06 17:38:52 +090088 // To proxy between upstream interface and guest OS interface (eth0-arc_eth0)
89 // Outbound RS, inbound RA, and bidirectional NS/NA will be proxied.
Taoyu Li7dca19a2020-03-16 16:27:07 +090090 bool AddInterfacePair(const std::string& ifname_physical,
91 const std::string& ifname_guest);
Taoyu Liaa6238b2019-09-06 17:38:52 +090092
Taoyu Li7dca19a2020-03-16 16:27:07 +090093 // Remove a proxy interface pair.
94 bool RemoveInterfacePair(const std::string& ifname_physical,
95 const std::string& ifname_guest);
Taoyu Liaa6238b2019-09-06 17:38:52 +090096
97 // Remove all proxy interface pair with ifindex.
98 bool RemoveInterface(const std::string& ifname);
99
Taoyu Lia0727dc2020-09-24 19:54:59 +0900100 // Utility to get a list of guest interfaces names that are currently being
101 // proxied with a specific physical interface.
102 std::vector<std::string> GetGuestInterfaces(
103 const std::string& ifname_physical);
104
Taoyu Liaa6238b2019-09-06 17:38:52 +0900105 private:
106 // Data structure to store interface mapping for a certain kind of packet to
107 // be proxied. For example, {1: {2}, 2: {1}} means that packet from interfaces
108 // 1 and 2 will be proxied to each other.
109 using interface_mapping = std::map<int, std::set<int>>;
110
Taoyu Lie47df4a2019-11-08 12:48:57 +0900111 static void ReplaceMacInIcmpOption(uint8_t* frame,
112 ssize_t frame_len,
113 size_t nd_hdr_len,
114 uint8_t opt_type,
115 const MacAddress& target_mac);
116
117 // Get MAC address on a local interface through ioctl().
118 // Returns false upon failure.
119 virtual bool GetLocalMac(int if_id, MacAddress* mac_addr);
120
121 // Query kernel NDP table and get the MAC address of a certain IPv6 neighbor.
122 // Returns false when neighbor entry is not found.
123 virtual bool GetNeighborMac(const in6_addr& ipv6_addr, MacAddress* mac_addr);
Taoyu Lif0370c92019-09-18 15:04:37 +0900124
Taoyu Liaa6238b2019-09-06 17:38:52 +0900125 interface_mapping* MapForType(uint8_t type);
Taoyu Liaf944c92019-10-01 12:22:31 +0900126 bool IsGuestInterface(int ifindex);
Taoyu Lia0727dc2020-09-24 19:54:59 +0900127 bool IsRouterInterface(int ifindex);
Taoyu Liaa6238b2019-09-06 17:38:52 +0900128
Taoyu Lie47df4a2019-11-08 12:48:57 +0900129 // Socket used to communicate with kernel through ioctl. No real packet data
130 // goes through this socket.
131 base::ScopedFD dummy_fd_;
132 base::ScopedFD rtnl_fd_;
Taoyu Li43f66882019-10-25 16:59:34 +0900133
Taoyu Li18041fe2019-11-13 15:49:31 +0900134 // Allocate slightly more space and adjust the buffer start location to
135 // make sure IP header is 4-bytes aligned.
136 uint8_t in_frame_buffer_extended_[IP_MAXPACKET + ETH_HLEN + 4];
137 uint8_t out_frame_buffer_extended_[IP_MAXPACKET + ETH_HLEN + 4];
138 uint8_t* in_frame_buffer_;
139 uint8_t* out_frame_buffer_;
Taoyu Liaa6238b2019-09-06 17:38:52 +0900140
141 interface_mapping if_map_rs_;
142 interface_mapping if_map_ra_;
143 interface_mapping if_map_ns_na_;
144
Taoyu Lie47df4a2019-11-08 12:48:57 +0900145 base::Callback<void(const std::string&, const std::string&)>
146 guest_discovery_handler_;
Taoyu Lia0727dc2020-09-24 19:54:59 +0900147 base::Callback<void(const std::string&, const std::string&)>
148 router_discovery_handler_;
Taoyu Lie47df4a2019-11-08 12:48:57 +0900149
Taoyu Lif0370c92019-09-18 15:04:37 +0900150 base::WeakPtrFactory<NDProxy> weak_factory_{this};
151
Taoyu Lie47df4a2019-11-08 12:48:57 +0900152 FRIEND_TEST(NDProxyTest, TranslateFrame);
Taoyu Liaa6238b2019-09-06 17:38:52 +0900153 DISALLOW_COPY_AND_ASSIGN(NDProxy);
154};
155
Taoyu Lie47df4a2019-11-08 12:48:57 +0900156// A wrapper class for running NDProxy in a daemon process. Control messages and
157// guest IP discovery messages are passed through |control_fd|.
158class NDProxyDaemon : public brillo::Daemon {
159 public:
160 explicit NDProxyDaemon(base::ScopedFD control_fd);
161 virtual ~NDProxyDaemon();
162
163 private:
164 // Overrides Daemon init callback. Returns 0 on success and < 0 on error.
165 int OnInit() override;
166 // FileDescriptorWatcher callbacks for new data on fd_.
167 void OnDataSocketReadReady();
168 // Callbacks to be registered to msg_dispatcher to handle control messages.
169 void OnParentProcessExit();
170 void OnDeviceMessage(const DeviceMessage& msg);
171
172 // Callback from NDProxy core when receive NA from guest
173 void OnGuestIpDiscovery(const std::string& ifname,
174 const std::string& ip6addr);
175
Taoyu Lia0727dc2020-09-24 19:54:59 +0900176 // Callback from NDProxy core when receive prefix info from router
177 void OnRouterDiscovery(const std::string& ifname, const std::string& ip6addr);
178
179 void SendMessage(NDProxyMessage::NDProxyEventType type,
180 const std::string& ifname,
181 const std::string& ip6addr);
182
183 // Map from guest-facing ifname to eui address we assigned
184 std::map<std::string, std::string> guest_if_addrs_;
185
Taoyu Lie47df4a2019-11-08 12:48:57 +0900186 // Utilize MessageDispatcher to watch control fd
187 std::unique_ptr<MessageDispatcher> msg_dispatcher_;
188
189 // Data fd and its watcher
190 base::ScopedFD fd_;
191 std::unique_ptr<base::FileDescriptorWatcher::Controller> watcher_;
192
193 NDProxy proxy_;
194
195 base::WeakPtrFactory<NDProxyDaemon> weak_factory_{this};
196
197 DISALLOW_COPY_AND_ASSIGN(NDProxyDaemon);
198};
199
Garrick Evans3388a032020-03-24 11:25:55 +0900200} // namespace patchpanel
Taoyu Liaa6238b2019-09-06 17:38:52 +0900201
Garrick Evans3388a032020-03-24 11:25:55 +0900202#endif // PATCHPANEL_NDPROXY_H_