1 // Copyright (c) 2020 The Chromium 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
5 #include "quiche/quic/qbone/bonnet/tun_device_controller.h"
6
7 #include <linux/rtnetlink.h>
8
9 #include "absl/time/clock.h"
10 #include "quiche/quic/platform/api/quic_logging.h"
11 #include "quiche/quic/qbone/qbone_constants.h"
12 #include "quiche/common/quiche_callbacks.h"
13
14 ABSL_FLAG(bool, qbone_tun_device_replace_default_routing_rules, true,
15 "If true, will define a rule that points packets sourced from the "
16 "qbone interface to the qbone table. This is unnecessary in "
17 "environments with no other ipv6 route.");
18
19 ABSL_FLAG(int, qbone_route_init_cwnd, 32,
20 "If non-zero, will add initcwnd to QBONE routing rules. Setting "
21 "a value below 10 is dangerous and not recommended.");
22
23 namespace quic {
24
UpdateAddress(const IpRange & desired_range)25 bool TunDeviceController::UpdateAddress(const IpRange& desired_range) {
26 if (!setup_tun_) {
27 return true;
28 }
29
30 NetlinkInterface::LinkInfo link_info{};
31 if (!netlink_->GetLinkInfo(ifname_, &link_info)) {
32 return false;
33 }
34
35 std::vector<NetlinkInterface::AddressInfo> addresses;
36 if (!netlink_->GetAddresses(link_info.index, 0, &addresses, nullptr)) {
37 return false;
38 }
39
40 QuicIpAddress desired_address = desired_range.FirstAddressInRange();
41
42 for (const auto& address : addresses) {
43 if (!netlink_->ChangeLocalAddress(
44 link_info.index, NetlinkInterface::Verb::kRemove,
45 address.interface_address, address.prefix_length, 0, 0, {})) {
46 return false;
47 }
48 }
49
50 bool address_updated = netlink_->ChangeLocalAddress(
51 link_info.index, NetlinkInterface::Verb::kAdd, desired_address,
52 desired_range.prefix_length(), IFA_F_PERMANENT | IFA_F_NODAD,
53 RT_SCOPE_LINK, {});
54
55 if (address_updated) {
56 current_address_ = desired_address;
57
58 for (const auto& cb : address_update_cbs_) {
59 cb(current_address_);
60 }
61 }
62
63 return address_updated;
64 }
65
UpdateRoutes(const IpRange & desired_range,const std::vector<IpRange> & desired_routes)66 bool TunDeviceController::UpdateRoutes(
67 const IpRange& desired_range, const std::vector<IpRange>& desired_routes) {
68 if (!setup_tun_) {
69 return true;
70 }
71
72 NetlinkInterface::LinkInfo link_info{};
73 if (!netlink_->GetLinkInfo(ifname_, &link_info)) {
74 QUIC_LOG(ERROR) << "Could not get link info for interface <" << ifname_
75 << ">";
76 return false;
77 }
78
79 std::vector<NetlinkInterface::RoutingRule> routing_rules;
80 if (!netlink_->GetRouteInfo(&routing_rules)) {
81 QUIC_LOG(ERROR) << "Unable to get route info";
82 return false;
83 }
84
85 for (const auto& rule : routing_rules) {
86 if (rule.out_interface == link_info.index &&
87 rule.table == QboneConstants::kQboneRouteTableId) {
88 if (!netlink_->ChangeRoute(NetlinkInterface::Verb::kRemove, rule.table,
89 rule.destination_subnet, rule.scope,
90 rule.preferred_source, rule.out_interface,
91 rule.init_cwnd)) {
92 QUIC_LOG(ERROR) << "Unable to remove old route to <"
93 << rule.destination_subnet.ToString() << ">";
94 return false;
95 }
96 }
97 }
98
99 if (!UpdateRules(desired_range)) {
100 return false;
101 }
102
103 QuicIpAddress desired_address = desired_range.FirstAddressInRange();
104
105 std::vector<IpRange> routes(desired_routes.begin(), desired_routes.end());
106 routes.emplace_back(*QboneConstants::TerminatorLocalAddressRange());
107
108 for (const auto& route : routes) {
109 if (!netlink_->ChangeRoute(NetlinkInterface::Verb::kReplace,
110 QboneConstants::kQboneRouteTableId, route,
111 RT_SCOPE_LINK, desired_address, link_info.index,
112 absl::GetFlag(FLAGS_qbone_route_init_cwnd))) {
113 QUIC_LOG(ERROR) << "Unable to add route <" << route.ToString() << ">";
114 return false;
115 }
116 }
117
118 return true;
119 }
120
UpdateRoutesWithRetries(const IpRange & desired_range,const std::vector<IpRange> & desired_routes,int retries)121 bool TunDeviceController::UpdateRoutesWithRetries(
122 const IpRange& desired_range, const std::vector<IpRange>& desired_routes,
123 int retries) {
124 while (retries-- > 0) {
125 if (UpdateRoutes(desired_range, desired_routes)) {
126 return true;
127 }
128 absl::SleepFor(absl::Milliseconds(100));
129 }
130 return false;
131 }
132
UpdateRules(IpRange desired_range)133 bool TunDeviceController::UpdateRules(IpRange desired_range) {
134 if (!absl::GetFlag(FLAGS_qbone_tun_device_replace_default_routing_rules)) {
135 return true;
136 }
137
138 std::vector<NetlinkInterface::IpRule> ip_rules;
139 if (!netlink_->GetRuleInfo(&ip_rules)) {
140 QUIC_LOG(ERROR) << "Unable to get rule info";
141 return false;
142 }
143
144 for (const auto& rule : ip_rules) {
145 if (rule.table == QboneConstants::kQboneRouteTableId) {
146 if (!netlink_->ChangeRule(NetlinkInterface::Verb::kRemove, rule.table,
147 rule.source_range)) {
148 QUIC_LOG(ERROR) << "Unable to remove old rule for table <" << rule.table
149 << "> from source <" << rule.source_range.ToString()
150 << ">";
151 return false;
152 }
153 }
154 }
155
156 if (!netlink_->ChangeRule(NetlinkInterface::Verb::kAdd,
157 QboneConstants::kQboneRouteTableId,
158 desired_range)) {
159 QUIC_LOG(ERROR) << "Unable to add rule for <" << desired_range.ToString()
160 << ">";
161 return false;
162 }
163
164 return true;
165 }
166
current_address()167 QuicIpAddress TunDeviceController::current_address() {
168 return current_address_;
169 }
170
RegisterAddressUpdateCallback(quiche::MultiUseCallback<void (QuicIpAddress)> cb)171 void TunDeviceController::RegisterAddressUpdateCallback(
172 quiche::MultiUseCallback<void(QuicIpAddress)> cb) {
173 address_update_cbs_.push_back(std::move(cb));
174 }
175
176 } // namespace quic
177