blob: 49bc8ffc3985d44ef766f5f7afd413fee886ef75 [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
52 // Somehow we got destroyed before the client died, most likely at exit. Make
53 // sure we don't get any callbacks to the destroyed instance.
54 if (child_watch_tag_) {
55 glib_->SourceRemove(child_watch_tag_);
56 child_watch_tag_ = 0;
57 }
58 if (pid_) {
59 glib_->SpawnClosePID(pid_);
60 pid_ = 0;
61 }
62 CleanupClientState();
Darin Petkovd1b715b2011-06-02 21:21:22 -070063}
64
Darin Petkov92c43902011-06-09 20:46:06 -070065bool DHCPConfig::RequestIP() {
Darin Petkovd1b715b2011-06-02 21:21:22 -070066 VLOG(2) << __func__ << ": " << GetDeviceName();
67 if (!pid_) {
68 return Start();
69 }
70 if (!proxy_.get()) {
71 LOG(ERROR)
72 << "Unable to acquire destination address before receiving request.";
73 return false;
74 }
Darin Petkov92c43902011-06-09 20:46:06 -070075 return RenewIP();
Darin Petkovd1b715b2011-06-02 21:21:22 -070076}
77
Darin Petkov92c43902011-06-09 20:46:06 -070078bool DHCPConfig::RenewIP() {
Darin Petkovd1b715b2011-06-02 21:21:22 -070079 VLOG(2) << __func__ << ": " << GetDeviceName();
80 if (!pid_ || !proxy_.get()) {
81 return false;
82 }
83 proxy_->DoRebind(GetDeviceName());
84 return true;
85}
86
Darin Petkov92c43902011-06-09 20:46:06 -070087bool DHCPConfig::ReleaseIP() {
88 VLOG(2) << __func__ << ": " << GetDeviceName();
89 // TODO(petkov): Implement D-Bus calls to Release and stop the process.
90}
91
Darin Petkovd1b715b2011-06-02 21:21:22 -070092void DHCPConfig::InitProxy(DBus::Connection *connection, const char *service) {
93 if (!proxy_.get()) {
94 proxy_.reset(new DHCPCDProxy(connection, service));
95 }
96}
97
Darin Petkove7cb7f82011-06-03 13:21:51 -070098void DHCPConfig::ProcessEventSignal(const std::string &reason,
99 const Configuration &configuration) {
100 LOG(INFO) << "Event reason: " << reason;
101
102 IPConfig::Properties properties;
103 if (!ParseConfiguration(configuration, &properties)) {
104 LOG(ERROR) << "Unable to parse the new DHCP configuration -- ignored.";
105 return;
106 }
107 UpdateProperties(properties);
108}
109
Darin Petkovd1b715b2011-06-02 21:21:22 -0700110bool DHCPConfig::Start() {
111 VLOG(2) << __func__ << ": " << GetDeviceName();
112
113 char *argv[4], *envp[1];
114 argv[0] = const_cast<char *>(kDHCPCDPath);
115 argv[1] = const_cast<char *>("-B"); // foreground
116 argv[2] = const_cast<char *>(GetDeviceName().c_str());
117 argv[3] = NULL;
118
119 envp[0] = NULL;
120
121 GPid pid = 0;
Darin Petkovf7897bc2011-06-08 17:13:36 -0700122 if (!glib_->SpawnAsync(NULL,
123 argv,
124 envp,
125 G_SPAWN_DO_NOT_REAP_CHILD,
126 NULL,
127 NULL,
128 &pid,
129 NULL)) {
Darin Petkovd1b715b2011-06-02 21:21:22 -0700130 LOG(ERROR) << "Unable to spawn " << kDHCPCDPath;
131 return false;
132 }
133 pid_ = pid;
134 LOG(INFO) << "Spawned " << kDHCPCDPath << " with pid: " << pid_;
Darin Petkovf7897bc2011-06-08 17:13:36 -0700135 provider_->BindPID(pid_, this);
Darin Petkov92c43902011-06-09 20:46:06 -0700136 child_watch_tag_ = glib_->ChildWatchAdd(pid, ChildWatchCallback, this);
Darin Petkovd1b715b2011-06-02 21:21:22 -0700137 return true;
Darin Petkov50308cd2011-06-01 18:25:07 -0700138}
139
Darin Petkov92c43902011-06-09 20:46:06 -0700140void DHCPConfig::Stop() {
141 if (pid_) {
142 VLOG(2) << "Terminating " << pid_;
143 PLOG_IF(ERROR, kill(pid_, SIGTERM) < 0);
144 }
145}
146
Darin Petkove7cb7f82011-06-03 13:21:51 -0700147string DHCPConfig::GetIPv4AddressString(unsigned int address) {
148 char str[INET_ADDRSTRLEN];
149 if (inet_ntop(AF_INET, &address, str, arraysize(str))) {
150 return str;
151 }
152 LOG(ERROR) << "Unable to convert IPv4 address to string: " << address;
153 return "";
154}
155
156bool DHCPConfig::ParseConfiguration(const Configuration& configuration,
157 IPConfig::Properties *properties) {
158 VLOG(2) << __func__;
159 for (Configuration::const_iterator it = configuration.begin();
160 it != configuration.end(); ++it) {
161 const string &key = it->first;
162 const DBus::Variant &value = it->second;
163 VLOG(2) << "Processing key: " << key;
164 if (key == kConfigurationKeyIPAddress) {
165 properties->address = GetIPv4AddressString(value.reader().get_uint32());
166 if (properties->address.empty()) {
167 return false;
168 }
169 } else if (key == kConfigurationKeySubnetCIDR) {
170 properties->subnet_cidr = value.reader().get_byte();
171 } else if (key == kConfigurationKeyBroadcastAddress) {
172 properties->broadcast_address =
173 GetIPv4AddressString(value.reader().get_uint32());
174 if (properties->broadcast_address.empty()) {
175 return false;
176 }
177 } else if (key == kConfigurationKeyRouters) {
Darin Petkovf7897bc2011-06-08 17:13:36 -0700178 vector<unsigned int> routers = value.operator vector<unsigned int>();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700179 if (routers.empty()) {
180 LOG(ERROR) << "No routers provided.";
181 return false;
182 }
183 properties->gateway = GetIPv4AddressString(routers[0]);
184 if (properties->gateway.empty()) {
185 return false;
186 }
187 } else if (key == kConfigurationKeyDNS) {
Darin Petkovf7897bc2011-06-08 17:13:36 -0700188 vector<unsigned int> servers = value.operator vector<unsigned int>();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700189 for (vector<unsigned int>::const_iterator it = servers.begin();
190 it != servers.end(); ++it) {
191 string server = GetIPv4AddressString(*it);
192 if (server.empty()) {
193 return false;
194 }
195 properties->dns_servers.push_back(server);
196 }
197 } else if (key == kConfigurationKeyDomainName) {
198 properties->domain_name = value.reader().get_string();
199 } else if (key == kConfigurationKeyDomainSearch) {
Darin Petkovf7897bc2011-06-08 17:13:36 -0700200 properties->domain_search = value.operator vector<string>();
Darin Petkove7cb7f82011-06-03 13:21:51 -0700201 } else if (key == kConfigurationKeyMTU) {
202 int mtu = value.reader().get_uint16();
203 if (mtu >= 576) {
204 properties->mtu = mtu;
205 }
206 } else {
207 VLOG(2) << "Key ignored.";
208 }
209 }
210 return true;
211}
212
Darin Petkov92c43902011-06-09 20:46:06 -0700213void DHCPConfig::ChildWatchCallback(GPid pid, gint status, gpointer data) {
214 VLOG(2) << "pid " << pid << " exit status " << status;
215 DHCPConfig *config = reinterpret_cast<DHCPConfig *>(data);
216 config->child_watch_tag_ = 0;
217
218 CHECK_EQ(pid, config->pid_);
219 config->glib_->SpawnClosePID(pid);
220 config->pid_ = 0;
221
222 config->CleanupClientState();
223
224 // |config| instance may be destroyed after this call.
225 config->provider_->UnbindPID(pid);
226}
227
228void DHCPConfig::CleanupClientState() {
229 file_util::Delete(root_.Append(base::StringPrintf(kDHCPCDPathFormatLease,
230 GetDeviceName().c_str())),
231 false);
232 file_util::Delete(root_.Append(base::StringPrintf(kDHCPCDPathFormatPID,
233 GetDeviceName().c_str())),
234 false);
235}
236
Darin Petkov50308cd2011-06-01 18:25:07 -0700237} // namespace shill