master
cpp 254 lines 8.12 KB
Raw
1 // Copyright (C) Microsoft Corporation. All rights reserved.
2
3 #include <linux/if_packet.h>
4 #include <linux/if_ether.h>
5 #include <chrono>
6 #include <arpa/inet.h>
7 #include <poll.h>
8 #include "lxwil.h"
9 #include "RuntimeErrorWithSourceLocation.h"
10 #include "IpNeighborManager.h"
11 #include "NetlinkTransactionError.h"
12 #include "Utils.h"
13
14 #define ARPHRD_ETHER 1
15 #define ARPOP_REQUEST 1
16 #define ARPOP_REPLY 2
17
18 const MacAddress BroadcastMac{0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
19
20 template <size_t TProtocolAddressLength>
21 struct _arp_packet_header
22 {
23 using IPAddress = std::array<uint8_t, TProtocolAddressLength>;
24 MacAddress Destination;
25 MacAddress Source;
26 uint16_t EthernetType;
27 uint16_t HardwareType;
28 uint16_t ProtocolType;
29 uint8_t HardwareAddressLength;
30 uint8_t ProtocolAddressLength;
31 uint16_t Operation;
32 MacAddress SenderHardwareAddress;
33 IPAddress SenderIpAddress;
34 MacAddress TargetHardwareAddress;
35 IPAddress TargetIpAddress;
36 } __attribute__((packed));
37
38 using arp_packet_ipv4_t = _arp_packet_header<4>;
39 using arp_packet_ipv6_t = _arp_packet_header<16>;
40
41 template <typename T>
42 void ComposeArpRequest(T& ArpRequest, uint16_t ProtocolType, const Neighbor& Source, const Neighbor& Target)
43 {
44 // Ethernet header
45 ArpRequest.Destination = BroadcastMac;
46 ArpRequest.Source = Source.macAddress;
47 ArpRequest.EthernetType = htons(ETH_P_ARP);
48 ArpRequest.HardwareType = htons(ARPHRD_ETHER);
49 ArpRequest.ProtocolType = htons(ProtocolType);
50 ArpRequest.HardwareAddressLength = sizeof(ArpRequest.SenderHardwareAddress);
51 ArpRequest.ProtocolAddressLength = sizeof(ArpRequest.SenderIpAddress);
52 ArpRequest.Operation = htons(ARPOP_REQUEST);
53 ArpRequest.SenderHardwareAddress = Source.macAddress;
54 ArpRequest.TargetHardwareAddress.fill(0);
55 Source.ipAddress.ConvertToBytes(ArpRequest.SenderIpAddress.data());
56 Target.ipAddress.ConvertToBytes(ArpRequest.TargetIpAddress.data());
57 }
58
59 template <typename T>
60 bool ParseArpReply(const T& ArpReply, uint16_t ProtocolType, const Neighbor& Source, Neighbor& Target)
61 {
62 typename T::IPAddress SourceIp;
63 typename T::IPAddress TargetIp;
64 Source.ipAddress.ConvertToBytes(SourceIp.data());
65 Target.ipAddress.ConvertToBytes(TargetIp.data());
66
67 if (ArpReply.Destination != Source.macAddress)
68 {
69 return false;
70 }
71 if (ArpReply.EthernetType != htons(ETH_P_ARP))
72 {
73 return false;
74 }
75 if (ArpReply.HardwareType != htons(ARPHRD_ETHER))
76 {
77 return false;
78 }
79 if (ArpReply.ProtocolType != htons(ProtocolType))
80 {
81 return false;
82 }
83 if (ArpReply.HardwareAddressLength != sizeof(ArpReply.SenderHardwareAddress))
84 {
85 return false;
86 }
87 if (ArpReply.ProtocolAddressLength != sizeof(ArpReply.SenderIpAddress))
88 {
89 return false;
90 }
91 if (ArpReply.Operation != htons(ARPOP_REPLY))
92 {
93 return false;
94 }
95 if (ArpReply.TargetHardwareAddress != Source.macAddress)
96 {
97 return false;
98 }
99 if (ArpReply.TargetIpAddress != SourceIp)
100 {
101 return false;
102 }
103
104 Target.macAddress = ArpReply.SenderHardwareAddress;
105 return true;
106 }
107
108 bool IpNeighborManager::PerformNeighborDiscovery(Neighbor& Local, Neighbor& Neighbor)
109 {
110 sockaddr_ll address{};
111 wil::unique_fd packet_socket = Syscall(socket, AF_PACKET, SOCK_RAW | SOCK_NONBLOCK, htons(ETH_P_ALL));
112 address.sll_family = AF_PACKET;
113 address.sll_protocol = htons(ETH_P_ALL);
114 address.sll_ifindex = Neighbor.dev;
115 Syscall(bind, packet_socket.get(), reinterpret_cast<sockaddr*>(&address), sizeof(address));
116
117 union
118 {
119 arp_packet_ipv4_t IPv4;
120 arp_packet_ipv6_t IPv6;
121 } ArpRequest, ArpReply;
122 size_t ArpPacketSize = Local.getFamily() == AF_INET ? sizeof(arp_packet_ipv4_t) : sizeof(arp_packet_ipv6_t);
123
124 if (Local.getFamily() == AF_INET)
125 {
126 ComposeArpRequest(ArpRequest.IPv4, ETH_P_IP, Local, Neighbor);
127 }
128 else
129 {
130 ComposeArpRequest(ArpRequest.IPv6, ETH_P_IPV6, Local, Neighbor);
131 }
132
133 auto wait_for_read = [](int fd, std::chrono::milliseconds timeout_ms) -> bool {
134 short poll_events = POLLIN | POLLPRI;
135 struct pollfd pollfds[1];
136 pollfds[0] = {.fd = fd, .events = poll_events, .revents = 0};
137
138 int return_value = Syscall(poll, pollfds, 1, timeout_ms.count());
139 return (return_value == 1) && (pollfds[0].revents == POLLIN);
140 };
141
142 for (size_t retry = 0; retry < 5; retry++)
143 {
144 auto expiry = std::chrono::steady_clock::now() + std::chrono::milliseconds(500);
145 Syscall(write, packet_socket.get(), &ArpRequest, ArpPacketSize);
146
147 while (std::chrono::steady_clock::now() < expiry)
148 {
149 if (!wait_for_read(packet_socket.get(), std::chrono::duration_cast<std::chrono::milliseconds>(expiry - std::chrono::steady_clock::now())))
150 {
151 continue;
152 }
153 int bytes_read = Syscall(read, packet_socket.get(), &ArpReply, ArpPacketSize);
154 if (bytes_read != ArpPacketSize)
155 {
156 continue;
157 }
158 if (Local.getFamily() == AF_INET)
159 {
160 if (ParseArpReply(ArpReply.IPv4, ETH_P_IP, Local, Neighbor))
161 {
162 return true;
163 }
164 }
165 else
166 {
167 if (ParseArpReply(ArpReply.IPv6, ETH_P_IPV6, Local, Neighbor))
168 {
169 return true;
170 }
171 }
172 }
173 }
174 return false;
175 }
176
177 void IpNeighborManager::ModifyNeighborEntry(const Neighbor& Neighbor, Operation operation)
178 {
179 assert(operation == Operation::Create || operation == Operation::Update || operation == Operation::Remove);
180
181 // In case of Remove, there are no additional flags needed besides NLM_F_REQUEST | NLM_F_ACK.
182 int flags = 0;
183 if (operation == Update)
184 {
185 flags = NLM_F_CREATE | NLM_F_REPLACE;
186 }
187 else if (operation == Create)
188 {
189 flags = NLM_F_CREATE;
190 }
191
192 int netlinkOperation = operation == Remove ? RTM_DELNEIGH : RTM_NEWNEIGH;
193 if (Neighbor.getFamily() == AF_INET)
194 {
195 ModifyNeighborEntryImpl<in_addr>(Neighbor, netlinkOperation, flags);
196 }
197 else
198 {
199 ModifyNeighborEntryImpl<in6_addr>(Neighbor, netlinkOperation, flags);
200 }
201 }
202
203 template <typename T>
204 void IpNeighborManager::SendMessage(const Neighbor& Neighbor, int operation, int flags, const std::function<void(T&)>& routine)
205 {
206 T message{};
207 message.header.ndm_family = Neighbor.getFamily();
208 message.header.ndm_ifindex = Neighbor.dev;
209 message.header.ndm_state = NUD_PERMANENT;
210 message.header.ndm_type = RTN_UNICAST;
211
212 routine(message);
213
214 auto transaction = m_channel.CreateTransaction(message, operation, flags);
215 try
216 {
217 transaction.Execute();
218 }
219 catch (const NetlinkTransactionError& transactionErr)
220 {
221 auto errorCode = transactionErr.Error();
222 if (errorCode.has_value())
223 {
224 // Errors "file exists" and "file not found" are ignored in order to avoid keeping
225 // track in GnsDaemon of what neighbour entries were added/deleted and allow the same entry
226 // to be added/deleted multiple times.
227 if (errorCode.value() == -EEXIST || errorCode.value() == -ENOENT)
228 {
229 return;
230 }
231 }
232
233 throw;
234 }
235 }
236
237 template <typename TAddr>
238 void IpNeighborManager::ModifyNeighborEntryImpl(const Neighbor& Neighbor, int operation, int flags)
239 {
240 struct Message
241 {
242 ndmsg header;
243 utils::AddressAttribute<TAddr> ip;
244 utils::MacAddressAttribute mac;
245 } __attribute__((packed));
246
247 SendMessage<Message>(Neighbor, operation, flags, [&](Message& message) {
248 utils::InitializeAddressAttribute<TAddr>(message.ip, Neighbor.ipAddress, NDA_DST);
249
250 message.mac.header.nla_len = sizeof(message.mac);
251 message.mac.header.nla_type = NDA_LLADDR;
252 message.mac.address = Neighbor.macAddress;
253 });
254 }