blob: 5f7f6f8c4908ff78bb79440345014ef9f00cfd20 [file] [log] [blame]
Peter Qiubf8e36c2014-12-03 22:59:45 -08001// Copyright 2014 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 "apmanager/dhcp_server.h"
6
7#include <net/if.h>
8#include <signal.h>
9
10#include <base/files/file_util.h>
11#include <base/strings/stringprintf.h>
12
13using std::string;
14
15namespace apmanager {
16
17// static.
18const char DHCPServer::kDnsmasqPath[] = "/usr/sbin/dnsmasq";
19const char DHCPServer::kDnsmasqConfigFilePathFormat[] = "/tmp/dhcpd-%d.conf";
20const char DHCPServer::kDHCPLeasesFilePathFormat[] = "/tmp/dhcpd-%d.leases";
21const char DHCPServer::kServerAddressFormat[] = "192.168.%d.254";
22const char DHCPServer::kAddressRangeLowFormat[] = "192.168.%d.1";
23const char DHCPServer::kAddressRangeHighFormat[] = "192.168.%d.128";
24const int DHCPServer::kServerAddressPrefix = 24;
25const int DHCPServer::kTerminationTimeoutSeconds = 2;
26
27DHCPServer::DHCPServer(uint16_t server_address_index,
28 const string& interface_name)
29 : server_address_index_(server_address_index),
30 interface_name_(interface_name),
31 server_address_(shill::IPAddress::kFamilyIPv4),
32 rtnl_handler_(shill::RTNLHandler::GetInstance()) {}
33
34DHCPServer::~DHCPServer() {
35 if (dnsmasq_process_) {
36 // The destructor of the Process will send a SIGKILL signal if it is not
37 // already terminated.
38 dnsmasq_process_->Kill(SIGTERM, kTerminationTimeoutSeconds);
39 dnsmasq_process_.reset();
40 rtnl_handler_->RemoveInterfaceAddress(
41 rtnl_handler_->GetInterfaceIndex(interface_name_), server_address_);
42 }
43}
44
45bool DHCPServer::Start() {
46 if (dnsmasq_process_) {
47 LOG(ERROR) << "DHCP Server already running";
48 return false;
49 }
50
51 // Generate dnsmasq config file.
52 string config_str = GenerateConfigFile();
53 base::FilePath file_path(base::StringPrintf(kDnsmasqConfigFilePathFormat,
54 server_address_index_));
55 if (base::WriteFile(file_path, config_str.c_str(), config_str.size()) == -1) {
56 LOG(ERROR) << "Failed to write configuration to a file";
57 return false;
58 }
59
60 // Setup local server address and bring up the interface in case it is down.
61 server_address_.SetAddressFromString(
62 base::StringPrintf(kServerAddressFormat, server_address_index_));
63 server_address_.set_prefix(kServerAddressPrefix);
64 int interface_index = rtnl_handler_->GetInterfaceIndex(interface_name_);
65 rtnl_handler_->AddInterfaceAddress(
66 interface_index,
67 server_address_,
68 server_address_.GetDefaultBroadcast(),
69 shill::IPAddress(shill::IPAddress::kFamilyIPv4));
70 rtnl_handler_->SetInterfaceFlags(interface_index, IFF_UP, IFF_UP);
71
72 // TODO(zqiu): use ProcessFactory for better unit testing.
73 // Start a dnsmasq process.
74 dnsmasq_process_.reset(new chromeos::ProcessImpl());
75 dnsmasq_process_->AddArg(kDnsmasqPath);
76 dnsmasq_process_->AddArg(base::StringPrintf("--conf-file=%s",
77 file_path.value().c_str()));
78 if (!dnsmasq_process_->Start()) {
79 rtnl_handler_->RemoveInterfaceAddress(interface_index, server_address_);
80 dnsmasq_process_.reset();
81 LOG(ERROR) << "Failed to start dnsmasq process";
82 return false;
83 }
84
85 return true;
86}
87
88string DHCPServer::GenerateConfigFile() {
89 string server_address = base::StringPrintf(kServerAddressFormat,
90 server_address_index_);
91 string address_low = base::StringPrintf(kAddressRangeLowFormat,
92 server_address_index_);
93 string address_high = base::StringPrintf(kAddressRangeHighFormat,
94 server_address_index_);
95 string lease_file_path = base::StringPrintf(kDHCPLeasesFilePathFormat,
96 server_address_index_);
97 string config;
98 config += "port=0\n";
99 config += "bind-interfaces\n";
100 config += "log-dhcp\n";
101 base::StringAppendF(
102 &config, "dhcp-range=%s,%s\n", address_low.c_str(), address_high.c_str());
103 base::StringAppendF(&config, "interface=%s\n", interface_name_.c_str());
104 base::StringAppendF(&config, "dhcp-leasefile=%s\n", lease_file_path.c_str());
105 return config;
106}
107
108} // namespace apmanager