• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  /*
2   * Copyright 2008, The Android Open Source Project
3   *
4   * Licensed under the Apache License, Version 2.0 (the "License");
5   * you may not use this file except in compliance with the License.
6   * You may obtain a copy of the License at
7   *
8   *     http://www.apache.org/licenses/LICENSE-2.0
9   *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  /* Utilities for managing the dhcpcd DHCP client daemon */
18  
19  #include <stdio.h>
20  #include <stdlib.h>
21  #include <string.h>
22  #include <unistd.h>
23  #include <arpa/inet.h>
24  #include <netinet/in.h>
25  
26  #include <cutils/properties.h>
27  
28  static const char DAEMON_NAME[]        = "dhcpcd";
29  static const char DAEMON_PROP_NAME[]   = "init.svc.dhcpcd";
30  static const char HOSTNAME_PROP_NAME[] = "net.hostname";
31  static const char DHCP_PROP_NAME_PREFIX[]  = "dhcp";
32  static const char DHCP_CONFIG_PATH[]   = "/system/etc/dhcpcd/dhcpcd.conf";
33  static const int NAP_TIME = 200;   /* wait for 200ms at a time */
34                                    /* when polling for property values */
35  static const char DAEMON_NAME_RENEW[]  = "iprenew";
36  static char errmsg[100];
37  /* interface length for dhcpcd daemon start (dhcpcd_<interface> as defined in init.rc file)
38   * or for filling up system properties dhcpcd.<interface>.ipaddress, dhcpcd.<interface>.dns1
39   * and other properties on a successful bind
40   */
41  #define MAX_INTERFACE_LENGTH 25
42  
43  /*
44   * P2p interface names increase sequentially p2p-p2p0-1, p2p-p2p0-2.. after
45   * group formation. This does not work well with system properties which can quickly
46   * exhaust or for specifiying a dhcp start target in init which requires
47   * interface to be pre-defined in init.rc file.
48   *
49   * This function returns a common string p2p for all p2p interfaces.
50   */
get_p2p_interface_replacement(const char * interface,char * p2p_interface)51  void get_p2p_interface_replacement(const char *interface, char *p2p_interface) {
52      /* Use p2p for any interface starting with p2p. */
53      if (strncmp(interface, "p2p",3) == 0) {
54          strncpy(p2p_interface, "p2p", MAX_INTERFACE_LENGTH);
55      } else {
56          strncpy(p2p_interface, interface, MAX_INTERFACE_LENGTH);
57      }
58  }
59  
60  /*
61   * Wait for a system property to be assigned a specified value.
62   * If desired_value is NULL, then just wait for the property to
63   * be created with any value. maxwait is the maximum amount of
64   * time in seconds to wait before giving up.
65   */
wait_for_property(const char * name,const char * desired_value,int maxwait)66  static int wait_for_property(const char *name, const char *desired_value, int maxwait)
67  {
68      char value[PROPERTY_VALUE_MAX] = {'\0'};
69      int maxnaps = (maxwait * 1000) / NAP_TIME;
70  
71      if (maxnaps < 1) {
72          maxnaps = 1;
73      }
74  
75      while (maxnaps-- > 0) {
76          usleep(NAP_TIME * 1000);
77          if (property_get(name, value, NULL)) {
78              if (desired_value == NULL ||
79                      strcmp(value, desired_value) == 0) {
80                  return 0;
81              }
82          }
83      }
84      return -1; /* failure */
85  }
86  
fill_ip_info(const char * interface,char * ipaddr,char * gateway,uint32_t * prefixLength,char * dns[],char * server,uint32_t * lease,char * vendorInfo,char * domain,char * mtu)87  static int fill_ip_info(const char *interface,
88                       char *ipaddr,
89                       char *gateway,
90                       uint32_t *prefixLength,
91                       char *dns[],
92                       char *server,
93                       uint32_t *lease,
94                       char *vendorInfo,
95                       char *domain,
96                       char *mtu)
97  {
98      char prop_name[PROPERTY_KEY_MAX];
99      char prop_value[PROPERTY_VALUE_MAX];
100      /* Interface name after converting p2p0-p2p0-X to p2p to reuse system properties */
101      char p2p_interface[MAX_INTERFACE_LENGTH];
102      int x;
103  
104      get_p2p_interface_replacement(interface, p2p_interface);
105  
106      snprintf(prop_name, sizeof(prop_name), "%s.%s.ipaddress", DHCP_PROP_NAME_PREFIX, p2p_interface);
107      property_get(prop_name, ipaddr, NULL);
108  
109      snprintf(prop_name, sizeof(prop_name), "%s.%s.gateway", DHCP_PROP_NAME_PREFIX, p2p_interface);
110      property_get(prop_name, gateway, NULL);
111  
112      snprintf(prop_name, sizeof(prop_name), "%s.%s.server", DHCP_PROP_NAME_PREFIX, p2p_interface);
113      property_get(prop_name, server, NULL);
114  
115      //TODO: Handle IPv6 when we change system property usage
116      if (gateway[0] == '\0' || strncmp(gateway, "0.0.0.0", 7) == 0) {
117          //DHCP server is our best bet as gateway
118          strncpy(gateway, server, PROPERTY_VALUE_MAX);
119      }
120  
121      snprintf(prop_name, sizeof(prop_name), "%s.%s.mask", DHCP_PROP_NAME_PREFIX, p2p_interface);
122      if (property_get(prop_name, prop_value, NULL)) {
123          int p;
124          // this conversion is v4 only, but this dhcp client is v4 only anyway
125          in_addr_t mask = ntohl(inet_addr(prop_value));
126          // Check netmask is a valid IP address.  ntohl gives NONE response (all 1's) for
127          // non 255.255.255.255 inputs.  if we get that value check if it is legit..
128          if (mask == INADDR_NONE && strcmp(prop_value, "255.255.255.255") != 0) {
129              snprintf(errmsg, sizeof(errmsg), "DHCP gave invalid net mask %s", prop_value);
130              return -1;
131          }
132          for (p = 0; p < 32; p++) {
133              if (mask == 0) break;
134              // check for non-contiguous netmask, e.g., 255.254.255.0
135              if ((mask & 0x80000000) == 0) {
136                  snprintf(errmsg, sizeof(errmsg), "DHCP gave invalid net mask %s", prop_value);
137                  return -1;
138              }
139              mask = mask << 1;
140          }
141          *prefixLength = p;
142      }
143  
144      for (x=0; dns[x] != NULL; x++) {
145          snprintf(prop_name, sizeof(prop_name), "%s.%s.dns%d", DHCP_PROP_NAME_PREFIX, p2p_interface, x+1);
146          property_get(prop_name, dns[x], NULL);
147      }
148  
149      snprintf(prop_name, sizeof(prop_name), "%s.%s.leasetime", DHCP_PROP_NAME_PREFIX, p2p_interface);
150      if (property_get(prop_name, prop_value, NULL)) {
151          *lease = atol(prop_value);
152      }
153  
154      snprintf(prop_name, sizeof(prop_name), "%s.%s.vendorInfo", DHCP_PROP_NAME_PREFIX,
155              p2p_interface);
156      property_get(prop_name, vendorInfo, NULL);
157  
158      snprintf(prop_name, sizeof(prop_name), "%s.%s.domain", DHCP_PROP_NAME_PREFIX,
159              p2p_interface);
160      property_get(prop_name, domain, NULL);
161  
162      snprintf(prop_name, sizeof(prop_name), "%s.%s.mtu", DHCP_PROP_NAME_PREFIX,
163              p2p_interface);
164      property_get(prop_name, mtu, NULL);
165  
166      return 0;
167  }
168  
ipaddr_to_string(in_addr_t addr)169  static const char *ipaddr_to_string(in_addr_t addr)
170  {
171      struct in_addr in_addr;
172  
173      in_addr.s_addr = addr;
174      return inet_ntoa(in_addr);
175  }
176  
177  /*
178   * Start the dhcp client daemon, and wait for it to finish
179   * configuring the interface.
180   *
181   * The device init.rc file needs a corresponding entry for this work.
182   *
183   * Example:
184   * service dhcpcd_<interface> /system/bin/dhcpcd -ABKL -f dhcpcd.conf
185   */
dhcp_do_request(const char * interface,char * ipaddr,char * gateway,uint32_t * prefixLength,char * dns[],char * server,uint32_t * lease,char * vendorInfo,char * domain,char * mtu)186  int dhcp_do_request(const char *interface,
187                      char *ipaddr,
188                      char *gateway,
189                      uint32_t *prefixLength,
190                      char *dns[],
191                      char *server,
192                      uint32_t *lease,
193                      char *vendorInfo,
194                      char *domain,
195                      char *mtu)
196  {
197      char result_prop_name[PROPERTY_KEY_MAX];
198      char daemon_prop_name[PROPERTY_KEY_MAX];
199      char prop_value[PROPERTY_VALUE_MAX] = {'\0'};
200      char daemon_cmd[PROPERTY_VALUE_MAX * 2 + sizeof(DHCP_CONFIG_PATH)];
201      const char *ctrl_prop = "ctl.start";
202      const char *desired_status = "running";
203      /* Interface name after converting p2p0-p2p0-X to p2p to reuse system properties */
204      char p2p_interface[MAX_INTERFACE_LENGTH];
205  
206      get_p2p_interface_replacement(interface, p2p_interface);
207  
208      snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result",
209              DHCP_PROP_NAME_PREFIX,
210              p2p_interface);
211  
212      snprintf(daemon_prop_name, sizeof(daemon_prop_name), "%s_%s",
213              DAEMON_PROP_NAME,
214              p2p_interface);
215  
216      /* Erase any previous setting of the dhcp result property */
217      property_set(result_prop_name, "");
218  
219      /* Start the daemon and wait until it's ready */
220      if (property_get(HOSTNAME_PROP_NAME, prop_value, NULL) && (prop_value[0] != '\0'))
221          snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:-f %s -h %s %s", DAEMON_NAME,
222                   p2p_interface, DHCP_CONFIG_PATH, prop_value, interface);
223      else
224          snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:-f %s %s", DAEMON_NAME,
225                   p2p_interface, DHCP_CONFIG_PATH, interface);
226      memset(prop_value, '\0', PROPERTY_VALUE_MAX);
227      property_set(ctrl_prop, daemon_cmd);
228      if (wait_for_property(daemon_prop_name, desired_status, 10) < 0) {
229          snprintf(errmsg, sizeof(errmsg), "%s", "Timed out waiting for dhcpcd to start");
230          return -1;
231      }
232  
233      /* Wait for the daemon to return a result */
234      if (wait_for_property(result_prop_name, NULL, 30) < 0) {
235          snprintf(errmsg, sizeof(errmsg), "%s", "Timed out waiting for DHCP to finish");
236          return -1;
237      }
238  
239      if (!property_get(result_prop_name, prop_value, NULL)) {
240          /* shouldn't ever happen, given the success of wait_for_property() */
241          snprintf(errmsg, sizeof(errmsg), "%s", "DHCP result property was not set");
242          return -1;
243      }
244      if (strcmp(prop_value, "ok") == 0) {
245          char dns_prop_name[PROPERTY_KEY_MAX];
246          if (fill_ip_info(interface, ipaddr, gateway, prefixLength, dns,
247                  server, lease, vendorInfo, domain, mtu) == -1) {
248              return -1;
249          }
250          return 0;
251      } else {
252          snprintf(errmsg, sizeof(errmsg), "DHCP result was %s", prop_value);
253          return -1;
254      }
255  }
256  
257  /**
258   * Stop the DHCP client daemon.
259   */
dhcp_stop(const char * interface)260  int dhcp_stop(const char *interface)
261  {
262      char result_prop_name[PROPERTY_KEY_MAX];
263      char daemon_prop_name[PROPERTY_KEY_MAX];
264      char daemon_cmd[PROPERTY_VALUE_MAX * 2];
265      const char *ctrl_prop = "ctl.stop";
266      const char *desired_status = "stopped";
267  
268      char p2p_interface[MAX_INTERFACE_LENGTH];
269  
270      get_p2p_interface_replacement(interface, p2p_interface);
271  
272      snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result",
273              DHCP_PROP_NAME_PREFIX,
274              p2p_interface);
275  
276      snprintf(daemon_prop_name, sizeof(daemon_prop_name), "%s_%s",
277              DAEMON_PROP_NAME,
278              p2p_interface);
279  
280      snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s", DAEMON_NAME, p2p_interface);
281  
282      /* Stop the daemon and wait until it's reported to be stopped */
283      property_set(ctrl_prop, daemon_cmd);
284      if (wait_for_property(daemon_prop_name, desired_status, 5) < 0) {
285          return -1;
286      }
287      property_set(result_prop_name, "failed");
288      return 0;
289  }
290  
291  /**
292   * Release the current DHCP client lease.
293   */
dhcp_release_lease(const char * interface)294  int dhcp_release_lease(const char *interface)
295  {
296      char daemon_prop_name[PROPERTY_KEY_MAX];
297      char daemon_cmd[PROPERTY_VALUE_MAX * 2];
298      const char *ctrl_prop = "ctl.stop";
299      const char *desired_status = "stopped";
300  
301      char p2p_interface[MAX_INTERFACE_LENGTH];
302  
303      get_p2p_interface_replacement(interface, p2p_interface);
304  
305      snprintf(daemon_prop_name, sizeof(daemon_prop_name), "%s_%s",
306              DAEMON_PROP_NAME,
307              p2p_interface);
308  
309      snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s", DAEMON_NAME, p2p_interface);
310  
311      /* Stop the daemon and wait until it's reported to be stopped */
312      property_set(ctrl_prop, daemon_cmd);
313      if (wait_for_property(daemon_prop_name, desired_status, 5) < 0) {
314          return -1;
315      }
316      return 0;
317  }
318  
dhcp_get_errmsg()319  char *dhcp_get_errmsg() {
320      return errmsg;
321  }
322  
323  /**
324   * The device init.rc file needs a corresponding entry.
325   *
326   * Example:
327   * service iprenew_<interface> /system/bin/dhcpcd -n
328   *
329   */
dhcp_do_request_renew(const char * interface,char * ipaddr,char * gateway,uint32_t * prefixLength,char * dns[],char * server,uint32_t * lease,char * vendorInfo,char * domain,char * mtu)330  int dhcp_do_request_renew(const char *interface,
331                      char *ipaddr,
332                      char *gateway,
333                      uint32_t *prefixLength,
334                      char *dns[],
335                      char *server,
336                      uint32_t *lease,
337                      char *vendorInfo,
338                      char *domain,
339                      char *mtu)
340  {
341      char result_prop_name[PROPERTY_KEY_MAX];
342      char prop_value[PROPERTY_VALUE_MAX] = {'\0'};
343      char daemon_cmd[PROPERTY_VALUE_MAX * 2];
344      const char *ctrl_prop = "ctl.start";
345  
346      char p2p_interface[MAX_INTERFACE_LENGTH];
347  
348      get_p2p_interface_replacement(interface, p2p_interface);
349  
350      snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result",
351              DHCP_PROP_NAME_PREFIX,
352              p2p_interface);
353  
354      /* Erase any previous setting of the dhcp result property */
355      property_set(result_prop_name, "");
356  
357      /* Start the renew daemon and wait until it's ready */
358      snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:%s", DAEMON_NAME_RENEW,
359              p2p_interface, interface);
360      memset(prop_value, '\0', PROPERTY_VALUE_MAX);
361      property_set(ctrl_prop, daemon_cmd);
362  
363      /* Wait for the daemon to return a result */
364      if (wait_for_property(result_prop_name, NULL, 30) < 0) {
365          snprintf(errmsg, sizeof(errmsg), "%s", "Timed out waiting for DHCP Renew to finish");
366          return -1;
367      }
368  
369      if (!property_get(result_prop_name, prop_value, NULL)) {
370          /* shouldn't ever happen, given the success of wait_for_property() */
371          snprintf(errmsg, sizeof(errmsg), "%s", "DHCP Renew result property was not set");
372          return -1;
373      }
374      if (strcmp(prop_value, "ok") == 0) {
375          return fill_ip_info(interface, ipaddr, gateway, prefixLength, dns,
376                  server, lease, vendorInfo, domain, mtu);
377      } else {
378          snprintf(errmsg, sizeof(errmsg), "DHCP Renew result was %s", prop_value);
379          return -1;
380      }
381  }
382