blob: 7a3cfe57ae16df9d5b7f46624107cd013801655b [file] [log] [blame]
Jason Jeremy Iman16f91722020-01-14 09:53:28 +09001// Copyright 2020 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#include "patchpanel/broadcast_forwarder.h"
Jason Jeremy Iman16f91722020-01-14 09:53:28 +09006
7#include <arpa/inet.h>
8#include <linux/filter.h>
9#include <linux/if_ether.h>
10#include <linux/if_packet.h>
11#include <linux/rtnetlink.h>
12#include <net/if.h>
13#include <netinet/ip.h>
14#include <netinet/udp.h>
15#include <shill/net/rtnl_handler.h>
16#include <sys/ioctl.h>
17#include <sys/socket.h>
18#include <sys/types.h>
19
20#include <utility>
21
22#include <base/bind.h>
23#include <base/logging.h>
24
Garrick Evans3388a032020-03-24 11:25:55 +090025#include "patchpanel/socket.h"
Jason Jeremy Iman16f91722020-01-14 09:53:28 +090026
27namespace {
28
29constexpr int kBufSize = 4096;
Jason Jeremy Iman53a1fea2020-02-07 10:38:36 +090030constexpr uint16_t kIpFragOffsetMask = 0x1FFF;
Jason Jeremy Iman16f91722020-01-14 09:53:28 +090031// Broadcast forwarder will not forward system ports (0 - 1023).
32constexpr uint16_t kMinValidPort = 1024;
33
34// SetBcastSockFilter filters out packets by only accepting (all conditions
35// must be fulfilled):
36// - UDP protocol,
37// - Destination address equals to 255.255.255.255 or |bcast_addr|,
38// - Source and destination port is not a system port (>= 1024).
39bool SetBcastSockFilter(int fd, uint32_t bcast_addr) {
40 sock_filter kBcastFwdBpfInstructions[] = {
41 // Load IP protocol value.
42 BPF_STMT(BPF_LD | BPF_B | BPF_ABS, offsetof(iphdr, protocol)),
43 // Check if equals UDP, if not, then goto return 0.
44 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_UDP, 0, 8),
45 // Load IP destination address.
46 BPF_STMT(BPF_LD | BPF_W | BPF_IND, offsetof(iphdr, daddr)),
47 // Check if it is a broadcast address.
48 // All 1s.
Garrick Evans3388a032020-03-24 11:25:55 +090049 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, patchpanel::kBcastAddr, 1, 0),
Jason Jeremy Iman16f91722020-01-14 09:53:28 +090050 // Current interface broadcast address.
51 BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htonl(bcast_addr), 0, 5),
52 // Move index to start of UDP header.
53 BPF_STMT(BPF_LDX | BPF_IMM, sizeof(iphdr)),
54 // Load UDP source port.
55 BPF_STMT(BPF_LD | BPF_H | BPF_IND, offsetof(udphdr, uh_sport)),
56 // Check if it is a valid source port (>= 1024).
57 BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, kMinValidPort, 0, 2),
58 // Load UDP destination port.
59 BPF_STMT(BPF_LD | BPF_H | BPF_IND, offsetof(udphdr, uh_dport)),
60 // Check if it is a valid destination port (>= 1024).
61 BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, kMinValidPort, 1, 0),
62 // Return 0.
63 BPF_STMT(BPF_RET | BPF_K, 0),
64 // Return MAX.
65 BPF_STMT(BPF_RET | BPF_K, IP_MAXPACKET),
66 };
67 sock_fprog kBcastFwdBpfProgram = {
68 .len = sizeof(kBcastFwdBpfInstructions) / sizeof(sock_filter),
69 .filter = kBcastFwdBpfInstructions};
70
71 if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &kBcastFwdBpfProgram,
72 sizeof(kBcastFwdBpfProgram)) != 0) {
73 PLOG(ERROR)
74 << "setsockopt(SO_ATTACH_FILTER) failed for broadcast forwarder";
75 return false;
76 }
77 return true;
78}
79
80void Ioctl(int fd,
81 const std::string& ifname,
82 unsigned int cmd,
83 struct ifreq* ifr) {
84 if (ifname.empty()) {
85 LOG(WARNING) << "Empty interface name";
86 return;
87 }
88
89 memset(ifr, 0, sizeof(struct ifreq));
90 strncpy(ifr->ifr_name, ifname.c_str(), IFNAMSIZ);
91 if (ioctl(fd, cmd, ifr) < 0) {
92 // Ignore EADDRNOTAVAIL: IPv4 was not provisioned.
93 if (errno != EADDRNOTAVAIL) {
94 PLOG(ERROR) << "ioctl call failed for " << ifname;
95 }
96 }
97}
98
99uint32_t GetIfreqAddr(const struct ifreq& ifr) {
100 return reinterpret_cast<const struct sockaddr_in*>(&ifr.ifr_addr)
101 ->sin_addr.s_addr;
102}
103
104uint32_t GetIfreqBroadaddr(const struct ifreq& ifr) {
105 return reinterpret_cast<const struct sockaddr_in*>(&ifr.ifr_broadaddr)
106 ->sin_addr.s_addr;
107}
108
109uint32_t GetIfreqNetmask(const struct ifreq& ifr) {
110 return reinterpret_cast<const struct sockaddr_in*>(&ifr.ifr_netmask)
111 ->sin_addr.s_addr;
112}
113
114} // namespace
115
Garrick Evans3388a032020-03-24 11:25:55 +0900116namespace patchpanel {
Jason Jeremy Iman16f91722020-01-14 09:53:28 +0900117
118BroadcastForwarder::Socket::Socket(base::ScopedFD fd,
119 const base::Callback<void(int)>& callback,
120 uint32_t addr,
121 uint32_t broadaddr,
122 uint32_t netmask)
123 : fd(std::move(fd)), addr(addr), broadaddr(broadaddr), netmask(netmask) {
124 watcher = base::FileDescriptorWatcher::WatchReadable(
125 Socket::fd.get(), base::BindRepeating(callback, Socket::fd.get()));
126}
127
128BroadcastForwarder::BroadcastForwarder(const std::string& dev_ifname)
129 : dev_ifname_(dev_ifname) {
130 addr_listener_ = std::make_unique<shill::RTNLListener>(
131 shill::RTNLHandler::kRequestAddr,
132 base::Bind(&BroadcastForwarder::AddrMsgHandler,
133 weak_factory_.GetWeakPtr()));
134 shill::RTNLHandler::GetInstance()->Start(RTMGRP_IPV4_IFADDR);
135}
136
137void BroadcastForwarder::AddrMsgHandler(const shill::RTNLMessage& msg) {
138 if (!msg.HasAttribute(IFA_LABEL)) {
139 LOG(ERROR) << "Address event message does not have IFA_LABEL";
140 return;
141 }
142
143 if (msg.mode() != shill::RTNLMessage::kModeAdd)
144 return;
145
146 shill::ByteString b(msg.GetAttribute(IFA_LABEL));
147 std::string ifname(reinterpret_cast<const char*>(
148 b.GetSubstring(0, IFNAMSIZ).GetConstData()));
149 if (ifname != dev_ifname_)
150 return;
151
152 // Interface address is added.
153 if (msg.HasAttribute(IFA_ADDRESS)) {
154 shill::ByteString b(msg.GetAttribute(IFA_ADDRESS));
155 memcpy(&dev_socket_->addr, b.GetConstData(), b.GetLength());
156 }
157
158 // Broadcast address is added.
159 if (msg.HasAttribute(IFA_BROADCAST)) {
160 shill::ByteString b(msg.GetAttribute(IFA_BROADCAST));
161 memcpy(&dev_socket_->broadaddr, b.GetConstData(), b.GetLength());
162
163 base::ScopedFD dev_fd(BindRaw(dev_ifname_));
164 if (!dev_fd.is_valid()) {
165 LOG(WARNING) << "Could not bind socket on " << dev_ifname_;
166 return;
167 }
168 dev_socket_.reset(new Socket(
169 std::move(dev_fd),
170 base::BindRepeating(&BroadcastForwarder::OnFileCanReadWithoutBlocking,
171 base::Unretained(this)),
172 dev_socket_->addr, dev_socket_->broadaddr));
173 }
174}
175
176base::ScopedFD BroadcastForwarder::Bind(const std::string& ifname,
177 uint16_t port) {
178 base::ScopedFD fd(socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0));
179 if (!fd.is_valid()) {
180 PLOG(ERROR) << "socket() failed for broadcast forwarder on " << ifname
181 << " for port: " << port;
182 return base::ScopedFD();
183 }
184
185 struct ifreq ifr;
186 memset(&ifr, 0, sizeof(ifr));
187 strncpy(ifr.ifr_name, ifname.c_str(), IFNAMSIZ);
188 if (setsockopt(fd.get(), SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr))) {
189 PLOG(ERROR) << "setsockopt(SOL_SOCKET) failed for broadcast forwarder on "
190 << ifname << " for port: " << port;
191 return base::ScopedFD();
192 }
193
194 int on = 1;
195 if (setsockopt(fd.get(), SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) {
196 PLOG(ERROR) << "setsockopt(SO_BROADCAST) failed for broadcast forwarder on "
197 << ifname << " for: " << port;
198 return base::ScopedFD();
199 }
200
Jason Jeremy Iman5bfef942020-02-05 20:17:26 +0900201 if (setsockopt(fd.get(), SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
202 PLOG(ERROR) << "setsockopt(SO_REUSEADDR) failed for broadcast forwarder on "
203 << ifname << " for: " << port;
204 return base::ScopedFD();
205 }
206
Jason Jeremy Iman16f91722020-01-14 09:53:28 +0900207 struct sockaddr_in bind_addr;
208 memset(&bind_addr, 0, sizeof(bind_addr));
209 bind_addr.sin_addr.s_addr = htonl(INADDR_ANY);
210 bind_addr.sin_family = AF_INET;
211 bind_addr.sin_port = htons(port);
212
213 if (bind(fd.get(), (const struct sockaddr*)&bind_addr, sizeof(bind_addr)) <
214 0) {
215 PLOG(ERROR) << "bind(" << port << ") failed for broadcast forwarder on "
216 << ifname << " for: " << port;
217 return base::ScopedFD();
218 }
219
220 return fd;
221}
222
223base::ScopedFD BroadcastForwarder::BindRaw(const std::string& ifname) {
224 base::ScopedFD fd(
225 socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_IP)));
226 if (!fd.is_valid()) {
227 PLOG(ERROR) << "socket() failed for raw socket";
228 return base::ScopedFD();
229 }
230
231 struct ifreq ifr;
232 memset(&ifr, 0, sizeof(ifr));
233 strncpy(ifr.ifr_name, ifname.c_str(), IFNAMSIZ);
234 if (ioctl(fd.get(), SIOCGIFINDEX, &ifr) < 0) {
235 PLOG(ERROR) << "SIOCGIFINDEX failed for " << ifname;
236 return base::ScopedFD();
237 }
238
239 struct sockaddr_ll bindaddr;
240 memset(&bindaddr, 0, sizeof(bindaddr));
241 bindaddr.sll_family = AF_PACKET;
242 bindaddr.sll_protocol = htons(ETH_P_IP);
243 bindaddr.sll_ifindex = ifr.ifr_ifindex;
244
245 if (bind(fd.get(), (const struct sockaddr*)&bindaddr, sizeof(bindaddr)) < 0) {
246 PLOG(ERROR) << "bind() failed for broadcast forwarder on " << ifname;
247 return base::ScopedFD();
248 }
249
250 Ioctl(fd.get(), ifname, SIOCGIFBRDADDR, &ifr);
251 uint32_t bcast_addr = GetIfreqBroadaddr(ifr);
252
253 if (!SetBcastSockFilter(fd.get(), bcast_addr)) {
254 return base::ScopedFD();
255 }
256
257 return fd;
258}
259
260bool BroadcastForwarder::AddGuest(const std::string& br_ifname) {
261 if (br_sockets_.find(br_ifname) != br_sockets_.end()) {
262 LOG(WARNING) << "Forwarding is already started between " << dev_ifname_
263 << " and " << br_ifname;
264 return false;
265 }
266
267 base::ScopedFD br_fd(BindRaw(br_ifname));
268 if (!br_fd.is_valid()) {
269 LOG(WARNING) << "Could not bind socket on " << br_ifname;
270 return false;
271 }
272
273 struct ifreq ifr;
274 Ioctl(br_fd.get(), br_ifname, SIOCGIFADDR, &ifr);
275 uint32_t br_addr = GetIfreqAddr(ifr);
276 Ioctl(br_fd.get(), br_ifname, SIOCGIFBRDADDR, &ifr);
277 uint32_t br_broadaddr = GetIfreqBroadaddr(ifr);
278 Ioctl(br_fd.get(), br_ifname, SIOCGIFNETMASK, &ifr);
279 uint32_t br_netmask = GetIfreqNetmask(ifr);
280
281 std::unique_ptr<Socket> br_socket = std::make_unique<Socket>(
282 std::move(br_fd),
283 base::BindRepeating(&BroadcastForwarder::OnFileCanReadWithoutBlocking,
284 base::Unretained(this)),
285 br_addr, br_broadaddr, br_netmask);
286
287 br_sockets_.emplace(br_ifname, std::move(br_socket));
288
289 // Broadcast forwarder is not started yet.
290 if (dev_socket_ == nullptr) {
291 base::ScopedFD dev_fd(BindRaw(dev_ifname_));
292 if (!dev_fd.is_valid()) {
293 LOG(WARNING) << "Could not bind socket on " << dev_ifname_;
294 br_sockets_.clear();
295 return false;
296 }
297
298 Ioctl(dev_fd.get(), dev_ifname_, SIOCGIFADDR, &ifr);
299 uint32_t dev_addr = GetIfreqAddr(ifr);
300 Ioctl(dev_fd.get(), dev_ifname_, SIOCGIFBRDADDR, &ifr);
301 uint32_t dev_broadaddr = GetIfreqBroadaddr(ifr);
302
303 dev_socket_.reset(new Socket(
304 std::move(dev_fd),
305 base::BindRepeating(&BroadcastForwarder::OnFileCanReadWithoutBlocking,
306 base::Unretained(this)),
307 dev_addr, dev_broadaddr));
308 }
309 return true;
310}
311
312void BroadcastForwarder::RemoveGuest(const std::string& br_ifname) {
313 const auto& socket = br_sockets_.find(br_ifname);
314 if (socket == br_sockets_.end()) {
315 LOG(WARNING) << "Forwarding is not started between " << dev_ifname_
316 << " and " << br_ifname;
317 return;
318 }
319 br_sockets_.erase(socket);
320}
321
322void BroadcastForwarder::OnFileCanReadWithoutBlocking(int fd) {
323 alignas(4) uint8_t buffer[kBufSize];
324 uint8_t* data = buffer + sizeof(iphdr) + sizeof(udphdr);
325
326 sockaddr_ll dst_addr;
327 struct iovec iov = {
328 .iov_base = buffer,
329 .iov_len = kBufSize,
330 };
331 msghdr hdr = {
332 .msg_name = &dst_addr,
333 .msg_namelen = sizeof(dst_addr),
334 .msg_iov = &iov,
335 .msg_iovlen = 1,
336 .msg_control = nullptr,
337 .msg_controllen = 0,
338 .msg_flags = 0,
339 };
Jason Jeremy Iman53a1fea2020-02-07 10:38:36 +0900340
341 ssize_t msg_len = recvmsg(fd, &hdr, 0);
342 if (msg_len < 0) {
Jason Jeremy Iman16f91722020-01-14 09:53:28 +0900343 PLOG(ERROR) << "recvmsg() failed";
344 return;
345 }
346
347 // These headers are taken directly from the buffer and is 4 bytes aligned.
348 struct iphdr* ip_hdr = (struct iphdr*)(buffer);
349 struct udphdr* udp_hdr = (struct udphdr*)(buffer + sizeof(iphdr));
350
Jason Jeremy Iman53a1fea2020-02-07 10:38:36 +0900351 // Drop fragmented packets.
352 if ((ntohs(ip_hdr->frag_off) & (kIpFragOffsetMask | IP_MF)) != 0)
353 return;
354
355 // Store the length of the message without its headers.
Jason Jeremy Iman16f91722020-01-14 09:53:28 +0900356 ssize_t len = ntohs(udp_hdr->len) - sizeof(udphdr);
357
Jason Jeremy Iman53a1fea2020-02-07 10:38:36 +0900358 // Validate UDP length.
359 if ((len + sizeof(udphdr) + sizeof(iphdr) > msg_len) || (len < 0))
360 return;
361
Jason Jeremy Iman16f91722020-01-14 09:53:28 +0900362 struct sockaddr_in fromaddr = {0};
363 fromaddr.sin_family = AF_INET;
364 fromaddr.sin_port = udp_hdr->uh_sport;
365 fromaddr.sin_addr.s_addr = ip_hdr->saddr;
366
367 struct sockaddr_in dst = {0};
368 dst.sin_family = AF_INET;
369 dst.sin_port = udp_hdr->uh_dport;
370 dst.sin_addr.s_addr = ip_hdr->daddr;
371
372 // Forward ingress traffic to guests.
373 if (fd == dev_socket_->fd.get()) {
374 // Prevent looped back broadcast packets to be forwarded.
375 if (fromaddr.sin_addr.s_addr == dev_socket_->addr)
376 return;
377
378 SendToGuests(buffer, len, dst);
379 return;
380 }
381
382 for (auto const& socket : br_sockets_) {
383 if (fd != socket.second->fd.get())
384 continue;
385
386 // Prevent looped back broadcast packets to be forwarded.
387 if (fromaddr.sin_addr.s_addr == socket.second->addr)
388 return;
389
390 // We are spoofing packets source IP to be the actual sender source IP.
391 // Prevent looped back broadcast packets by not forwarding anything from
392 // outside the interface netmask.
393 if ((fromaddr.sin_addr.s_addr & socket.second->netmask) !=
394 (socket.second->addr & socket.second->netmask))
395 return;
396
397 // Forward egress traffic from one guest to outside network.
398 SendToNetwork(ntohs(fromaddr.sin_port), data, len, dst);
399 }
400}
401
402bool BroadcastForwarder::SendToNetwork(uint16_t src_port,
403 const void* data,
404 ssize_t len,
405 const struct sockaddr_in& dst) {
406 base::ScopedFD temp_fd(Bind(dev_ifname_, src_port));
407 if (!temp_fd.is_valid()) {
408 LOG(WARNING) << "Could not bind socket on " << dev_ifname_ << " for port "
409 << src_port;
410 return false;
411 }
412
413 struct sockaddr_in dev_dst = {0};
414 memcpy(&dev_dst, &dst, sizeof(sockaddr_in));
415
416 if (dev_dst.sin_addr.s_addr != kBcastAddr)
417 dev_dst.sin_addr.s_addr = dev_socket_->broadaddr;
418
419 if (sendto(temp_fd.get(), data, len, 0,
420 reinterpret_cast<const struct sockaddr*>(&dev_dst),
421 sizeof(struct sockaddr_in)) < 0) {
422 PLOG(WARNING) << "sendto failed";
423 return false;
424 }
425 return true;
426}
427
428bool BroadcastForwarder::SendToGuests(const void* ip_pkt,
429 ssize_t len,
430 const struct sockaddr_in& dst) {
431 bool success = true;
432
433 base::ScopedFD raw(socket(AF_INET, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_UDP));
434 if (!raw.is_valid()) {
435 PLOG(ERROR) << "socket() failed for raw socket";
436 return false;
437 }
438
439 int on = 1;
440 if (setsockopt(raw.get(), IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0) {
441 PLOG(ERROR) << "setsockopt(IP_HDRINCL) failed";
442 return false;
443 }
444 if (setsockopt(raw.get(), SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) {
445 PLOG(ERROR) << "setsockopt(SO_BROADCAST) failed";
446 return false;
447 }
448
449 // Copy IP packet received by the lan interface and only change its
450 // destination address.
451 alignas(4) uint8_t buffer[kBufSize];
452 memset(buffer, 0, kBufSize);
453 memcpy(buffer, reinterpret_cast<const uint8_t*>(ip_pkt),
454 sizeof(iphdr) + sizeof(udphdr) + len);
455
456 // These headers are taken directly from the buffer and is 4 bytes aligned.
457 struct iphdr* ip_hdr = (struct iphdr*)buffer;
458 struct udphdr* udp_hdr = (struct udphdr*)(buffer + sizeof(struct iphdr));
459
460 ip_hdr->check = 0;
461 udp_hdr->check = 0;
462
463 struct sockaddr_in br_dst = {0};
464 memcpy(&br_dst, &dst, sizeof(struct sockaddr_in));
465
466 for (auto const& socket : br_sockets_) {
467 // Set destination address.
468 if (br_dst.sin_addr.s_addr != kBcastAddr) {
469 br_dst.sin_addr.s_addr = socket.second->broadaddr;
470 ip_hdr->daddr = socket.second->broadaddr;
471 ip_hdr->check = Ipv4Checksum(ip_hdr);
472 }
473 udp_hdr->check = Udpv4Checksum(ip_hdr, udp_hdr);
474
475 struct ifreq ifr;
476 memset(&ifr, 0, sizeof(ifr));
477 strncpy(ifr.ifr_name, socket.first.c_str(), IFNAMSIZ);
478 if (setsockopt(raw.get(), SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr))) {
479 PLOG(ERROR) << "setsockopt(SOL_SOCKET) failed for broadcast forwarder on "
480 << socket.first;
481 continue;
482 }
483
484 // Use already created broadcast fd.
485 if (sendto(raw.get(), buffer,
486 sizeof(struct iphdr) + sizeof(struct udphdr) + len, 0,
487 reinterpret_cast<const struct sockaddr*>(&br_dst),
488 sizeof(struct sockaddr_in)) < 0) {
489 PLOG(WARNING) << "sendto failed";
490 success = false;
491 }
492 }
493 return success;
494}
495
Garrick Evans3388a032020-03-24 11:25:55 +0900496} // namespace patchpanel