blob: ffe219b6b24ca614f6493865c73c3ef9169bb124 [file] [log] [blame]
Darin Petkov50308cd2011-06-01 18:25:07 -07001// Copyright (c) 2011 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
5#include "shill/dhcp_config.h"
6
Darin Petkove7cb7f82011-06-03 13:21:51 -07007#include <arpa/inet.h>
8
Darin Petkov92c43902011-06-09 20:46:06 -07009#include <base/file_util.h>
Darin Petkov50308cd2011-06-01 18:25:07 -070010#include <base/logging.h>
Darin Petkov92c43902011-06-09 20:46:06 -070011#include <base/stringprintf.h>
Darin Petkovd1b715b2011-06-02 21:21:22 -070012
13#include "shill/dhcpcd_proxy.h"
14#include "shill/dhcp_provider.h"
Darin Petkovf7897bc2011-06-08 17:13:36 -070015#include "shill/glib_interface.h"
Darin Petkov50308cd2011-06-01 18:25:07 -070016
Darin Petkove7cb7f82011-06-03 13:21:51 -070017using std::string;
18using std::vector;
19
Darin Petkov50308cd2011-06-01 18:25:07 -070020namespace shill {
21
Darin Petkove7cb7f82011-06-03 13:21:51 -070022const char DHCPConfig::kConfigurationKeyBroadcastAddress[] = "BroadcastAddress";
23const char DHCPConfig::kConfigurationKeyDNS[] = "DomainNameServers";
24const char DHCPConfig::kConfigurationKeyDomainName[] = "DomainName";
25const char DHCPConfig::kConfigurationKeyDomainSearch[] = "DomainSearch";
26const char DHCPConfig::kConfigurationKeyIPAddress[] = "IPAddress";
27const char DHCPConfig::kConfigurationKeyMTU[] = "InterfaceMTU";
28const char DHCPConfig::kConfigurationKeyRouters[] = "Routers";
29const char DHCPConfig::kConfigurationKeySubnetCIDR[] = "SubnetCIDR";
Darin Petkovd1b715b2011-06-02 21:21:22 -070030const char DHCPConfig::kDHCPCDPath[] = "/sbin/dhcpcd";
Darin Petkov92c43902011-06-09 20:46:06 -070031const char DHCPConfig::kDHCPCDPathFormatLease[] = "var/run/dhcpcd-%s.lease";
32const char DHCPConfig::kDHCPCDPathFormatPID[] = "var/run/dhcpcd-%s.pid";
Darin Petkove7cb7f82011-06-03 13:21:51 -070033
Darin Petkovf7897bc2011-06-08 17:13:36 -070034DHCPConfig::DHCPConfig(DHCPProvider *provider,
35 DeviceConstRefPtr device,
36 GLibInterface *glib)
Darin Petkovd1b715b2011-06-02 21:21:22 -070037 : IPConfig(device),
38 provider_(provider),
Darin Petkovf7897bc2011-06-08 17:13:36 -070039 pid_(0),
Darin Petkov92c43902011-06-09 20:46:06 -070040 child_watch_tag_(0),
41 root_("/"),
Darin Petkovf7897bc2011-06-08 17:13:36 -070042 glib_(glib) {
Darin Petkovd1b715b2011-06-02 21:21:22 -070043 VLOG(2) << __func__ << ": " << GetDeviceName();
Darin Petkov50308cd2011-06-01 18:25:07 -070044}
45
46DHCPConfig::~DHCPConfig() {
Darin Petkovd1b715b2011-06-02 21:21:22 -070047 VLOG(2) << __func__ << ": " << GetDeviceName();
Darin Petkov92c43902011-06-09 20:46:06 -070048
49 // Don't leave behind dhcpcd running.
50 Stop();
51
Darin Petkov98dd6a02011-06-10 15:12:57 -070052 // Make sure we don't get any callbacks to the destroyed instance.
Darin Petkov92c43902011-06-09 20:46:06 -070053 CleanupClientState();
Darin Petkovd1b715b2011-06-02 21:21:22 -070054}
55
Darin Petkov92c43902011-06-09 20:46:06 -070056bool DHCPConfig::RequestIP() {
Darin Petkovd1b715b2011-06-02 21:21:22 -070057 VLOG(2) << __func__ << ": " << GetDeviceName();
58 if (!pid_) {
59 return Start();
60 }
61 if (!proxy_.get()) {
Darin Petkov98dd6a02011-06-10 15:12:57 -070062 LOG(ERROR) << "Unable to request IP before acquiring destination.";
63 return Restart();
Darin Petkovd1b715b2011-06-02 21:21:22 -070064 }
Darin Petkov92c43902011-06-09 20:46:06 -070065 return RenewIP();
Darin Petkovd1b715b2011-06-02 21:21:22 -070066}
67
Darin Petkov92c43902011-06-09 20:46:06 -070068bool DHCPConfig::RenewIP() {
Darin Petkovd1b715b2011-06-02 21:21:22 -070069 VLOG(2) << __func__ << ": " << GetDeviceName();
Darin Petkov98dd6a02011-06-10 15:12:57 -070070 if (!pid_) {
71 return false;
72 }
73 if (!proxy_.get()) {
74 LOG(ERROR) << "Unable to renew IP before acquiring destination.";
Darin Petkovd1b715b2011-06-02 21:21:22 -070075 return false;
76 }
77 proxy_->DoRebind(GetDeviceName());
78 return true;
79}
80
Darin Petkov92c43902011-06-09 20:46:06 -070081bool DHCPConfig::ReleaseIP() {
82 VLOG(2) << __func__ << ": " << GetDeviceName();
Darin Petkov98dd6a02011-06-10 15:12:57 -070083 if (!pid_) {
84 return true;
85 }
86 if (!proxy_.get()) {
87 LOG(ERROR) << "Unable to release IP before acquiring destination.";
88 return false;
89 }
90 proxy_->DoRelease(GetDeviceName());
91 Stop();
92 return true;
Darin Petkov92c43902011-06-09 20:46:06 -070093}
94
Darin Petkovd1b715b2011-06-02 21:21:22 -070095void DHCPConfig::InitProxy(DBus::Connection *connection, const char *service) {
96 if (!proxy_.get()) {
97 proxy_.reset(new DHCPCDProxy(connection, service));
98 }
99}
100
Darin Petkove7cb7f82011-06-03 13:21:51 -0700101void DHCPConfig::ProcessEventSignal(const std::string &reason,
102 const Configuration &configuration) {
103 LOG(INFO) << "Event reason: " << reason;
104
105 IPConfig::Properties properties;
106 if (!ParseConfiguration(configuration, &properties)) {
107 LOG(ERROR) << "Unable to parse the new DHCP configuration -- ignored.";
108 return;
109 }
110 UpdateProperties(properties);
111}
112
Darin Petkovd1b715b2011-06-02 21:21:22 -0700113bool DHCPConfig::Start() {
114 VLOG(2) << __func__ << ": " << GetDeviceName();
115
Darin Petkov98dd6a02011-06-10 15:12:57 -0700116 char *argv[4] = {
117 const_cast<char *>(kDHCPCDPath),
118 const_cast<char *>("-B"), // foreground
119 const_cast<char *>(GetDeviceName().c_str()),
120 NULL
121 };
122 char *envp[1] = { NULL };
Darin Petkovd1b715b2011-06-02 21:21:22 -0700123
Darin Petkov98dd6a02011-06-10 15:12:57 -0700124 CHECK(!pid_);
Darin Petkovf7897bc2011-06-08 17:13:36 -0700125 if (!glib_->SpawnAsync(NULL,
126 argv,
127 envp,
128 G_SPAWN_DO_NOT_REAP_CHILD,
129 NULL,
130 NULL,
Darin Petkov98dd6a02011-06-10 15:12:57 -0700131 &pid_,
Darin Petkovf7897bc2011-06-08 17:13:36 -0700132 NULL)) {
Darin Petkovd1b715b2011-06-02 21:21:22 -0700133 LOG(ERROR) << "Unable to spawn " << kDHCPCDPath;
134 return false;
135 }
Darin Petkovd1b715b2011-06-02 21:21:22 -0700136 LOG(INFO) << "Spawned " << kDHCPCDPath << " with pid: " << pid_;
Darin Petkovf7897bc2011-06-08 17:13:36 -0700137 provider_->BindPID(pid_, this);
Darin Petkov98dd6a02011-06-10 15:12:57 -0700138 CHECK(!child_watch_tag_);
139 child_watch_tag_ = glib_->ChildWatchAdd(pid_, ChildWatchCallback, this);
Darin Petkovd1b715b2011-06-02 21:21:22 -0700140 return true;
Darin Petkov50308cd2011-06-01 18:25:07 -0700141}
142
Darin Petkov92c43902011-06-09 20:46:06 -0700143void DHCPConfig::Stop() {
144 if (pid_) {
145 VLOG(2) << "Terminating " << pid_;
146 PLOG_IF(ERROR, kill(pid_, SIGTERM) < 0);
147 }
148}
149
Darin Petkov98dd6a02011-06-10 15:12:57 -0700150bool DHCPConfig::Restart() {
151 // Check to ensure that this instance doesn't get destroyed in the middle of
152 // this call. If stopping a running client while there's only one reference to
153 // this instance, we will end up destroying it when the PID is unbound from
154 // the Provider. Since the Provider doesn't invoke Restart, this would mean
155 // that Restart was erroneously executed through a bare reference.
156 CHECK(!pid_ || !HasOneRef());
157 Stop();
158 if (pid_) {
159 provider_->UnbindPID(pid_);
160 }
161 CleanupClientState();
162 return Start();
163}
164
Darin Petkove7cb7f82011-06-03 13:21:51 -0700165string DHCPConfig::GetIPv4AddressString(unsigned int address) {
166 char str[INET_ADDRSTRLEN];
167 if (inet_ntop(AF_INET, &address, str, arraysize(str))) {
168 return str;
169 }
170 LOG(ERROR) << "Unable to convert IPv4 address to string: " << address;
171 return "";
172}
173
174bool DHCPConfig::ParseConfiguration(const Configuration& configuration,
175 IPConfig::Properties *properties) {
176 VLOG(2) << __func__;
177 for (Configuration::const_iterator it = configuration.begin();
178 it != configuration.end(); ++it) {
179 const string &key = it->first;
180 const DBus::Variant &value = it->second;
181 VLOG(2) << "Processing key: " << key;
182 if (key == kConfigurationKeyIPAddress) {
183 properties->address = GetIPv4AddressString(value.reader().get_uint32());
184 if (properties->address.empty()) {
185 return false;
186 }
187 } else if (key == kConfigurationKeySubnetCIDR) {
188 properties->subnet_cidr = value.reader().get_byte();
189 } else if (key == kConfigurationKeyBroadcastAddress) {
190 properties->broadcast_address =
191 GetIPv4AddressString(value.reader().get_uint32());
192 if (properties->broadcast_address.empty()) {
193 return false;
194 }
195 } else if (key == kConfigurationKeyRouters) {
Darin Petkovf7897bc2011-06-08 17:13:36 -0700196 vector<unsigned int> routers = value.operator vector<unsigned int>();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700197 if (routers.empty()) {
198 LOG(ERROR) << "No routers provided.";
199 return false;
200 }
201 properties->gateway = GetIPv4AddressString(routers[0]);
202 if (properties->gateway.empty()) {
203 return false;
204 }
205 } else if (key == kConfigurationKeyDNS) {
Darin Petkovf7897bc2011-06-08 17:13:36 -0700206 vector<unsigned int> servers = value.operator vector<unsigned int>();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700207 for (vector<unsigned int>::const_iterator it = servers.begin();
208 it != servers.end(); ++it) {
209 string server = GetIPv4AddressString(*it);
210 if (server.empty()) {
211 return false;
212 }
213 properties->dns_servers.push_back(server);
214 }
215 } else if (key == kConfigurationKeyDomainName) {
216 properties->domain_name = value.reader().get_string();
217 } else if (key == kConfigurationKeyDomainSearch) {
Darin Petkovf7897bc2011-06-08 17:13:36 -0700218 properties->domain_search = value.operator vector<string>();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700219 } else if (key == kConfigurationKeyMTU) {
220 int mtu = value.reader().get_uint16();
221 if (mtu >= 576) {
222 properties->mtu = mtu;
223 }
224 } else {
225 VLOG(2) << "Key ignored.";
226 }
227 }
228 return true;
229}
230
Darin Petkov92c43902011-06-09 20:46:06 -0700231void DHCPConfig::ChildWatchCallback(GPid pid, gint status, gpointer data) {
232 VLOG(2) << "pid " << pid << " exit status " << status;
233 DHCPConfig *config = reinterpret_cast<DHCPConfig *>(data);
234 config->child_watch_tag_ = 0;
235
236 CHECK_EQ(pid, config->pid_);
237 config->glib_->SpawnClosePID(pid);
238 config->pid_ = 0;
239
240 config->CleanupClientState();
241
242 // |config| instance may be destroyed after this call.
243 config->provider_->UnbindPID(pid);
244}
245
246void DHCPConfig::CleanupClientState() {
Darin Petkov98dd6a02011-06-10 15:12:57 -0700247 if (child_watch_tag_) {
248 glib_->SourceRemove(child_watch_tag_);
249 child_watch_tag_ = 0;
250 }
251 if (pid_) {
252 glib_->SpawnClosePID(pid_);
253 pid_ = 0;
254 }
Darin Petkov92c43902011-06-09 20:46:06 -0700255 file_util::Delete(root_.Append(base::StringPrintf(kDHCPCDPathFormatLease,
256 GetDeviceName().c_str())),
257 false);
258 file_util::Delete(root_.Append(base::StringPrintf(kDHCPCDPathFormatPID,
259 GetDeviceName().c_str())),
260 false);
261}
262
Darin Petkov50308cd2011-06-01 18:25:07 -0700263} // namespace shill