blob: bc38e235328cef0a8e21a9a80f9e97f89da0f951 [file] [log] [blame]
mukesh agrawalddc378f2012-02-17 18:26:20 -08001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Paul Stewartdd60e452011-08-08 11:38:36 -07002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "shill/connection.h"
6
Paul Stewart9a908082011-08-31 12:18:48 -07007#include <arpa/inet.h>
8#include <linux/rtnetlink.h>
9
10#include "shill/device_info.h"
Christopher Wileyb691efd2012-08-09 13:51:51 -070011#include "shill/logging.h"
Paul Stewartdd60e452011-08-08 11:38:36 -070012#include "shill/resolver.h"
13#include "shill/routing_table.h"
14#include "shill/rtnl_handler.h"
15
Darin Petkov13e6d552012-05-09 14:22:23 +020016using base::Bind;
17using base::Closure;
18using base::Unretained;
19using std::deque;
Paul Stewartdd60e452011-08-08 11:38:36 -070020using std::string;
Paul Stewartd62d6032012-09-11 11:35:49 -070021using std::vector;
Paul Stewartdd60e452011-08-08 11:38:36 -070022
23namespace shill {
24
25// static
26const uint32 Connection::kDefaultMetric = 1;
27// static
Paul Stewart7cfca042011-12-08 14:18:17 -080028const uint32 Connection::kNonDefaultMetricBase = 10;
Paul Stewartdd60e452011-08-08 11:38:36 -070029
Darin Petkov13e6d552012-05-09 14:22:23 +020030Connection::Binder::Binder(const string &name,
31 const Closure &disconnect_callback)
32 : name_(name),
Darin Petkov13e6d552012-05-09 14:22:23 +020033 client_disconnect_callback_(disconnect_callback) {}
34
35Connection::Binder::~Binder() {
36 Attach(NULL);
37}
38
Darin Petkovef1f9fe2012-05-11 16:51:52 +020039void Connection::Binder::Attach(const ConnectionRefPtr &to_connection) {
Darin Petkov13e6d552012-05-09 14:22:23 +020040 if (connection_) {
41 connection_->DetachBinder(this);
42 LOG(INFO) << name_ << ": unbound from connection: "
43 << connection_->interface_name();
Darin Petkovef1f9fe2012-05-11 16:51:52 +020044 connection_.reset();
Darin Petkov13e6d552012-05-09 14:22:23 +020045 }
Darin Petkovef1f9fe2012-05-11 16:51:52 +020046 if (to_connection) {
47 connection_ = to_connection->weak_ptr_factory_.GetWeakPtr();
Darin Petkov13e6d552012-05-09 14:22:23 +020048 connection_->AttachBinder(this);
49 LOG(INFO) << name_ << ": bound to connection: "
50 << connection_->interface_name();
51 }
52}
53
54void Connection::Binder::OnDisconnect() {
55 LOG(INFO) << name_ << ": bound connection disconnected: "
56 << connection_->interface_name();
Darin Petkovef1f9fe2012-05-11 16:51:52 +020057 connection_.reset();
Darin Petkov13e6d552012-05-09 14:22:23 +020058 if (!client_disconnect_callback_.is_null()) {
59 SLOG(Connection, 2) << "Running client disconnect callback.";
60 client_disconnect_callback_.Run();
61 }
62}
63
Paul Stewart9a908082011-08-31 12:18:48 -070064Connection::Connection(int interface_index,
65 const std::string& interface_name,
Paul Stewarte00600e2012-03-16 07:08:00 -070066 Technology::Identifier technology,
Paul Stewartbf667612012-06-29 14:49:54 -070067 const DeviceInfo *device_info,
68 bool is_short_dns_timeout_enabled)
Darin Petkov13e6d552012-05-09 14:22:23 +020069 : weak_ptr_factory_(this),
70 is_default_(false),
Paul Stewart4a6748d2012-07-17 14:31:36 -070071 has_broadcast_domain_(false),
Paul Stewartc8f4bef2011-12-13 09:45:51 -080072 routing_request_count_(0),
Paul Stewartdd60e452011-08-08 11:38:36 -070073 interface_index_(interface_index),
74 interface_name_(interface_name),
Paul Stewarte00600e2012-03-16 07:08:00 -070075 technology_(technology),
Paul Stewart4a6748d2012-07-17 14:31:36 -070076 local_(IPAddress::kFamilyUnknown),
77 gateway_(IPAddress::kFamilyUnknown),
Darin Petkov13e6d552012-05-09 14:22:23 +020078 lower_binder_(
79 interface_name_,
80 // Connection owns a single instance of |lower_binder_| so it's safe
81 // to use an Unretained callback.
82 Bind(&Connection::OnLowerDisconnect, Unretained(this))),
Paul Stewartbf667612012-06-29 14:49:54 -070083 dns_timeout_parameters_(Resolver::kDefaultTimeout),
Paul Stewart9a908082011-08-31 12:18:48 -070084 device_info_(device_info),
Paul Stewartdd60e452011-08-08 11:38:36 -070085 resolver_(Resolver::GetInstance()),
86 routing_table_(RoutingTable::GetInstance()),
87 rtnl_handler_(RTNLHandler::GetInstance()) {
Ben Chanfad4a0b2012-04-18 15:49:59 -070088 SLOG(Connection, 2) << __func__ << "(" << interface_index << ", "
89 << interface_name << ", "
90 << Technology::NameFromIdentifier(technology) << ")";
Paul Stewartbf667612012-06-29 14:49:54 -070091 if (is_short_dns_timeout_enabled) {
92 dns_timeout_parameters_ = Resolver::kShortTimeout;
93 }
Paul Stewartdd60e452011-08-08 11:38:36 -070094}
95
96Connection::~Connection() {
Ben Chanfad4a0b2012-04-18 15:49:59 -070097 SLOG(Connection, 2) << __func__ << " " << interface_name_;
Paul Stewart9a908082011-08-31 12:18:48 -070098
Darin Petkov13e6d552012-05-09 14:22:23 +020099 NotifyBindersOnDisconnect();
100
Paul Stewartc8f4bef2011-12-13 09:45:51 -0800101 DCHECK(!routing_request_count_);
Thieu Lefb46caf2012-03-08 11:57:15 -0800102 routing_table_->FlushRoutes(interface_index_);
Paul Stewarte93b0382012-04-24 13:11:28 -0700103 routing_table_->FlushRoutesWithTag(interface_index_);
Paul Stewart9a908082011-08-31 12:18:48 -0700104 device_info_->FlushAddresses(interface_index_);
Paul Stewartdd60e452011-08-08 11:38:36 -0700105}
106
107void Connection::UpdateFromIPConfig(const IPConfigRefPtr &config) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700108 SLOG(Connection, 2) << __func__ << " " << interface_name_;
Paul Stewarte6132022011-08-16 09:11:02 -0700109
Paul Stewart9a908082011-08-31 12:18:48 -0700110 const IPConfig::Properties &properties = config->properties();
Paul Stewarte93b0382012-04-24 13:11:28 -0700111 if (!properties.trusted_ip.empty() && !PinHostRoute(properties)) {
112 LOG(ERROR) << "Unable to pin host route to " << properties.trusted_ip;
113 return;
114 }
115
Paul Stewart9a908082011-08-31 12:18:48 -0700116 IPAddress local(properties.address_family);
117 if (!local.SetAddressFromString(properties.address)) {
118 LOG(ERROR) << "Local address " << properties.address << " is invalid";
119 return;
120 }
Paul Stewart48100b02012-03-19 07:53:52 -0700121 local.set_prefix(properties.subnet_prefix);
Paul Stewart9a908082011-08-31 12:18:48 -0700122
123 IPAddress broadcast(properties.address_family);
Paul Stewart1062d9d2012-04-27 10:42:27 -0700124 if (properties.broadcast_address.empty()) {
Paul Stewartfe1c0e12012-04-30 19:57:04 -0700125 if (properties.peer_address.empty()) {
Paul Stewart1062d9d2012-04-27 10:42:27 -0700126 LOG(WARNING) << "Broadcast address is not set. Using default.";
Paul Stewartfe1c0e12012-04-30 19:57:04 -0700127 broadcast = local.GetDefaultBroadcast();
Paul Stewart1062d9d2012-04-27 10:42:27 -0700128 }
129 } else if (!broadcast.SetAddressFromString(properties.broadcast_address)) {
Paul Stewart9a908082011-08-31 12:18:48 -0700130 LOG(ERROR) << "Broadcast address " << properties.broadcast_address
131 << " is invalid";
132 return;
133 }
134
Paul Stewart48100b02012-03-19 07:53:52 -0700135 IPAddress peer(properties.address_family);
136 if (!properties.peer_address.empty() &&
137 !peer.SetAddressFromString(properties.peer_address)) {
138 LOG(ERROR) << "Peer address " << properties.peer_address
139 << " is invalid";
140 return;
141 }
142
Paul Stewarte78ec542012-06-08 18:28:50 -0700143 IPAddress gateway(properties.address_family);
Paul Stewart5b7ba8c2012-04-18 09:08:00 -0700144 if (!properties.gateway.empty() &&
Paul Stewarte78ec542012-06-08 18:28:50 -0700145 !gateway.SetAddressFromString(properties.gateway)) {
Paul Stewart5b7ba8c2012-04-18 09:08:00 -0700146 LOG(ERROR) << "Gateway address " << properties.peer_address
147 << " is invalid";
148 return;
149 }
150
Paul Stewarte78ec542012-06-08 18:28:50 -0700151 if (!FixGatewayReachability(&local, &peer, gateway)) {
Paul Stewart53a30382012-04-26 09:06:59 -0700152 LOG(WARNING) << "Expect limited network connectivity.";
153 }
Paul Stewart5b7ba8c2012-04-18 09:08:00 -0700154
Paul Stewart05a42c22012-08-02 16:47:21 -0700155 if (device_info_->HasOtherAddress(interface_index_, local)) {
156 // The address has changed for this interface. We need to flush
157 // everything and start over.
158 LOG(INFO) << __func__ << ": Flushing old addresses and routes.";
159 routing_table_->FlushRoutes(interface_index_);
160 device_info_->FlushAddresses(interface_index_);
161 }
162
Paul Stewarte78ec542012-06-08 18:28:50 -0700163 LOG(INFO) << __func__ << ": Installing with parameters:"
164 << " local=" << local.ToString()
165 << " broadcast=" << broadcast.ToString()
166 << " peer=" << peer.ToString()
167 << " gateway=" << gateway.ToString();
Paul Stewart48100b02012-03-19 07:53:52 -0700168 rtnl_handler_->AddInterfaceAddress(interface_index_, local, broadcast, peer);
Paul Stewartdd60e452011-08-08 11:38:36 -0700169
Paul Stewarte78ec542012-06-08 18:28:50 -0700170 if (gateway.IsValid()) {
171 routing_table_->SetDefaultRoute(interface_index_, gateway,
Paul Stewart5b7ba8c2012-04-18 09:08:00 -0700172 GetMetric(is_default_));
Paul Stewart5b7ba8c2012-04-18 09:08:00 -0700173 }
Paul Stewartdd60e452011-08-08 11:38:36 -0700174
Paul Stewart3f68bb12012-03-15 13:33:10 -0700175 // Install any explicitly configured routes at the default metric.
176 routing_table_->ConfigureRoutes(interface_index_, config, kDefaultMetric);
177
Ben Chana0163122012-09-25 15:10:52 -0700178 if (properties.blackhole_ipv6) {
179 routing_table_->CreateBlackholeRoute(interface_index_,
180 IPAddress::kFamilyIPv6,
181 kDefaultMetric);
182 }
183
Paul Stewartd62d6032012-09-11 11:35:49 -0700184 // Save a copy of the last non-null DNS config.
Paul Stewartdd60e452011-08-08 11:38:36 -0700185 if (!config->properties().dns_servers.empty()) {
186 dns_servers_ = config->properties().dns_servers;
Paul Stewartd62d6032012-09-11 11:35:49 -0700187 }
188
189 if (!config->properties().domain_search.empty()) {
Paul Stewartdd60e452011-08-08 11:38:36 -0700190 dns_domain_search_ = config->properties().domain_search;
Paul Stewartd62d6032012-09-11 11:35:49 -0700191 }
192
193 if (!config->properties().domain_name.empty()) {
194 dns_domain_name_ = config->properties().domain_name;
Paul Stewartdd60e452011-08-08 11:38:36 -0700195 }
196
Paul Stewart10241e32012-04-23 18:15:06 -0700197 ipconfig_rpc_identifier_ = config->GetRpcIdentifier();
198
Paul Stewartdd60e452011-08-08 11:38:36 -0700199 if (is_default_) {
Paul Stewart6f65c0b2012-09-11 14:57:32 -0700200 PushDNSConfig();
Paul Stewartdd60e452011-08-08 11:38:36 -0700201 }
Paul Stewart4a6748d2012-07-17 14:31:36 -0700202
203 local_ = local;
204 gateway_ = gateway;
205 has_broadcast_domain_ = !peer.IsValid();
Paul Stewartdd60e452011-08-08 11:38:36 -0700206}
207
Paul Stewartc1dec4d2011-12-08 15:25:28 -0800208void Connection::SetIsDefault(bool is_default) {
Ben Chanfad4a0b2012-04-18 15:49:59 -0700209 SLOG(Connection, 2) << __func__ << " " << interface_name_
210 << " (index " << interface_index_ << ") "
211 << is_default_ << " -> " << is_default;
Paul Stewartdd60e452011-08-08 11:38:36 -0700212 if (is_default == is_default_) {
213 return;
214 }
215
Paul Stewart7cfca042011-12-08 14:18:17 -0800216 routing_table_->SetDefaultMetric(interface_index_, GetMetric(is_default));
Paul Stewartdd60e452011-08-08 11:38:36 -0700217
Paul Stewartc681fa02012-03-02 19:40:04 -0800218 is_default_ = is_default;
219
Paul Stewartdd60e452011-08-08 11:38:36 -0700220 if (is_default) {
Paul Stewart6f65c0b2012-09-11 14:57:32 -0700221 PushDNSConfig();
Paul Stewartc681fa02012-03-02 19:40:04 -0800222 DeviceRefPtr device = device_info_->GetDevice(interface_index_);
223 if (device) {
224 device->RequestPortalDetection();
225 }
Paul Stewartdd60e452011-08-08 11:38:36 -0700226 }
Paul Stewarte78ec542012-06-08 18:28:50 -0700227 routing_table_->FlushCache();
Paul Stewartdd60e452011-08-08 11:38:36 -0700228}
229
Paul Stewart6f65c0b2012-09-11 14:57:32 -0700230void Connection::PushDNSConfig() {
231 vector<string> domain_search = dns_domain_search_;
232 if (domain_search.empty() && !dns_domain_name_.empty()) {
233 SLOG(Connection, 2) << "Setting domain search to domain name "
234 << dns_domain_name_;
235 domain_search.push_back(dns_domain_name_ + ".");
236 }
237 resolver_->SetDNSFromLists(dns_servers_, domain_search,
238 dns_timeout_parameters_);
239}
240
Paul Stewartc8f4bef2011-12-13 09:45:51 -0800241void Connection::RequestRouting() {
242 if (routing_request_count_++ == 0) {
243 DeviceRefPtr device = device_info_->GetDevice(interface_index_);
244 DCHECK(device.get());
245 if (!device.get()) {
246 LOG(ERROR) << "Device is NULL!";
247 return;
248 }
249 device->DisableReversePathFilter();
250 }
251}
252
253void Connection::ReleaseRouting() {
254 DCHECK(routing_request_count_ > 0);
255 if (--routing_request_count_ == 0) {
256 DeviceRefPtr device = device_info_->GetDevice(interface_index_);
257 DCHECK(device.get());
258 if (!device.get()) {
259 LOG(ERROR) << "Device is NULL!";
260 return;
261 }
262 device->EnableReversePathFilter();
263
264 // Clear any cached routes that might have accumulated while reverse-path
265 // filtering was disabled.
266 routing_table_->FlushCache();
267 }
268}
269
Paul Stewartf748a362012-03-07 12:01:20 -0800270bool Connection::RequestHostRoute(const IPAddress &address) {
271 // Set the prefix to be the entire address size.
272 IPAddress address_prefix(address);
273 address_prefix.set_prefix(address_prefix.GetLength() * 8);
274
Darin Petkov13e6d552012-05-09 14:22:23 +0200275 // Do not set interface_index_ since this may not be the default route through
276 // which this destination can be found. However, we should tag the created
277 // route with our interface index so we can clean this route up when this
278 // connection closes. Also, add route query callback to determine the lower
279 // connection and bind to it.
280 if (!routing_table_->RequestRouteToHost(
281 address_prefix,
282 -1,
283 interface_index_,
Darin Petkov5eb05422012-05-11 15:45:25 +0200284 Bind(&Connection::OnRouteQueryResponse,
285 weak_ptr_factory_.GetWeakPtr()))) {
Paul Stewartf748a362012-03-07 12:01:20 -0800286 LOG(ERROR) << "Could not request route to " << address.ToString();
287 return false;
288 }
289
290 return true;
291}
292
Paul Stewart5b7ba8c2012-04-18 09:08:00 -0700293// static
Paul Stewart53a30382012-04-26 09:06:59 -0700294bool Connection::FixGatewayReachability(IPAddress *local,
Paul Stewart49258292012-05-26 06:37:14 -0700295 IPAddress *peer,
296 const IPAddress &gateway) {
Paul Stewart53a30382012-04-26 09:06:59 -0700297 if (!gateway.IsValid()) {
298 LOG(WARNING) << "No gateway address was provided for this connection.";
299 return false;
300 }
301
Paul Stewart49258292012-05-26 06:37:14 -0700302 if (peer->IsValid()) {
303 if (gateway.Equals(*peer)) {
Paul Stewart53a30382012-04-26 09:06:59 -0700304 return true;
305 }
306 LOG(WARNING) << "Gateway address "
307 << gateway.ToString()
308 << " does not match peer address "
Paul Stewart49258292012-05-26 06:37:14 -0700309 << peer->ToString();
Paul Stewart53a30382012-04-26 09:06:59 -0700310 return false;
311 }
312
313 if (local->CanReachAddress(gateway)) {
314 return true;
Paul Stewart5b7ba8c2012-04-18 09:08:00 -0700315 }
316
317 LOG(WARNING) << "Gateway "
318 << gateway.ToString()
319 << " is unreachable from local address/prefix "
320 << local->ToString() << "/" << local->prefix();
321
Paul Stewart2aa5d7d2012-06-21 22:16:54 -0700322 bool found_new_prefix = false;
Paul Stewart5b7ba8c2012-04-18 09:08:00 -0700323 size_t original_prefix = local->prefix();
Paul Stewart2aa5d7d2012-06-21 22:16:54 -0700324 // Only try to expand the netmask if the configured prefix is
325 // less than "all ones". This special-cases the "all-ones"
326 // prefix as a forced conversion to point-to-point networking.
327 if (local->prefix() < IPAddress::GetMaxPrefixLength(local->family())) {
328 size_t prefix = original_prefix - 1;
329 for (; prefix >= local->GetMinPrefixLength(); --prefix) {
330 local->set_prefix(prefix);
331 if (local->CanReachAddress(gateway)) {
332 found_new_prefix = true;
333 break;
334 }
Paul Stewart5b7ba8c2012-04-18 09:08:00 -0700335 }
336 }
337
Paul Stewart2aa5d7d2012-06-21 22:16:54 -0700338 if (!found_new_prefix) {
Paul Stewart5b7ba8c2012-04-18 09:08:00 -0700339 // Restore the original prefix since we cannot find a better one.
340 local->set_prefix(original_prefix);
Paul Stewart49258292012-05-26 06:37:14 -0700341 DCHECK(!peer->IsValid());
342 LOG(WARNING) << "Assuming point-to-point configuration.";
343 *peer = gateway;
344 return true;
Paul Stewart5b7ba8c2012-04-18 09:08:00 -0700345 }
Paul Stewart53a30382012-04-26 09:06:59 -0700346
Paul Stewart2aa5d7d2012-06-21 22:16:54 -0700347 LOG(WARNING) << "Mitigating this by setting local prefix to "
348 << local->prefix();
Paul Stewart53a30382012-04-26 09:06:59 -0700349 return true;
Paul Stewart5b7ba8c2012-04-18 09:08:00 -0700350}
351
Paul Stewart7cfca042011-12-08 14:18:17 -0800352uint32 Connection::GetMetric(bool is_default) {
353 // If this is not the default route, assign a metric based on the interface
354 // index. This way all non-default routes (even to the same gateway IP) end
355 // up with unique metrics so they do not collide.
356 return is_default ? kDefaultMetric : kNonDefaultMetricBase + interface_index_;
357}
358
Paul Stewarte93b0382012-04-24 13:11:28 -0700359bool Connection::PinHostRoute(const IPConfig::Properties &properties) {
360 SLOG(Connection, 2) << __func__;
361 if (properties.gateway.empty() || properties.trusted_ip.empty()) {
Darin Petkove8587e32012-07-02 13:56:07 +0200362 LOG_IF(ERROR, properties.gateway.empty())
363 << "No gateway -- unable to pin host route.";
364 LOG_IF(ERROR, properties.trusted_ip.empty())
365 << "No trusted IP -- unable to pin host route.";
Paul Stewarte93b0382012-04-24 13:11:28 -0700366 return false;
367 }
368
369 IPAddress trusted_ip(properties.address_family);
370 if (!trusted_ip.SetAddressFromString(properties.trusted_ip)) {
371 LOG(ERROR) << "Failed to parse trusted_ip "
372 << properties.trusted_ip << "; ignored.";
373 return false;
374 }
375
376 return RequestHostRoute(trusted_ip);
377}
378
Darin Petkov13e6d552012-05-09 14:22:23 +0200379void Connection::OnRouteQueryResponse(int interface_index,
380 const RoutingTableEntry &entry) {
381 SLOG(Connection, 2) << __func__ << "(" << interface_index << ", "
Darin Petkov5eb05422012-05-11 15:45:25 +0200382 << entry.tag << ")" << " @ " << interface_name_;
Darin Petkov13e6d552012-05-09 14:22:23 +0200383 lower_binder_.Attach(NULL);
384 DeviceRefPtr device = device_info_->GetDevice(interface_index);
385 if (!device) {
386 LOG(ERROR) << "Unable to lookup device for index " << interface_index;
387 return;
388 }
389 ConnectionRefPtr connection = device->connection();
390 if (!connection) {
391 LOG(ERROR) << "Device " << interface_index << " has no connection.";
392 return;
393 }
394 lower_binder_.Attach(connection);
Paul Stewart4a6748d2012-07-17 14:31:36 -0700395 connection->CreateGatewayRoute();
396}
397
398bool Connection::CreateGatewayRoute() {
399 // Ensure that the gateway for the lower connection remains reachable,
400 // since we may create routes that conflict with it.
401 if (!has_broadcast_domain_) {
402 return false;
403 }
404 // It is not worth keeping track of this route, since it is benign,
405 // and only pins persistent state that was already true of the connection.
406 // If DHCP parameters change later (without the connection having been
407 // destroyed and recreated), the binding processes will likely terminate
408 // and restart, causing a new link route to be created.
409 return routing_table_->CreateLinkRoute(interface_index_, local_, gateway_);
Darin Petkov13e6d552012-05-09 14:22:23 +0200410}
411
412void Connection::OnLowerDisconnect() {
Darin Petkov5eb05422012-05-11 15:45:25 +0200413 SLOG(Connection, 2) << __func__ << " @ " << interface_name_;
Darin Petkov13e6d552012-05-09 14:22:23 +0200414 // Ensures that |this| instance doesn't get destroyed in the middle of
415 // notifying the binders. This method needs to be separate from
416 // NotifyBindersOnDisconnect because the latter may be invoked by Connection's
417 // destructor when |this| instance's reference count is already 0.
418 ConnectionRefPtr connection(this);
419 connection->NotifyBindersOnDisconnect();
420}
421
422void Connection::NotifyBindersOnDisconnect() {
423 // Note that this method may be invoked by the destructor.
Darin Petkov5eb05422012-05-11 15:45:25 +0200424 SLOG(Connection, 2) << __func__ << " @ " << interface_name_;
Darin Petkov13e6d552012-05-09 14:22:23 +0200425
426 // Unbinds the lower connection before notifying the binders. This ensures
427 // correct behavior in case of circular binding.
428 lower_binder_.Attach(NULL);
429 while (!binders_.empty()) {
430 // Pop the binder first and then notify it to ensure that each binder is
431 // notified only once.
432 Binder *binder = binders_.front();
433 binders_.pop_front();
434 binder->OnDisconnect();
435 }
436}
437
438void Connection::AttachBinder(Binder *binder) {
Darin Petkov5eb05422012-05-11 15:45:25 +0200439 SLOG(Connection, 2) << __func__ << "(" << binder->name() << ")" << " @ "
440 << interface_name_;
Darin Petkov13e6d552012-05-09 14:22:23 +0200441 binders_.push_back(binder);
442}
443
444void Connection::DetachBinder(Binder *binder) {
Darin Petkov5eb05422012-05-11 15:45:25 +0200445 SLOG(Connection, 2) << __func__ << "(" << binder->name() << ")" << " @ "
446 << interface_name_;
Darin Petkov13e6d552012-05-09 14:22:23 +0200447 for (deque<Binder *>::iterator it = binders_.begin();
448 it != binders_.end(); ++it) {
449 if (binder == *it) {
450 binders_.erase(it);
451 return;
452 }
453 }
454}
455
Paul Stewartdd60e452011-08-08 11:38:36 -0700456} // namespace shill