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 *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 
    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  */
    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  */
    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  */
    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 
    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  */
    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