aboutsummaryrefslogtreecommitdiffstats
path: root/libnetutils
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
committerThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
commit4f6e8d7a00cbeda1e70cc15be9c4af1018bdad53 (patch)
tree54fd1b2695a591d2306d41264df67c53077b752c /libnetutils
downloadsystem_core-4f6e8d7a00cbeda1e70cc15be9c4af1018bdad53.zip
system_core-4f6e8d7a00cbeda1e70cc15be9c4af1018bdad53.tar.gz
system_core-4f6e8d7a00cbeda1e70cc15be9c4af1018bdad53.tar.bz2
Initial Contribution
Diffstat (limited to 'libnetutils')
-rw-r--r--libnetutils/Android.mk23
-rw-r--r--libnetutils/dhcp_utils.c186
-rw-r--r--libnetutils/dhcpclient.c562
-rw-r--r--libnetutils/dhcpmsg.c100
-rw-r--r--libnetutils/dhcpmsg.h106
-rw-r--r--libnetutils/ifc_utils.c428
-rw-r--r--libnetutils/ifc_utils.h35
-rw-r--r--libnetutils/packet.c239
-rw-r--r--libnetutils/packet.h25
9 files changed, 1704 insertions, 0 deletions
diff --git a/libnetutils/Android.mk b/libnetutils/Android.mk
new file mode 100644
index 0000000..46102d5
--- /dev/null
+++ b/libnetutils/Android.mk
@@ -0,0 +1,23 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ dhcpclient.c \
+ dhcpmsg.c \
+ dhcp_utils.c \
+ ifc_utils.c \
+ packet.c
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils
+
+# need "-lrt" on Linux simulator to pick up clock_gettime
+ifeq ($(TARGET_SIMULATOR),true)
+ ifeq ($(HOST_OS),linux)
+ LOCAL_LDLIBS += -lrt -lpthread
+ endif
+endif
+
+LOCAL_MODULE:= libnetutils
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/libnetutils/dhcp_utils.c b/libnetutils/dhcp_utils.c
new file mode 100644
index 0000000..97fe76d
--- /dev/null
+++ b/libnetutils/dhcp_utils.c
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Utilities for managing the dhcpcd DHCP client daemon */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include <cutils/properties.h>
+
+static const char DAEMON_NAME[] = "dhcpcd";
+static const char DAEMON_PROP_NAME[] = "init.svc.dhcpcd";
+static const char DHCP_PROP_NAME_PREFIX[] = "dhcp";
+static const int NAP_TIME = 1; /* wait for 1 second at a time */
+ /* when polling for property values */
+static char errmsg[100];
+
+/*
+ * Wait for a system property to be assigned a specified value.
+ * If desired_value is NULL, then just wait for the property to
+ * be created with any value. maxwait is the maximum amount of
+ * time in seconds to wait before giving up.
+ */
+static int wait_for_property(const char *name, const char *desired_value, int maxwait)
+{
+ char value[PROPERTY_VALUE_MAX] = {'\0'};
+ int maxnaps = maxwait / NAP_TIME;
+
+ if (maxnaps < 1) {
+ maxnaps = 1;
+ }
+
+ while (maxnaps-- > 0) {
+ usleep(1000000);
+ if (property_get(name, value, NULL)) {
+ if (desired_value == NULL ||
+ strcmp(value, desired_value) == 0) {
+ return 0;
+ }
+ }
+ }
+ return -1; /* failure */
+}
+
+static void fill_ip_info(const char *interface,
+ in_addr_t *ipaddr,
+ in_addr_t *gateway,
+ in_addr_t *mask,
+ in_addr_t *dns1,
+ in_addr_t *dns2,
+ in_addr_t *server,
+ uint32_t *lease)
+{
+ char prop_name[PROPERTY_KEY_MAX];
+ char prop_value[PROPERTY_VALUE_MAX];
+ struct in_addr addr;
+ in_addr_t iaddr;
+
+ snprintf(prop_name, sizeof(prop_name), "%s.%s.ipaddress", DHCP_PROP_NAME_PREFIX, interface);
+ if (property_get(prop_name, prop_value, NULL) && inet_aton(prop_value, &addr)) {
+ *ipaddr = addr.s_addr;
+ } else {
+ *ipaddr = 0;
+ }
+ snprintf(prop_name, sizeof(prop_name), "%s.%s.gateway", DHCP_PROP_NAME_PREFIX, interface);
+ if (property_get(prop_name, prop_value, NULL) && inet_aton(prop_value, &addr)) {
+ *gateway = addr.s_addr;
+ } else {
+ *gateway = 0;
+ }
+ snprintf(prop_name, sizeof(prop_name), "%s.%s.mask", DHCP_PROP_NAME_PREFIX, interface);
+ if (property_get(prop_name, prop_value, NULL) && inet_aton(prop_value, &addr)) {
+ *mask = addr.s_addr;
+ } else {
+ *mask = 0;
+ }
+ snprintf(prop_name, sizeof(prop_name), "%s.%s.dns1", DHCP_PROP_NAME_PREFIX, interface);
+ if (property_get(prop_name, prop_value, NULL) && inet_aton(prop_value, &addr)) {
+ *dns1 = addr.s_addr;
+ } else {
+ *dns1 = 0;
+ }
+ snprintf(prop_name, sizeof(prop_name), "%s.%s.dns2", DHCP_PROP_NAME_PREFIX, interface);
+ if (property_get(prop_name, prop_value, NULL) && inet_aton(prop_value, &addr)) {
+ *dns2 = addr.s_addr;
+ } else {
+ *dns2 = 0;
+ }
+ snprintf(prop_name, sizeof(prop_name), "%s.%s.server", DHCP_PROP_NAME_PREFIX, interface);
+ if (property_get(prop_name, prop_value, NULL) && inet_aton(prop_value, &addr)) {
+ *server = addr.s_addr;
+ } else {
+ *server = 0;
+ }
+ snprintf(prop_name, sizeof(prop_name), "%s.%s.leasetime", DHCP_PROP_NAME_PREFIX, interface);
+ if (property_get(prop_name, prop_value, NULL)) {
+ *lease = atol(prop_value);
+ }
+}
+
+/*
+ * Start the dhcp client daemon, and wait for it to finish
+ * configuring the interface.
+ */
+int dhcp_do_request(const char *interface,
+ in_addr_t *ipaddr,
+ in_addr_t *gateway,
+ in_addr_t *mask,
+ in_addr_t *dns1,
+ in_addr_t *dns2,
+ in_addr_t *server,
+ uint32_t *lease)
+{
+ char result_prop_name[PROPERTY_KEY_MAX];
+ char prop_value[PROPERTY_VALUE_MAX] = {'\0'};
+ const char *ctrl_prop = "ctl.start";
+ const char *desired_status = "running";
+
+ snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result",
+ DHCP_PROP_NAME_PREFIX,
+ interface);
+ /* Erase any previous setting of the dhcp result property */
+ property_set(result_prop_name, "");
+
+ /* Start the daemon and wait until it's ready */
+ property_set(ctrl_prop, DAEMON_NAME);
+ if (wait_for_property(DAEMON_PROP_NAME, desired_status, 10) < 0) {
+ snprintf(errmsg, sizeof(errmsg), "%s", "Timed out waiting for dhcpcd to start");
+ return -1;
+ }
+
+ /* Wait for the daemon to return a result */
+ if (wait_for_property(result_prop_name, NULL, 30) < 0) {
+ snprintf(errmsg, sizeof(errmsg), "%s", "Timed out waiting for DHCP to finish");
+ return -1;
+ }
+
+ if (!property_get(result_prop_name, prop_value, NULL)) {
+ /* shouldn't ever happen, given the success of wait_for_property() */
+ snprintf(errmsg, sizeof(errmsg), "%s", "DHCP result property was not set");
+ return -1;
+ }
+ if (strcmp(prop_value, "ok") == 0) {
+ fill_ip_info(interface, ipaddr, gateway, mask, dns1, dns2, server, lease);
+ return 0;
+ } else {
+ snprintf(errmsg, sizeof(errmsg), "DHCP result was %s", prop_value);
+ return -1;
+ }
+}
+
+/**
+ * Stop the DHCP client daemon.
+ */
+int dhcp_stop(const char *interface)
+{
+ const char *ctrl_prop = "ctl.stop";
+ const char *desired_status = "stopped";
+
+ /* Stop the daemon and wait until it's reported to be stopped */
+ property_set(ctrl_prop, DAEMON_NAME);
+ if (wait_for_property(DAEMON_PROP_NAME, desired_status, 5) < 0) {
+ return -1;
+ }
+ return 0;
+}
+
+char *dhcp_get_errmsg() {
+ return errmsg;
+}
diff --git a/libnetutils/dhcpclient.c b/libnetutils/dhcpclient.c
new file mode 100644
index 0000000..45e392a
--- /dev/null
+++ b/libnetutils/dhcpclient.c
@@ -0,0 +1,562 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+#include <time.h>
+#include <sys/time.h>
+#include <poll.h>
+
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include <cutils/properties.h>
+#define LOG_TAG "DHCP"
+#include <cutils/log.h>
+
+#include <dirent.h>
+
+#include "dhcpmsg.h"
+#include "ifc_utils.h"
+#include "packet.h"
+
+#define VERBOSE 2
+
+static int verbose = 1;
+static char errmsg[2048];
+
+typedef unsigned long long msecs_t;
+#if VERBOSE
+void dump_dhcp_msg();
+#endif
+
+msecs_t get_msecs(void)
+{
+ struct timespec ts;
+
+ if (clock_gettime(CLOCK_MONOTONIC, &ts)) {
+ return 0;
+ } else {
+ return (((msecs_t) ts.tv_sec) * ((msecs_t) 1000)) +
+ (((msecs_t) ts.tv_nsec) / ((msecs_t) 1000000));
+ }
+}
+
+void printerr(char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(errmsg, sizeof(errmsg), fmt, ap);
+ va_end(ap);
+
+ LOGD(errmsg);
+}
+
+const char *dhcp_lasterror()
+{
+ return errmsg;
+}
+
+int fatal(const char *reason)
+{
+ printerr("%s: %s\n", reason, strerror(errno));
+ return -1;
+// exit(1);
+}
+
+const char *ipaddr(uint32_t addr)
+{
+ static char buf[32];
+
+ sprintf(buf,"%d.%d.%d.%d",
+ addr & 255,
+ ((addr >> 8) & 255),
+ ((addr >> 16) & 255),
+ (addr >> 24));
+ return buf;
+}
+
+typedef struct dhcp_info dhcp_info;
+
+struct dhcp_info {
+ uint32_t type;
+
+ uint32_t ipaddr;
+ uint32_t gateway;
+ uint32_t netmask;
+
+ uint32_t dns1;
+ uint32_t dns2;
+
+ uint32_t serveraddr;
+ uint32_t lease;
+};
+
+dhcp_info last_good_info;
+
+void get_dhcp_info(uint32_t *ipaddr, uint32_t *gateway, uint32_t *mask,
+ uint32_t *dns1, uint32_t *dns2, uint32_t *server,
+ uint32_t *lease)
+{
+ *ipaddr = last_good_info.ipaddr;
+ *gateway = last_good_info.gateway;
+ *mask = last_good_info.netmask;
+ *dns1 = last_good_info.dns1;
+ *dns2 = last_good_info.dns2;
+ *server = last_good_info.serveraddr;
+ *lease = last_good_info.lease;
+}
+
+static int ifc_configure(const char *ifname, dhcp_info *info)
+{
+ char dns_prop_name[PROPERTY_KEY_MAX];
+
+ if (ifc_set_addr(ifname, info->ipaddr)) {
+ printerr("failed to set ipaddr %s: %s\n", ipaddr(info->ipaddr), strerror(errno));
+ return -1;
+ }
+ if (ifc_set_mask(ifname, info->netmask)) {
+ printerr("failed to set netmask %s: %s\n", ipaddr(info->netmask), strerror(errno));
+ return -1;
+ }
+ if (ifc_create_default_route(ifname, info->gateway)) {
+ printerr("failed to set default route %s: %s\n", ipaddr(info->gateway), strerror(errno));
+ return -1;
+ }
+
+ snprintf(dns_prop_name, sizeof(dns_prop_name), "net.%s.dns1", ifname);
+ property_set(dns_prop_name, info->dns1 ? ipaddr(info->dns1) : "");
+ snprintf(dns_prop_name, sizeof(dns_prop_name), "net.%s.dns2", ifname);
+ property_set(dns_prop_name, info->dns2 ? ipaddr(info->dns2) : "");
+
+ last_good_info = *info;
+
+ return 0;
+}
+
+static const char *dhcp_type_to_name(uint32_t type)
+{
+ switch(type) {
+ case DHCPDISCOVER: return "discover";
+ case DHCPOFFER: return "offer";
+ case DHCPREQUEST: return "request";
+ case DHCPDECLINE: return "decline";
+ case DHCPACK: return "ack";
+ case DHCPNAK: return "nak";
+ case DHCPRELEASE: return "release";
+ case DHCPINFORM: return "inform";
+ default: return "???";
+ }
+}
+
+void dump_dhcp_info(dhcp_info *info)
+{
+ char addr[20], gway[20], mask[20];
+ LOGD("--- dhcp %s (%d) ---",
+ dhcp_type_to_name(info->type), info->type);
+ strcpy(addr, ipaddr(info->ipaddr));
+ strcpy(gway, ipaddr(info->gateway));
+ strcpy(mask, ipaddr(info->netmask));
+ LOGD("ip %s gw %s mask %s", addr, gway, mask);
+ if (info->dns1) LOGD("dns1: %s", ipaddr(info->dns1));
+ if (info->dns2) LOGD("dns2: %s", ipaddr(info->dns2));
+ LOGD("server %s, lease %d seconds",
+ ipaddr(info->serveraddr), info->lease);
+}
+
+
+int decode_dhcp_msg(dhcp_msg *msg, int len, dhcp_info *info)
+{
+ uint8_t *x;
+ unsigned int opt;
+ int optlen;
+
+ memset(info, 0, sizeof(dhcp_info));
+ if (len < (DHCP_MSG_FIXED_SIZE + 4)) return -1;
+
+ len -= (DHCP_MSG_FIXED_SIZE + 4);
+
+ if (msg->options[0] != OPT_COOKIE1) return -1;
+ if (msg->options[1] != OPT_COOKIE2) return -1;
+ if (msg->options[2] != OPT_COOKIE3) return -1;
+ if (msg->options[3] != OPT_COOKIE4) return -1;
+
+ x = msg->options + 4;
+
+ while (len > 2) {
+ opt = *x++;
+ if (opt == OPT_PAD) {
+ len--;
+ continue;
+ }
+ if (opt == OPT_END) {
+ break;
+ }
+ optlen = *x++;
+ len -= 2;
+ if (optlen > len) {
+ break;
+ }
+ switch(opt) {
+ case OPT_SUBNET_MASK:
+ if (optlen >= 4) memcpy(&info->netmask, x, 4);
+ break;
+ case OPT_GATEWAY:
+ if (optlen >= 4) memcpy(&info->gateway, x, 4);
+ break;
+ case OPT_DNS:
+ if (optlen >= 4) memcpy(&info->dns1, x + 0, 4);
+ if (optlen >= 8) memcpy(&info->dns2, x + 4, 4);
+ break;
+ case OPT_LEASE_TIME:
+ if (optlen >= 4) {
+ memcpy(&info->lease, x, 4);
+ info->lease = ntohl(info->lease);
+ }
+ break;
+ case OPT_SERVER_ID:
+ if (optlen >= 4) memcpy(&info->serveraddr, x, 4);
+ break;
+ case OPT_MESSAGE_TYPE:
+ info->type = *x;
+ break;
+ default:
+ break;
+ }
+ x += optlen;
+ len -= optlen;
+ }
+
+ info->ipaddr = msg->yiaddr;
+
+ return 0;
+}
+
+#if VERBOSE
+
+static void hex2str(char *buf, const unsigned char *array, int len)
+{
+ int i;
+ char *cp = buf;
+
+ for (i = 0; i < len; i++) {
+ cp += sprintf(cp, " %02x ", array[i]);
+ }
+}
+
+void dump_dhcp_msg(dhcp_msg *msg, int len)
+{
+ unsigned char *x;
+ unsigned int n,c;
+ int optsz;
+ const char *name;
+ char buf[2048];
+
+ LOGD("===== DHCP message:");
+ if (len < DHCP_MSG_FIXED_SIZE) {
+ LOGD("Invalid length %d, should be %d", len, DHCP_MSG_FIXED_SIZE);
+ return;
+ }
+
+ len -= DHCP_MSG_FIXED_SIZE;
+
+ if (msg->op == OP_BOOTREQUEST)
+ name = "BOOTREQUEST";
+ else if (msg->op == OP_BOOTREPLY)
+ name = "BOOTREPLY";
+ else
+ name = "????";
+ LOGD("op = %s (%d), htype = %d, hlen = %d, hops = %d",
+ name, msg->op, msg->htype, msg->hlen, msg->hops);
+ LOGD("xid = 0x%08x secs = %d, flags = 0x%04x optlen = %d",
+ ntohl(msg->xid), ntohs(msg->secs), ntohs(msg->flags), len);
+ LOGD("ciaddr = %s", ipaddr(msg->ciaddr));
+ LOGD("yiaddr = %s", ipaddr(msg->yiaddr));
+ LOGD("siaddr = %s", ipaddr(msg->siaddr));
+ LOGD("giaddr = %s", ipaddr(msg->giaddr));
+
+ c = msg->hlen > 16 ? 16 : msg->hlen;
+ hex2str(buf, msg->chaddr, c);
+ LOGD("chaddr = {%s}", buf);
+
+ for (n = 0; n < 64; n++) {
+ if ((msg->sname[n] < ' ') || (msg->sname[n] > 127)) {
+ if (msg->sname[n] == 0) break;
+ msg->sname[n] = '.';
+ }
+ }
+ msg->sname[63] = 0;
+
+ for (n = 0; n < 128; n++) {
+ if ((msg->file[n] < ' ') || (msg->file[n] > 127)) {
+ if (msg->file[n] == 0) break;
+ msg->file[n] = '.';
+ }
+ }
+ msg->file[127] = 0;
+
+ LOGD("sname = '%s'", msg->sname);
+ LOGD("file = '%s'", msg->file);
+
+ if (len < 4) return;
+ len -= 4;
+ x = msg->options + 4;
+
+ while (len > 2) {
+ if (*x == 0) {
+ x++;
+ len--;
+ continue;
+ }
+ if (*x == OPT_END) {
+ break;
+ }
+ len -= 2;
+ optsz = x[1];
+ if (optsz > len) break;
+ if (x[0] == OPT_DOMAIN_NAME || x[0] == OPT_MESSAGE) {
+ if ((unsigned int)optsz < sizeof(buf) - 1) {
+ n = optsz;
+ } else {
+ n = sizeof(buf) - 1;
+ }
+ memcpy(buf, &x[2], n);
+ buf[n] = '\0';
+ } else {
+ hex2str(buf, &x[2], optsz);
+ }
+ if (x[0] == OPT_MESSAGE_TYPE)
+ name = dhcp_type_to_name(x[2]);
+ else
+ name = NULL;
+ LOGD("op %d len %d {%s} %s", x[0], optsz, buf, name == NULL ? "" : name);
+ len -= optsz;
+ x = x + optsz + 2;
+ }
+}
+
+#endif
+
+static int send_message(int sock, int if_index, dhcp_msg *msg, int size)
+{
+#if VERBOSE > 1
+ dump_dhcp_msg(msg, size);
+#endif
+ return send_packet(sock, if_index, msg, size, INADDR_ANY, INADDR_BROADCAST,
+ PORT_BOOTP_CLIENT, PORT_BOOTP_SERVER);
+}
+
+static int is_valid_reply(dhcp_msg *msg, dhcp_msg *reply, int sz)
+{
+ if (sz < DHCP_MSG_FIXED_SIZE) {
+ if (verbose) LOGD("netcfg: Wrong size %d != %d\n", sz, DHCP_MSG_FIXED_SIZE);
+ return 0;
+ }
+ if (reply->op != OP_BOOTREPLY) {
+ if (verbose) LOGD("netcfg: Wrong Op %d != %d\n", reply->op, OP_BOOTREPLY);
+ return 0;
+ }
+ if (reply->xid != msg->xid) {
+ if (verbose) LOGD("netcfg: Wrong Xid 0x%x != 0x%x\n", ntohl(reply->xid),
+ ntohl(msg->xid));
+ return 0;
+ }
+ if (reply->htype != msg->htype) {
+ if (verbose) LOGD("netcfg: Wrong Htype %d != %d\n", reply->htype, msg->htype);
+ return 0;
+ }
+ if (reply->hlen != msg->hlen) {
+ if (verbose) LOGD("netcfg: Wrong Hlen %d != %d\n", reply->hlen, msg->hlen);
+ return 0;
+ }
+ if (memcmp(msg->chaddr, reply->chaddr, msg->hlen)) {
+ if (verbose) LOGD("netcfg: Wrong chaddr %x != %x\n", *(reply->chaddr),*(msg->chaddr));
+ return 0;
+ }
+ return 1;
+}
+
+#define STATE_SELECTING 1
+#define STATE_REQUESTING 2
+
+#define TIMEOUT_INITIAL 4000
+#define TIMEOUT_MAX 32000
+
+int dhcp_init_ifc(const char *ifname)
+{
+ dhcp_msg discover_msg;
+ dhcp_msg request_msg;
+ dhcp_msg reply;
+ dhcp_msg *msg;
+ dhcp_info info;
+ int s, r, size;
+ int valid_reply;
+ uint32_t xid;
+ unsigned char hwaddr[6];
+ struct pollfd pfd;
+ unsigned int state;
+ unsigned int timeout;
+ int if_index;
+
+ xid = (uint32_t) get_msecs();
+
+ if (ifc_get_hwaddr(ifname, hwaddr)) {
+ return fatal("cannot obtain interface address");
+ }
+ if (ifc_get_ifindex(ifname, &if_index)) {
+ return fatal("cannot obtain interface index");
+ }
+
+ s = open_raw_socket(ifname, hwaddr, if_index);
+
+ timeout = TIMEOUT_INITIAL;
+ state = STATE_SELECTING;
+ info.type = 0;
+ goto transmit;
+
+ for (;;) {
+ pfd.fd = s;
+ pfd.events = POLLIN;
+ pfd.revents = 0;
+ r = poll(&pfd, 1, timeout);
+
+ if (r == 0) {
+#if VERBOSE
+ printerr("TIMEOUT\n");
+#endif
+ if (timeout >= TIMEOUT_MAX) {
+ printerr("timed out\n");
+ if ( info.type == DHCPOFFER ) {
+ printerr("no acknowledgement from DHCP server\nconfiguring %s with offered parameters\n", ifname);
+ return ifc_configure(ifname, &info);
+ }
+ errno = ETIME;
+ close(s);
+ return -1;
+ }
+ timeout = timeout * 2;
+
+ transmit:
+ size = 0;
+ msg = NULL;
+ switch(state) {
+ case STATE_SELECTING:
+ msg = &discover_msg;
+ size = init_dhcp_discover_msg(msg, hwaddr, xid);
+ break;
+ case STATE_REQUESTING:
+ msg = &request_msg;
+ size = init_dhcp_request_msg(msg, hwaddr, xid, info.ipaddr, info.serveraddr);
+ break;
+ default:
+ r = 0;
+ }
+ if (size != 0) {
+ r = send_message(s, if_index, msg, size);
+ if (r < 0) {
+ printerr("error sending dhcp msg: %s\n", strerror(errno));
+ }
+ }
+ continue;
+ }
+
+ if (r < 0) {
+ if ((errno == EAGAIN) || (errno == EINTR)) {
+ continue;
+ }
+ return fatal("poll failed");
+ }
+
+ errno = 0;
+ r = receive_packet(s, &reply);
+ if (r < 0) {
+ if (errno != 0) {
+ LOGD("receive_packet failed (%d): %s", r, strerror(errno));
+ if (errno == ENETDOWN || errno == ENXIO) {
+ return -1;
+ }
+ }
+ continue;
+ }
+
+#if VERBOSE > 1
+ dump_dhcp_msg(&reply, r);
+#endif
+ decode_dhcp_msg(&reply, r, &info);
+
+ if (state == STATE_SELECTING) {
+ valid_reply = is_valid_reply(&discover_msg, &reply, r);
+ } else {
+ valid_reply = is_valid_reply(&request_msg, &reply, r);
+ }
+ if (!valid_reply) {
+ printerr("invalid reply\n");
+ continue;
+ }
+
+ if (verbose) dump_dhcp_info(&info);
+
+ switch(state) {
+ case STATE_SELECTING:
+ if (info.type == DHCPOFFER) {
+ state = STATE_REQUESTING;
+ timeout = TIMEOUT_INITIAL;
+ xid++;
+ goto transmit;
+ }
+ break;
+ case STATE_REQUESTING:
+ if (info.type == DHCPACK) {
+ printerr("configuring %s\n", ifname);
+ close(s);
+ return ifc_configure(ifname, &info);
+ } else if (info.type == DHCPNAK) {
+ printerr("configuration request denied\n");
+ close(s);
+ return -1;
+ } else {
+ printerr("ignoring %s message in state %d\n",
+ dhcp_type_to_name(info.type), state);
+ }
+ break;
+ }
+ }
+ close(s);
+ return 0;
+}
+
+int do_dhcp(char *iname)
+{
+ if (ifc_set_addr(iname, 0)) {
+ printerr("failed to set ip addr for %s to 0.0.0.0: %s\n", iname, strerror(errno));
+ return -1;
+ }
+
+ if (ifc_up(iname)) {
+ printerr("failed to bring up interface %s: %s\n", iname, strerror(errno));
+ return -1;
+ }
+
+ return dhcp_init_ifc(iname);
+}
diff --git a/libnetutils/dhcpmsg.c b/libnetutils/dhcpmsg.c
new file mode 100644
index 0000000..1e0a233
--- /dev/null
+++ b/libnetutils/dhcpmsg.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <netinet/in.h>
+
+#include "dhcpmsg.h"
+
+static void *init_dhcp_msg(dhcp_msg *msg, int type, void *hwaddr, uint32_t xid)
+{
+ uint8_t *x;
+
+ memset(msg, 0, sizeof(dhcp_msg));
+
+ msg->op = OP_BOOTREQUEST;
+ msg->htype = HTYPE_ETHER;
+ msg->hlen = 6;
+ msg->hops = 0;
+
+ msg->flags = htons(FLAGS_BROADCAST);
+
+ msg->xid = xid;
+
+ memcpy(msg->chaddr, hwaddr, 6);
+
+ x = msg->options;
+
+ *x++ = OPT_COOKIE1;
+ *x++ = OPT_COOKIE2;
+ *x++ = OPT_COOKIE3;
+ *x++ = OPT_COOKIE4;
+
+ *x++ = OPT_MESSAGE_TYPE;
+ *x++ = 1;
+ *x++ = type;
+
+ return x;
+}
+
+int init_dhcp_discover_msg(dhcp_msg *msg, void *hwaddr, uint32_t xid)
+{
+ uint8_t *x;
+
+ x = init_dhcp_msg(msg, DHCPDISCOVER, hwaddr, xid);
+
+ *x++ = OPT_PARAMETER_LIST;
+ *x++ = 4;
+ *x++ = OPT_SUBNET_MASK;
+ *x++ = OPT_GATEWAY;
+ *x++ = OPT_DNS;
+ *x++ = OPT_BROADCAST_ADDR;
+
+ *x++ = OPT_END;
+
+ return DHCP_MSG_FIXED_SIZE + (x - msg->options);
+}
+
+int init_dhcp_request_msg(dhcp_msg *msg, void *hwaddr, uint32_t xid,
+ uint32_t ipaddr, uint32_t serveraddr)
+{
+ uint8_t *x;
+
+ x = init_dhcp_msg(msg, DHCPREQUEST, hwaddr, xid);
+
+ *x++ = OPT_PARAMETER_LIST;
+ *x++ = 4;
+ *x++ = OPT_SUBNET_MASK;
+ *x++ = OPT_GATEWAY;
+ *x++ = OPT_DNS;
+ *x++ = OPT_BROADCAST_ADDR;
+
+ *x++ = OPT_REQUESTED_IP;
+ *x++ = 4;
+ memcpy(x, &ipaddr, 4);
+ x += 4;
+
+ *x++ = OPT_SERVER_ID;
+ *x++ = 4;
+ memcpy(x, &serveraddr, 4);
+ x += 4;
+
+ *x++ = OPT_END;
+
+ return DHCP_MSG_FIXED_SIZE + (x - msg->options);
+}
diff --git a/libnetutils/dhcpmsg.h b/libnetutils/dhcpmsg.h
new file mode 100644
index 0000000..c86e400
--- /dev/null
+++ b/libnetutils/dhcpmsg.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _WIFI_DHCP_H_
+#define _WIFI_DHCP_H_
+
+#include <sys/types.h>
+
+#define PORT_BOOTP_SERVER 67
+#define PORT_BOOTP_CLIENT 68
+
+/* RFC 2131 p 9 */
+typedef struct dhcp_msg dhcp_msg;
+
+#define OP_BOOTREQUEST 1
+#define OP_BOOTREPLY 2
+
+#define FLAGS_BROADCAST 0x8000
+
+#define HTYPE_ETHER 1
+
+struct dhcp_msg
+{
+ uint8_t op; /* BOOTREQUEST / BOOTREPLY */
+ uint8_t htype; /* hw addr type */
+ uint8_t hlen; /* hw addr len */
+ uint8_t hops; /* client set to 0 */
+
+ uint32_t xid; /* transaction id */
+
+ uint16_t secs; /* seconds since start of acq */
+ uint16_t flags;
+
+ uint32_t ciaddr; /* client IP addr */
+ uint32_t yiaddr; /* your (client) IP addr */
+ uint32_t siaddr; /* ip addr of next server */
+ /* (DHCPOFFER and DHCPACK) */
+ uint32_t giaddr; /* relay agent IP addr */
+
+ uint8_t chaddr[16]; /* client hw addr */
+ char sname[64]; /* asciiz server hostname */
+ char file[128]; /* asciiz boot file name */
+
+ uint8_t options[1024]; /* optional parameters */
+};
+
+#define DHCP_MSG_FIXED_SIZE 236
+
+/* first four bytes of options are a cookie to indicate that
+** the payload are DHCP options as opposed to some other BOOTP
+** extension.
+*/
+#define OPT_COOKIE1 0x63
+#define OPT_COOKIE2 0x82
+#define OPT_COOKIE3 0x53
+#define OPT_COOKIE4 0x63
+
+/* BOOTP/DHCP options - see RFC 2132 */
+#define OPT_PAD 0
+
+#define OPT_SUBNET_MASK 1 /* 4 <ipaddr> */
+#define OPT_TIME_OFFSET 2 /* 4 <seconds> */
+#define OPT_GATEWAY 3 /* 4*n <ipaddr> * n */
+#define OPT_DNS 6 /* 4*n <ipaddr> * n */
+#define OPT_DOMAIN_NAME 15 /* n <domainnamestring> */
+#define OPT_BROADCAST_ADDR 28 /* 4 <ipaddr> */
+
+#define OPT_REQUESTED_IP 50 /* 4 <ipaddr> */
+#define OPT_LEASE_TIME 51 /* 4 <seconds> */
+#define OPT_MESSAGE_TYPE 53 /* 1 <msgtype> */
+#define OPT_SERVER_ID 54 /* 4 <ipaddr> */
+#define OPT_PARAMETER_LIST 55 /* n <optcode> * n */
+#define OPT_MESSAGE 56 /* n <errorstring> */
+#define OPT_CLASS_ID 60 /* n <opaque> */
+#define OPT_CLIENT_ID 61 /* n <opaque> */
+#define OPT_END 255
+
+/* DHCP message types */
+#define DHCPDISCOVER 1
+#define DHCPOFFER 2
+#define DHCPREQUEST 3
+#define DHCPDECLINE 4
+#define DHCPACK 5
+#define DHCPNAK 6
+#define DHCPRELEASE 7
+#define DHCPINFORM 8
+
+int init_dhcp_discover_msg(dhcp_msg *msg, void *hwaddr, uint32_t xid);
+
+int init_dhcp_request_msg(dhcp_msg *msg, void *hwaddr, uint32_t xid,
+ uint32_t ipaddr, uint32_t serveraddr);
+
+#endif
diff --git a/libnetutils/ifc_utils.c b/libnetutils/ifc_utils.c
new file mode 100644
index 0000000..88635d9
--- /dev/null
+++ b/libnetutils/ifc_utils.c
@@ -0,0 +1,428 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <linux/if.h>
+#include <linux/sockios.h>
+#include <linux/route.h>
+#include <linux/wireless.h>
+
+#ifdef ANDROID
+#define LOG_TAG "NetUtils"
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#else
+#include <stdio.h>
+#include <string.h>
+#define LOGD printf
+#define LOGW printf
+#endif
+
+static int ifc_ctl_sock = -1;
+void printerr(char *fmt, ...);
+
+static const char *ipaddr_to_string(uint32_t addr)
+{
+ struct in_addr in_addr;
+
+ in_addr.s_addr = addr;
+ return inet_ntoa(in_addr);
+}
+
+int ifc_init(void)
+{
+ if (ifc_ctl_sock == -1) {
+ ifc_ctl_sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (ifc_ctl_sock < 0) {
+ printerr("socket() failed: %s\n", strerror(errno));
+ }
+ }
+ return ifc_ctl_sock < 0 ? -1 : 0;
+}
+
+void ifc_close(void)
+{
+ if (ifc_ctl_sock != -1) {
+ (void)close(ifc_ctl_sock);
+ ifc_ctl_sock = -1;
+ }
+}
+
+static void ifc_init_ifr(const char *name, struct ifreq *ifr)
+{
+ memset(ifr, 0, sizeof(struct ifreq));
+ strncpy(ifr->ifr_name, name, IFNAMSIZ);
+ ifr->ifr_name[IFNAMSIZ - 1] = 0;
+}
+
+int ifc_get_hwaddr(const char *name, void *ptr)
+{
+ int r;
+ struct ifreq ifr;
+ ifc_init_ifr(name, &ifr);
+
+ r = ioctl(ifc_ctl_sock, SIOCGIFHWADDR, &ifr);
+ if(r < 0) return -1;
+
+ memcpy(ptr, &ifr.ifr_hwaddr.sa_data, 6);
+ return 0;
+}
+
+int ifc_get_ifindex(const char *name, int *if_indexp)
+{
+ int r;
+ struct ifreq ifr;
+ ifc_init_ifr(name, &ifr);
+
+ r = ioctl(ifc_ctl_sock, SIOCGIFINDEX, &ifr);
+ if(r < 0) return -1;
+
+ *if_indexp = ifr.ifr_ifindex;
+ return 0;
+}
+
+static int ifc_set_flags(const char *name, unsigned set, unsigned clr)
+{
+ struct ifreq ifr;
+ ifc_init_ifr(name, &ifr);
+
+ if(ioctl(ifc_ctl_sock, SIOCGIFFLAGS, &ifr) < 0) return -1;
+ ifr.ifr_flags = (ifr.ifr_flags & (~clr)) | set;
+ return ioctl(ifc_ctl_sock, SIOCSIFFLAGS, &ifr);
+}
+
+int ifc_up(const char *name)
+{
+ return ifc_set_flags(name, IFF_UP, 0);
+}
+
+int ifc_down(const char *name)
+{
+ return ifc_set_flags(name, 0, IFF_UP);
+}
+
+static void init_sockaddr_in(struct sockaddr *sa, in_addr_t addr)
+{
+ struct sockaddr_in *sin = (struct sockaddr_in *) sa;
+ sin->sin_family = AF_INET;
+ sin->sin_port = 0;
+ sin->sin_addr.s_addr = addr;
+}
+
+int ifc_set_addr(const char *name, in_addr_t addr)
+{
+ struct ifreq ifr;
+
+ ifc_init_ifr(name, &ifr);
+ init_sockaddr_in(&ifr.ifr_addr, addr);
+
+ return ioctl(ifc_ctl_sock, SIOCSIFADDR, &ifr);
+}
+
+int ifc_set_mask(const char *name, in_addr_t mask)
+{
+ struct ifreq ifr;
+
+ ifc_init_ifr(name, &ifr);
+ init_sockaddr_in(&ifr.ifr_addr, mask);
+
+ return ioctl(ifc_ctl_sock, SIOCSIFNETMASK, &ifr);
+}
+
+int ifc_get_info(const char *name, in_addr_t *addr, in_addr_t *mask, unsigned *flags)
+{
+ struct ifreq ifr;
+ ifc_init_ifr(name, &ifr);
+
+ if (addr != NULL) {
+ if(ioctl(ifc_ctl_sock, SIOCGIFADDR, &ifr) < 0) {
+ *addr = 0;
+ } else {
+ *addr = ((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr.s_addr;
+ }
+ }
+
+ if (mask != NULL) {
+ if(ioctl(ifc_ctl_sock, SIOCGIFNETMASK, &ifr) < 0) {
+ *mask = 0;
+ } else {
+ *mask = ((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr.s_addr;
+ }
+ }
+
+ if (flags != NULL) {
+ if(ioctl(ifc_ctl_sock, SIOCGIFFLAGS, &ifr) < 0) {
+ *flags = 0;
+ } else {
+ *flags = ifr.ifr_flags;
+ }
+ }
+
+ return 0;
+}
+
+
+int ifc_create_default_route(const char *name, in_addr_t addr)
+{
+ struct rtentry rt;
+
+ memset(&rt, 0, sizeof(rt));
+
+ rt.rt_dst.sa_family = AF_INET;
+ rt.rt_flags = RTF_UP | RTF_GATEWAY;
+ rt.rt_dev = (void*) name;
+ init_sockaddr_in(&rt.rt_genmask, 0);
+ init_sockaddr_in(&rt.rt_gateway, addr);
+
+ return ioctl(ifc_ctl_sock, SIOCADDRT, &rt);
+}
+
+int ifc_add_host_route(const char *name, in_addr_t addr)
+{
+ struct rtentry rt;
+ int result;
+
+ memset(&rt, 0, sizeof(rt));
+
+ rt.rt_dst.sa_family = AF_INET;
+ rt.rt_flags = RTF_UP | RTF_HOST;
+ rt.rt_dev = (void*) name;
+ init_sockaddr_in(&rt.rt_dst, addr);
+ init_sockaddr_in(&rt.rt_genmask, 0);
+ init_sockaddr_in(&rt.rt_gateway, 0);
+
+ ifc_init();
+ result = ioctl(ifc_ctl_sock, SIOCADDRT, &rt);
+ if (result < 0 && errno == EEXIST) {
+ result = 0;
+ }
+ ifc_close();
+ return result;
+}
+
+int ifc_disable(const char *ifname)
+{
+ int result;
+
+ ifc_init();
+ result = ifc_down(ifname);
+ ifc_set_addr(ifname, 0);
+ ifc_close();
+ return result;
+}
+
+int ifc_reset_connections(const char *ifname)
+{
+#ifdef HAVE_ANDROID_OS
+ int result;
+ in_addr_t myaddr;
+ struct ifreq ifr;
+
+ ifc_init();
+ ifc_get_info(ifname, &myaddr, NULL, NULL);
+ ifc_init_ifr(ifname, &ifr);
+ init_sockaddr_in(&ifr.ifr_addr, myaddr);
+ result = ioctl(ifc_ctl_sock, SIOCKILLADDR, &ifr);
+ ifc_close();
+
+ return result;
+#else
+ return 0;
+#endif
+}
+
+/*
+ * Remove the routes associated with the named interface.
+ */
+int ifc_remove_host_routes(const char *name)
+{
+ char ifname[64];
+ in_addr_t dest, gway, mask;
+ int flags, refcnt, use, metric, mtu, win, irtt;
+ struct rtentry rt;
+ FILE *fp;
+ struct in_addr addr;
+
+ fp = fopen("/proc/net/route", "r");
+ if (fp == NULL)
+ return -1;
+ /* Skip the header line */
+ if (fscanf(fp, "%*[^\n]\n") < 0) {
+ fclose(fp);
+ return -1;
+ }
+ ifc_init();
+ for (;;) {
+ int nread = fscanf(fp, "%63s%X%X%X%d%d%d%X%d%d%d\n",
+ ifname, &dest, &gway, &flags, &refcnt, &use, &metric, &mask,
+ &mtu, &win, &irtt);
+ if (nread != 11) {
+ break;
+ }
+ if ((flags & (RTF_UP|RTF_HOST)) != (RTF_UP|RTF_HOST)
+ || strcmp(ifname, name) != 0) {
+ continue;
+ }
+ memset(&rt, 0, sizeof(rt));
+ rt.rt_dev = (void *)name;
+ init_sockaddr_in(&rt.rt_dst, dest);
+ init_sockaddr_in(&rt.rt_gateway, gway);
+ init_sockaddr_in(&rt.rt_genmask, mask);
+ addr.s_addr = dest;
+ if (ioctl(ifc_ctl_sock, SIOCDELRT, &rt) < 0) {
+ LOGD("failed to remove route for %s to %s: %s",
+ ifname, inet_ntoa(addr), strerror(errno));
+ }
+ }
+ fclose(fp);
+ ifc_close();
+ return 0;
+}
+
+/*
+ * Return the address of the default gateway
+ *
+ * TODO: factor out common code from this and remove_host_routes()
+ * so that we only scan /proc/net/route in one place.
+ */
+int ifc_get_default_route(const char *ifname)
+{
+ char name[64];
+ in_addr_t dest, gway, mask;
+ int flags, refcnt, use, metric, mtu, win, irtt;
+ int result;
+ FILE *fp;
+
+ fp = fopen("/proc/net/route", "r");
+ if (fp == NULL)
+ return 0;
+ /* Skip the header line */
+ if (fscanf(fp, "%*[^\n]\n") < 0) {
+ fclose(fp);
+ return 0;
+ }
+ ifc_init();
+ result = 0;
+ for (;;) {
+ int nread = fscanf(fp, "%63s%X%X%X%d%d%d%X%d%d%d\n",
+ name, &dest, &gway, &flags, &refcnt, &use, &metric, &mask,
+ &mtu, &win, &irtt);
+ if (nread != 11) {
+ break;
+ }
+ if ((flags & (RTF_UP|RTF_GATEWAY)) == (RTF_UP|RTF_GATEWAY)
+ && dest == 0
+ && strcmp(ifname, name) == 0) {
+ result = gway;
+ break;
+ }
+ }
+ fclose(fp);
+ ifc_close();
+ return result;
+}
+
+/*
+ * Sets the specified gateway as the default route for the named interface.
+ */
+int ifc_set_default_route(const char *ifname, in_addr_t gateway)
+{
+ struct in_addr addr;
+ int result;
+
+ ifc_init();
+ addr.s_addr = gateway;
+ if ((result = ifc_create_default_route(ifname, gateway)) < 0) {
+ LOGD("failed to add %s as default route for %s: %s",
+ inet_ntoa(addr), ifname, strerror(errno));
+ }
+ ifc_close();
+ return result;
+}
+
+/*
+ * Removes the default route for the named interface.
+ */
+int ifc_remove_default_route(const char *ifname)
+{
+ struct rtentry rt;
+ int result;
+
+ ifc_init();
+ memset(&rt, 0, sizeof(rt));
+ rt.rt_dev = (void *)ifname;
+ rt.rt_flags = RTF_UP|RTF_GATEWAY;
+ init_sockaddr_in(&rt.rt_dst, 0);
+ if ((result = ioctl(ifc_ctl_sock, SIOCDELRT, &rt)) < 0) {
+ LOGD("failed to remove default route for %s: %s", ifname, strerror(errno));
+ }
+ ifc_close();
+ return result;
+}
+
+int
+ifc_configure(const char *ifname,
+ in_addr_t address,
+ in_addr_t netmask,
+ in_addr_t gateway,
+ in_addr_t dns1,
+ in_addr_t dns2) {
+
+ char dns_prop_name[PROPERTY_KEY_MAX];
+
+ ifc_init();
+
+ if (ifc_up(ifname)) {
+ printerr("failed to turn on interface %s: %s\n", ifname, strerror(errno));
+ ifc_close();
+ return -1;
+ }
+ if (ifc_set_addr(ifname, address)) {
+ printerr("failed to set ipaddr %s: %s\n", ipaddr_to_string(address), strerror(errno));
+ ifc_close();
+ return -1;
+ }
+ if (ifc_set_mask(ifname, netmask)) {
+ printerr("failed to set netmask %s: %s\n", ipaddr_to_string(netmask), strerror(errno));
+ ifc_close();
+ return -1;
+ }
+ if (ifc_create_default_route(ifname, gateway)) {
+ printerr("failed to set default route %s: %s\n", ipaddr_to_string(gateway), strerror(errno));
+ ifc_close();
+ return -1;
+ }
+
+ ifc_close();
+
+ snprintf(dns_prop_name, sizeof(dns_prop_name), "dhcp.%s.dns1", ifname);
+ property_set(dns_prop_name, dns1 ? ipaddr_to_string(dns1) : "");
+ snprintf(dns_prop_name, sizeof(dns_prop_name), "dhcp.%s.dns2", ifname);
+ property_set(dns_prop_name, dns2 ? ipaddr_to_string(dns2) : "");
+
+ return 0;
+}
diff --git a/libnetutils/ifc_utils.h b/libnetutils/ifc_utils.h
new file mode 100644
index 0000000..49b8747
--- /dev/null
+++ b/libnetutils/ifc_utils.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _IFC_UTILS_H_
+#define _IFC_UTILS_H_
+
+int ifc_init(void);
+
+int ifc_get_ifindex(const char *name, int *if_indexp);
+int ifc_get_hwaddr(const char *name, void *ptr);
+
+int ifc_up(const char *name);
+int ifc_down(const char *name);
+
+int ifc_set_addr(const char *name, unsigned addr);
+int ifc_set_mask(const char *name, unsigned mask);
+
+int ifc_create_default_route(const char *name, unsigned addr);
+
+int ifc_get_info(const char *name, unsigned *addr, unsigned *mask, unsigned *flags);
+
+#endif
diff --git a/libnetutils/packet.c b/libnetutils/packet.c
new file mode 100644
index 0000000..9388345
--- /dev/null
+++ b/libnetutils/packet.c
@@ -0,0 +1,239 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/uio.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <linux/if_packet.h>
+#include <linux/if_ether.h>
+#include <errno.h>
+
+#ifdef ANDROID
+#define LOG_TAG "DHCP"
+#include <cutils/log.h>
+#else
+#include <stdio.h>
+#include <string.h>
+#define LOGD printf
+#define LOGW printf
+#endif
+
+#include "dhcpmsg.h"
+
+int fatal();
+
+int open_raw_socket(const char *ifname, uint8_t *hwaddr, int if_index)
+{
+ int s, flag;
+ struct sockaddr_ll bindaddr;
+
+ if((s = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP))) < 0) {
+ return fatal("socket(PF_PACKET)");
+ }
+
+ memset(&bindaddr, 0, sizeof(bindaddr));
+ bindaddr.sll_family = AF_PACKET;
+ bindaddr.sll_protocol = htons(ETH_P_IP);
+ bindaddr.sll_halen = ETH_ALEN;
+ memcpy(bindaddr.sll_addr, hwaddr, ETH_ALEN);
+ bindaddr.sll_ifindex = if_index;
+
+ if (bind(s, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) {
+ return fatal("Cannot bind raw socket to interface");
+ }
+
+ return s;
+}
+
+static uint32_t checksum(void *buffer, unsigned int count, uint32_t startsum)
+{
+ uint16_t *up = (uint16_t *)buffer;
+ uint32_t sum = startsum;
+ uint32_t upper16;
+
+ while (count > 1) {
+ sum += *up++;
+ count -= 2;
+ }
+ if (count > 0) {
+ sum += (uint16_t) *(uint8_t *)up;
+ }
+ while ((upper16 = (sum >> 16)) != 0) {
+ sum = (sum & 0xffff) + upper16;
+ }
+ return sum;
+}
+
+static uint32_t finish_sum(uint32_t sum)
+{
+ return ~sum & 0xffff;
+}
+
+int send_packet(int s, int if_index, struct dhcp_msg *msg, int size,
+ uint32_t saddr, uint32_t daddr, uint32_t sport, uint32_t dport)
+{
+ struct iphdr ip;
+ struct udphdr udp;
+ struct iovec iov[3];
+ uint32_t udpsum;
+ uint16_t temp;
+ struct msghdr msghdr;
+ struct sockaddr_ll destaddr;
+
+ ip.version = IPVERSION;
+ ip.ihl = sizeof(ip) >> 2;
+ ip.tos = 0;
+ ip.tot_len = htons(sizeof(ip) + sizeof(udp) + size);
+ ip.id = 0;
+ ip.frag_off = 0;
+ ip.ttl = IPDEFTTL;
+ ip.protocol = IPPROTO_UDP;
+ ip.check = 0;
+ ip.saddr = saddr;
+ ip.daddr = daddr;
+ ip.check = finish_sum(checksum(&ip, sizeof(ip), 0));
+
+ udp.source = htons(sport);
+ udp.dest = htons(dport);
+ udp.len = htons(sizeof(udp) + size);
+ udp.check = 0;
+
+ /* Calculate checksum for pseudo header */
+ udpsum = checksum(&ip.saddr, sizeof(ip.saddr), 0);
+ udpsum = checksum(&ip.daddr, sizeof(ip.daddr), udpsum);
+ temp = htons(IPPROTO_UDP);
+ udpsum = checksum(&temp, sizeof(temp), udpsum);
+ temp = udp.len;
+ udpsum = checksum(&temp, sizeof(temp), udpsum);
+
+ /* Add in the checksum for the udp header */
+ udpsum = checksum(&udp, sizeof(udp), udpsum);
+
+ /* Add in the checksum for the data */
+ udpsum = checksum(msg, size, udpsum);
+ udp.check = finish_sum(udpsum);
+
+ iov[0].iov_base = (char *)&ip;
+ iov[0].iov_len = sizeof(ip);
+ iov[1].iov_base = (char *)&udp;
+ iov[1].iov_len = sizeof(udp);
+ iov[2].iov_base = (char *)msg;
+ iov[2].iov_len = size;
+ memset(&destaddr, 0, sizeof(destaddr));
+ destaddr.sll_family = AF_PACKET;
+ destaddr.sll_protocol = htons(ETH_P_IP);
+ destaddr.sll_ifindex = if_index;
+ destaddr.sll_halen = ETH_ALEN;
+ memcpy(destaddr.sll_addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN);
+
+ msghdr.msg_name = &destaddr;
+ msghdr.msg_namelen = sizeof(destaddr);
+ msghdr.msg_iov = iov;
+ msghdr.msg_iovlen = sizeof(iov) / sizeof(struct iovec);
+ msghdr.msg_flags = 0;
+ msghdr.msg_control = 0;
+ msghdr.msg_controllen = 0;
+ return sendmsg(s, &msghdr, 0);
+}
+
+int receive_packet(int s, struct dhcp_msg *msg)
+{
+ int nread;
+ int is_valid;
+ struct dhcp_packet {
+ struct iphdr ip;
+ struct udphdr udp;
+ struct dhcp_msg dhcp;
+ } packet;
+ int dhcp_size;
+ uint32_t sum;
+ uint16_t temp;
+ uint32_t saddr, daddr;
+
+ nread = read(s, &packet, sizeof(packet));
+ if (nread < 0) {
+ return -1;
+ }
+ /*
+ * The raw packet interface gives us all packets received by the
+ * network interface. We need to filter out all packets that are
+ * not meant for us.
+ */
+ is_valid = 0;
+ if (nread < (int)(sizeof(struct iphdr) + sizeof(struct udphdr))) {
+#if VERBOSE
+ LOGD("Packet is too small (%d) to be a UDP datagram", nread);
+#endif
+ } else if (packet.ip.version != IPVERSION || packet.ip.ihl != (sizeof(packet.ip) >> 2)) {
+#if VERBOSE
+ LOGD("Not a valid IP packet");
+#endif
+ } else if (nread < ntohs(packet.ip.tot_len)) {
+#if VERBOSE
+ LOGD("Packet was truncated (read %d, needed %d)", nread, ntohs(packet.ip.tot_len));
+#endif
+ } else if (packet.ip.protocol != IPPROTO_UDP) {
+#if VERBOSE
+ LOGD("IP protocol (%d) is not UDP", packet.ip.protocol);
+#endif
+ } else if (packet.udp.dest != htons(PORT_BOOTP_CLIENT)) {
+#if VERBOSE
+ LOGD("UDP dest port (%d) is not DHCP client", ntohs(packet.udp.dest));
+#endif
+ } else {
+ is_valid = 1;
+ }
+
+ if (!is_valid) {
+ return -1;
+ }
+
+ /* Seems like it's probably a valid DHCP packet */
+ /* validate IP header checksum */
+ sum = finish_sum(checksum(&packet.ip, sizeof(packet.ip), 0));
+ if (sum != 0) {
+ LOGW("IP header checksum failure (0x%x)", packet.ip.check);
+ return -1;
+ }
+ /*
+ * Validate the UDP checksum.
+ * Since we don't need the IP header anymore, we "borrow" it
+ * to construct the pseudo header used in the checksum calculation.
+ */
+ dhcp_size = ntohs(packet.udp.len) - sizeof(packet.udp);
+ saddr = packet.ip.saddr;
+ daddr = packet.ip.daddr;
+ nread = ntohs(packet.ip.tot_len);
+ memset(&packet.ip, 0, sizeof(packet.ip));
+ packet.ip.saddr = saddr;
+ packet.ip.daddr = daddr;
+ packet.ip.protocol = IPPROTO_UDP;
+ packet.ip.tot_len = packet.udp.len;
+ temp = packet.udp.check;
+ packet.udp.check = 0;
+ sum = finish_sum(checksum(&packet, nread, 0));
+ packet.udp.check = temp;
+ if (temp != sum) {
+ LOGW("UDP header checksum failure (0x%x should be 0x%x)", sum, temp);
+ return -1;
+ }
+ memcpy(msg, &packet.dhcp, dhcp_size);
+ return dhcp_size;
+}
diff --git a/libnetutils/packet.h b/libnetutils/packet.h
new file mode 100644
index 0000000..aade392
--- /dev/null
+++ b/libnetutils/packet.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _WIFI_PACKET_H_
+#define _WIFI_PACKET_H_
+
+int open_raw_socket(const char *ifname, uint8_t *hwaddr, int if_index);
+int send_packet(int s, int if_index, struct dhcp_msg *msg, int size,
+ uint32_t saddr, uint32_t daddr, uint32_t sport, uint32_t dport);
+int receive_packet(int s, struct dhcp_msg *msg);
+
+#endif