Home | History | Annotate | Download | only in libnetutils
      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  */
     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  */
     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 
     87 static int fill_ip_info(const char *interface,
     88                      char *ipaddr,
     89                      char *gateway,
     90                      uint32_t *prefixLength,
     91                      char *dns1,
     92                      char *dns2,
     93                      char *server,
     94                      uint32_t *lease,
     95                      char *vendorInfo)
     96 {
     97     char prop_name[PROPERTY_KEY_MAX];
     98     char prop_value[PROPERTY_VALUE_MAX];
     99     /* Interface name after converting p2p0-p2p0-X to p2p to reuse system properties */
    100     char p2p_interface[MAX_INTERFACE_LENGTH];
    101 
    102     get_p2p_interface_replacement(interface, p2p_interface);
    103 
    104     snprintf(prop_name, sizeof(prop_name), "%s.%s.ipaddress", DHCP_PROP_NAME_PREFIX, p2p_interface);
    105     property_get(prop_name, ipaddr, NULL);
    106 
    107     snprintf(prop_name, sizeof(prop_name), "%s.%s.gateway", DHCP_PROP_NAME_PREFIX, p2p_interface);
    108     property_get(prop_name, gateway, NULL);
    109 
    110     snprintf(prop_name, sizeof(prop_name), "%s.%s.server", DHCP_PROP_NAME_PREFIX, p2p_interface);
    111     property_get(prop_name, server, NULL);
    112 
    113     //TODO: Handle IPv6 when we change system property usage
    114     if (strcmp(gateway, "0.0.0.0") == 0) {
    115         //DHCP server is our best bet as gateway
    116         strncpy(gateway, server, PROPERTY_VALUE_MAX);
    117     }
    118 
    119     snprintf(prop_name, sizeof(prop_name), "%s.%s.mask", DHCP_PROP_NAME_PREFIX, p2p_interface);
    120     if (property_get(prop_name, prop_value, NULL)) {
    121         int p;
    122         // this conversion is v4 only, but this dhcp client is v4 only anyway
    123         in_addr_t mask = ntohl(inet_addr(prop_value));
    124         // Check netmask is a valid IP address.  ntohl gives NONE response (all 1's) for
    125         // non 255.255.255.255 inputs.  if we get that value check if it is legit..
    126         if (mask == INADDR_NONE && strcmp(prop_value, "255.255.255.255") != 0) {
    127             snprintf(errmsg, sizeof(errmsg), "DHCP gave invalid net mask %s", prop_value);
    128             return -1;
    129         }
    130         for (p = 0; p < 32; p++) {
    131             if (mask == 0) break;
    132             // check for non-contiguous netmask, e.g., 255.254.255.0
    133             if ((mask & 0x80000000) == 0) {
    134                 snprintf(errmsg, sizeof(errmsg), "DHCP gave invalid net mask %s", prop_value);
    135                 return -1;
    136             }
    137             mask = mask << 1;
    138         }
    139         *prefixLength = p;
    140     }
    141     snprintf(prop_name, sizeof(prop_name), "%s.%s.dns1", DHCP_PROP_NAME_PREFIX, p2p_interface);
    142     property_get(prop_name, dns1, NULL);
    143 
    144     snprintf(prop_name, sizeof(prop_name), "%s.%s.dns2", DHCP_PROP_NAME_PREFIX, p2p_interface);
    145     property_get(prop_name, dns2, NULL);
    146 
    147     snprintf(prop_name, sizeof(prop_name), "%s.%s.leasetime", DHCP_PROP_NAME_PREFIX, p2p_interface);
    148     if (property_get(prop_name, prop_value, NULL)) {
    149         *lease = atol(prop_value);
    150     }
    151 
    152     snprintf(prop_name, sizeof(prop_name), "%s.%s.vendorInfo", DHCP_PROP_NAME_PREFIX,
    153             p2p_interface);
    154     property_get(prop_name, vendorInfo, NULL);
    155 
    156     return 0;
    157 }
    158 
    159 static const char *ipaddr_to_string(in_addr_t addr)
    160 {
    161     struct in_addr in_addr;
    162 
    163     in_addr.s_addr = addr;
    164     return inet_ntoa(in_addr);
    165 }
    166 
    167 /*
    168  * Start the dhcp client daemon, and wait for it to finish
    169  * configuring the interface.
    170  *
    171  * The device init.rc file needs a corresponding entry for this work.
    172  *
    173  * Example:
    174  * service dhcpcd_<interface> /system/bin/dhcpcd -ABKL -f dhcpcd.conf
    175  */
    176 int dhcp_do_request(const char *interface,
    177                     char *ipaddr,
    178                     char *gateway,
    179                     uint32_t *prefixLength,
    180                     char *dns1,
    181                     char *dns2,
    182                     char *server,
    183                     uint32_t *lease,
    184                     char *vendorInfo)
    185 {
    186     char result_prop_name[PROPERTY_KEY_MAX];
    187     char daemon_prop_name[PROPERTY_KEY_MAX];
    188     char prop_value[PROPERTY_VALUE_MAX] = {'\0'};
    189     char daemon_cmd[PROPERTY_VALUE_MAX * 2 + sizeof(DHCP_CONFIG_PATH)];
    190     const char *ctrl_prop = "ctl.start";
    191     const char *desired_status = "running";
    192     /* Interface name after converting p2p0-p2p0-X to p2p to reuse system properties */
    193     char p2p_interface[MAX_INTERFACE_LENGTH];
    194 
    195     get_p2p_interface_replacement(interface, p2p_interface);
    196 
    197     snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result",
    198             DHCP_PROP_NAME_PREFIX,
    199             p2p_interface);
    200 
    201     snprintf(daemon_prop_name, sizeof(daemon_prop_name), "%s_%s",
    202             DAEMON_PROP_NAME,
    203             p2p_interface);
    204 
    205     /* Erase any previous setting of the dhcp result property */
    206     property_set(result_prop_name, "");
    207 
    208     /* Start the daemon and wait until it's ready */
    209     if (property_get(HOSTNAME_PROP_NAME, prop_value, NULL) && (prop_value[0] != '\0'))
    210         snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:-f %s -h %s %s", DAEMON_NAME,
    211                  p2p_interface, DHCP_CONFIG_PATH, prop_value, interface);
    212     else
    213         snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:-f %s %s", DAEMON_NAME,
    214                  DHCP_CONFIG_PATH, p2p_interface, interface);
    215     memset(prop_value, '\0', PROPERTY_VALUE_MAX);
    216     property_set(ctrl_prop, daemon_cmd);
    217     if (wait_for_property(daemon_prop_name, desired_status, 10) < 0) {
    218         snprintf(errmsg, sizeof(errmsg), "%s", "Timed out waiting for dhcpcd to start");
    219         return -1;
    220     }
    221 
    222     /* Wait for the daemon to return a result */
    223     if (wait_for_property(result_prop_name, NULL, 30) < 0) {
    224         snprintf(errmsg, sizeof(errmsg), "%s", "Timed out waiting for DHCP to finish");
    225         return -1;
    226     }
    227 
    228     if (!property_get(result_prop_name, prop_value, NULL)) {
    229         /* shouldn't ever happen, given the success of wait_for_property() */
    230         snprintf(errmsg, sizeof(errmsg), "%s", "DHCP result property was not set");
    231         return -1;
    232     }
    233     if (strcmp(prop_value, "ok") == 0) {
    234         char dns_prop_name[PROPERTY_KEY_MAX];
    235         if (fill_ip_info(interface, ipaddr, gateway, prefixLength,
    236                 dns1, dns2, server, lease, vendorInfo) == -1) {
    237             return -1;
    238         }
    239 
    240         /* copy dns data to system properties - TODO - remove this after we have async
    241          * notification of renewal's */
    242         snprintf(dns_prop_name, sizeof(dns_prop_name), "net.%s.dns1", interface);
    243         property_set(dns_prop_name, *dns1 ? ipaddr_to_string(*dns1) : "");
    244         snprintf(dns_prop_name, sizeof(dns_prop_name), "net.%s.dns2", interface);
    245         property_set(dns_prop_name, *dns2 ? ipaddr_to_string(*dns2) : "");
    246         return 0;
    247     } else {
    248         snprintf(errmsg, sizeof(errmsg), "DHCP result was %s", prop_value);
    249         return -1;
    250     }
    251 }
    252 
    253 /**
    254  * Stop the DHCP client daemon.
    255  */
    256 int dhcp_stop(const char *interface)
    257 {
    258     char result_prop_name[PROPERTY_KEY_MAX];
    259     char daemon_prop_name[PROPERTY_KEY_MAX];
    260     char daemon_cmd[PROPERTY_VALUE_MAX * 2];
    261     const char *ctrl_prop = "ctl.stop";
    262     const char *desired_status = "stopped";
    263 
    264     char p2p_interface[MAX_INTERFACE_LENGTH];
    265 
    266     get_p2p_interface_replacement(interface, p2p_interface);
    267 
    268     snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result",
    269             DHCP_PROP_NAME_PREFIX,
    270             p2p_interface);
    271 
    272     snprintf(daemon_prop_name, sizeof(daemon_prop_name), "%s_%s",
    273             DAEMON_PROP_NAME,
    274             p2p_interface);
    275 
    276     snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s", DAEMON_NAME, p2p_interface);
    277 
    278     /* Stop the daemon and wait until it's reported to be stopped */
    279     property_set(ctrl_prop, daemon_cmd);
    280     if (wait_for_property(daemon_prop_name, desired_status, 5) < 0) {
    281         return -1;
    282     }
    283     property_set(result_prop_name, "failed");
    284     return 0;
    285 }
    286 
    287 /**
    288  * Release the current DHCP client lease.
    289  */
    290 int dhcp_release_lease(const char *interface)
    291 {
    292     char daemon_prop_name[PROPERTY_KEY_MAX];
    293     char daemon_cmd[PROPERTY_VALUE_MAX * 2];
    294     const char *ctrl_prop = "ctl.stop";
    295     const char *desired_status = "stopped";
    296 
    297     char p2p_interface[MAX_INTERFACE_LENGTH];
    298 
    299     get_p2p_interface_replacement(interface, p2p_interface);
    300 
    301     snprintf(daemon_prop_name, sizeof(daemon_prop_name), "%s_%s",
    302             DAEMON_PROP_NAME,
    303             p2p_interface);
    304 
    305     snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s", DAEMON_NAME, p2p_interface);
    306 
    307     /* Stop the daemon and wait until it's reported to be stopped */
    308     property_set(ctrl_prop, daemon_cmd);
    309     if (wait_for_property(daemon_prop_name, desired_status, 5) < 0) {
    310         return -1;
    311     }
    312     return 0;
    313 }
    314 
    315 char *dhcp_get_errmsg() {
    316     return errmsg;
    317 }
    318 
    319 /**
    320  * The device init.rc file needs a corresponding entry.
    321  *
    322  * Example:
    323  * service iprenew_<interface> /system/bin/dhcpcd -n
    324  *
    325  */
    326 int dhcp_do_request_renew(const char *interface,
    327                     char *ipaddr,
    328                     char *gateway,
    329                     uint32_t *prefixLength,
    330                     char *dns1,
    331                     char *dns2,
    332                     char *server,
    333                     uint32_t *lease,
    334                     char *vendorInfo)
    335 {
    336     char result_prop_name[PROPERTY_KEY_MAX];
    337     char prop_value[PROPERTY_VALUE_MAX] = {'\0'};
    338     char daemon_cmd[PROPERTY_VALUE_MAX * 2];
    339     const char *ctrl_prop = "ctl.start";
    340 
    341     char p2p_interface[MAX_INTERFACE_LENGTH];
    342 
    343     get_p2p_interface_replacement(interface, p2p_interface);
    344 
    345     snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result",
    346             DHCP_PROP_NAME_PREFIX,
    347             p2p_interface);
    348 
    349     /* Erase any previous setting of the dhcp result property */
    350     property_set(result_prop_name, "");
    351 
    352     /* Start the renew daemon and wait until it's ready */
    353     snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:%s", DAEMON_NAME_RENEW,
    354             p2p_interface, interface);
    355     memset(prop_value, '\0', PROPERTY_VALUE_MAX);
    356     property_set(ctrl_prop, daemon_cmd);
    357 
    358     /* Wait for the daemon to return a result */
    359     if (wait_for_property(result_prop_name, NULL, 30) < 0) {
    360         snprintf(errmsg, sizeof(errmsg), "%s", "Timed out waiting for DHCP Renew to finish");
    361         return -1;
    362     }
    363 
    364     if (!property_get(result_prop_name, prop_value, NULL)) {
    365         /* shouldn't ever happen, given the success of wait_for_property() */
    366         snprintf(errmsg, sizeof(errmsg), "%s", "DHCP Renew result property was not set");
    367         return -1;
    368     }
    369     if (strcmp(prop_value, "ok") == 0) {
    370         fill_ip_info(interface, ipaddr, gateway, prefixLength,
    371                 dns1, dns2, server, lease, vendorInfo);
    372         return 0;
    373     } else {
    374         snprintf(errmsg, sizeof(errmsg), "DHCP Renew result was %s", prop_value);
    375         return -1;
    376     }
    377 }
    378