diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:32:55 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:32:55 -0800 |
commit | dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0 (patch) | |
tree | 2ba8d1a0846d69b18f623515e8d9b5d9fe38b590 /libnetutils | |
parent | e54eebbf1a908d65ee8cf80bab62821c05666d70 (diff) | |
download | system_core-dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0.zip system_core-dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0.tar.gz system_core-dd7bc3319deb2b77c5d07a51b7d6cd7e11b5beb0.tar.bz2 |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'libnetutils')
-rw-r--r-- | libnetutils/Android.mk | 23 | ||||
-rw-r--r-- | libnetutils/dhcp_utils.c | 207 | ||||
-rw-r--r-- | libnetutils/dhcpclient.c | 562 | ||||
-rw-r--r-- | libnetutils/dhcpmsg.c | 100 | ||||
-rw-r--r-- | libnetutils/dhcpmsg.h | 106 | ||||
-rw-r--r-- | libnetutils/ifc_utils.c | 428 | ||||
-rw-r--r-- | libnetutils/ifc_utils.h | 35 | ||||
-rw-r--r-- | libnetutils/packet.c | 239 | ||||
-rw-r--r-- | libnetutils/packet.h | 25 |
9 files changed, 1725 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..bad2e2f --- /dev/null +++ b/libnetutils/dhcp_utils.c @@ -0,0 +1,207 @@ +/* + * 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) +{ + char result_prop_name[PROPERTY_KEY_MAX]; + const char *ctrl_prop = "ctl.stop"; + const char *desired_status = "stopped"; + + snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result", + DHCP_PROP_NAME_PREFIX, + interface); + /* 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; + } + property_set(result_prop_name, "failed"); + return 0; +} + +/** + * Release the current DHCP client lease. + */ +int dhcp_release_lease(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 |