blob: 4a4427b5d06ee346e53f7a575521fb25da323b37 [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>
19
20#include <base/files/scoped_file.h>
21#include <base/macros.h>
Taoyu Lif0370c92019-09-18 15:04:37 +090022#include <brillo/daemons/daemon.h>
Taoyu Lie47df4a2019-11-08 12:48:57 +090023#include <gtest/gtest_prod.h> // for FRIEND_TEST
Taoyu Lif0370c92019-09-18 15:04:37 +090024
Garrick Evans3388a032020-03-24 11:25:55 +090025#include "patchpanel/mac_address_generator.h"
26#include "patchpanel/message_dispatcher.h"
Taoyu Liaa6238b2019-09-06 17:38:52 +090027
Garrick Evans3388a032020-03-24 11:25:55 +090028namespace patchpanel {
Taoyu Liaa6238b2019-09-06 17:38:52 +090029
30// Forward ICMPv6 RS/RA/NS/NA mssages between network interfaces according to
31// RFC 4389. Support asymmetric proxy that RS will be proxied one-way from
32// guest interface to physical interface ('Outbound') and RA the other way back
33// ('Inbound'), as well as symmetric proxy among guest interfaces that only
Taoyu Lif0370c92019-09-18 15:04:37 +090034// NS/NA will be proxied.
Taoyu Lie47df4a2019-11-08 12:48:57 +090035class NDProxy {
Taoyu Liaa6238b2019-09-06 17:38:52 +090036 public:
Taoyu Lie47df4a2019-11-08 12:48:57 +090037 static constexpr ssize_t kTranslateErrorNotICMPv6Frame = -1;
38 static constexpr ssize_t kTranslateErrorNotNDFrame = -2;
39 static constexpr ssize_t kTranslateErrorInsufficientLength = -3;
40 static constexpr ssize_t kTranslateErrorBufferMisaligned = -4;
Taoyu Liaa6238b2019-09-06 17:38:52 +090041
Taoyu Lie47df4a2019-11-08 12:48:57 +090042 NDProxy();
43 virtual ~NDProxy() = default;
44
45 ssize_t TranslateNDFrame(const uint8_t* in_frame,
46 ssize_t frame_len,
47 const MacAddress& local_mac_addr,
48 uint8_t* out_frame);
Taoyu Li18041fe2019-11-13 15:49:31 +090049
50 // Given an extended |buffer|, find a proper frame buffer pointer so that
51 // pt > buffer, and start of IP header (pt + ETH_H_LEN) is 4-bytes aligned.
52 // In the worst case the size of usable buffer will be original size minus 3.
53 // 4x => 4x+2; 4x+1 => 4x+2; 4x+2 => 4x+2; 4x+3 => 4x+6
54 static const uint8_t* AlignFrameBuffer(const uint8_t* buffer) {
55 return buffer + 3 - (reinterpret_cast<uintptr_t>(buffer + 1) & 0x3);
56 }
57
58 static uint8_t* AlignFrameBuffer(uint8_t* buffer) {
59 return buffer + 3 - (reinterpret_cast<uintptr_t>(buffer + 1) & 0x3);
60 }
Taoyu Liaa6238b2019-09-06 17:38:52 +090061
Taoyu Liba04fe32020-01-14 13:19:35 +090062 // Helper function to create a AF_PACKET socket suitable for frame read/write.
63 static base::ScopedFD PreparePacketSocket();
64
Taoyu Lie47df4a2019-11-08 12:48:57 +090065 // Initialize the resources needed such as rtnl socket and dummy socket for
66 // ioctl. Return false if failed.
67 bool Init();
68
69 // Read one frame from AF_PACKET socket |fd| and process it. If proxying is
70 // needed, translated frame is sent out through the same socket.
71 void ReadAndProcessOneFrame(int fd);
72
73 // NDProxy can trigger a callback upon receiving NA frame with unicast IPv6
74 // address from guest OS interface.
75 void RegisterOnGuestIpDiscoveryHandler(
76 const base::Callback<void(const std::string&, const std::string&)>&
77 handler);
78
Taoyu Liaa6238b2019-09-06 17:38:52 +090079 // To proxy between upstream interface and guest OS interface (eth0-arc_eth0)
80 // Outbound RS, inbound RA, and bidirectional NS/NA will be proxied.
Taoyu Li7dca19a2020-03-16 16:27:07 +090081 bool AddInterfacePair(const std::string& ifname_physical,
82 const std::string& ifname_guest);
Taoyu Liaa6238b2019-09-06 17:38:52 +090083
Taoyu Li7dca19a2020-03-16 16:27:07 +090084 // Remove a proxy interface pair.
85 bool RemoveInterfacePair(const std::string& ifname_physical,
86 const std::string& ifname_guest);
Taoyu Liaa6238b2019-09-06 17:38:52 +090087
88 // Remove all proxy interface pair with ifindex.
89 bool RemoveInterface(const std::string& ifname);
90
Taoyu Liaa6238b2019-09-06 17:38:52 +090091 private:
92 // Data structure to store interface mapping for a certain kind of packet to
93 // be proxied. For example, {1: {2}, 2: {1}} means that packet from interfaces
94 // 1 and 2 will be proxied to each other.
95 using interface_mapping = std::map<int, std::set<int>>;
96
Taoyu Lie47df4a2019-11-08 12:48:57 +090097 static void ReplaceMacInIcmpOption(uint8_t* frame,
98 ssize_t frame_len,
99 size_t nd_hdr_len,
100 uint8_t opt_type,
101 const MacAddress& target_mac);
102
103 // Get MAC address on a local interface through ioctl().
104 // Returns false upon failure.
105 virtual bool GetLocalMac(int if_id, MacAddress* mac_addr);
106
107 // Query kernel NDP table and get the MAC address of a certain IPv6 neighbor.
108 // Returns false when neighbor entry is not found.
109 virtual bool GetNeighborMac(const in6_addr& ipv6_addr, MacAddress* mac_addr);
Taoyu Lif0370c92019-09-18 15:04:37 +0900110
Taoyu Liaa6238b2019-09-06 17:38:52 +0900111 interface_mapping* MapForType(uint8_t type);
Taoyu Liaf944c92019-10-01 12:22:31 +0900112 bool IsGuestInterface(int ifindex);
Taoyu Liaa6238b2019-09-06 17:38:52 +0900113
Taoyu Lie47df4a2019-11-08 12:48:57 +0900114 // Socket used to communicate with kernel through ioctl. No real packet data
115 // goes through this socket.
116 base::ScopedFD dummy_fd_;
117 base::ScopedFD rtnl_fd_;
Taoyu Li43f66882019-10-25 16:59:34 +0900118
Taoyu Li18041fe2019-11-13 15:49:31 +0900119 // Allocate slightly more space and adjust the buffer start location to
120 // make sure IP header is 4-bytes aligned.
121 uint8_t in_frame_buffer_extended_[IP_MAXPACKET + ETH_HLEN + 4];
122 uint8_t out_frame_buffer_extended_[IP_MAXPACKET + ETH_HLEN + 4];
123 uint8_t* in_frame_buffer_;
124 uint8_t* out_frame_buffer_;
Taoyu Liaa6238b2019-09-06 17:38:52 +0900125
126 interface_mapping if_map_rs_;
127 interface_mapping if_map_ra_;
128 interface_mapping if_map_ns_na_;
129
Taoyu Lie47df4a2019-11-08 12:48:57 +0900130 base::Callback<void(const std::string&, const std::string&)>
131 guest_discovery_handler_;
132
Taoyu Lif0370c92019-09-18 15:04:37 +0900133 base::WeakPtrFactory<NDProxy> weak_factory_{this};
134
Taoyu Lie47df4a2019-11-08 12:48:57 +0900135 FRIEND_TEST(NDProxyTest, TranslateFrame);
Taoyu Liaa6238b2019-09-06 17:38:52 +0900136 DISALLOW_COPY_AND_ASSIGN(NDProxy);
137};
138
Taoyu Lie47df4a2019-11-08 12:48:57 +0900139// A wrapper class for running NDProxy in a daemon process. Control messages and
140// guest IP discovery messages are passed through |control_fd|.
141class NDProxyDaemon : public brillo::Daemon {
142 public:
143 explicit NDProxyDaemon(base::ScopedFD control_fd);
144 virtual ~NDProxyDaemon();
145
146 private:
147 // Overrides Daemon init callback. Returns 0 on success and < 0 on error.
148 int OnInit() override;
149 // FileDescriptorWatcher callbacks for new data on fd_.
150 void OnDataSocketReadReady();
151 // Callbacks to be registered to msg_dispatcher to handle control messages.
152 void OnParentProcessExit();
153 void OnDeviceMessage(const DeviceMessage& msg);
154
155 // Callback from NDProxy core when receive NA from guest
156 void OnGuestIpDiscovery(const std::string& ifname,
157 const std::string& ip6addr);
158
159 // Utilize MessageDispatcher to watch control fd
160 std::unique_ptr<MessageDispatcher> msg_dispatcher_;
161
162 // Data fd and its watcher
163 base::ScopedFD fd_;
164 std::unique_ptr<base::FileDescriptorWatcher::Controller> watcher_;
165
166 NDProxy proxy_;
167
168 base::WeakPtrFactory<NDProxyDaemon> weak_factory_{this};
169
170 DISALLOW_COPY_AND_ASSIGN(NDProxyDaemon);
171};
172
Garrick Evans3388a032020-03-24 11:25:55 +0900173} // namespace patchpanel
Taoyu Liaa6238b2019-09-06 17:38:52 +0900174
Garrick Evans3388a032020-03-24 11:25:55 +0900175#endif // PATCHPANEL_NDPROXY_H_