--- /dev/null
+/*
+ * Network Configuration Module
+ *
+ * Copyright (c) 2000 - 2012 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * 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 <net/if.h>
+#include <linux/if_packet.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/ether.h>
+#include <net/ethernet.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+#include <glib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdbool.h>
+
+#include "ip-conflict-detect.h"
+#include "network-state.h"
+#include "log.h"
+#include "neterror.h"
+
+#define ARP_PACKET_SIZE 60
+#define MAX_SIZE_ERROR_BUFFER 256
+#define IP_ADDRESS_LENGTH 4
+#define MAC_ADDRESS_LENGTH 6
+#define WLAN_MAC_ADDR_MAX 20
+
+#define MIN_ARP_SEND_TIME 2000
+#define MAX_ARP_SEND_TIME 32000
+#define GRATUITOUS_ARP_MAC_ADDR "00:00:00:00:00:00"
+#define UCHAR_TO_ADDRESS(hwaddr, buf) do {\
+ snprintf(buf, WLAN_MAC_ADDR_MAX,\
+ "%02X:%02X:%02X:%02X:%02X:%02X", hwaddr[0], hwaddr[1],\
+ hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5]);\
+ } while (0)
+
+struct arp_message {
+ /* Ethernet header */
+ unsigned char h_dest[MAC_ADDRESS_LENGTH]; /* destination ether addr */
+ unsigned char h_source[MAC_ADDRESS_LENGTH]; /* source ether addr */
+ unsigned short h_proto; /* packet type ID field */
+
+ /* ARP packet */
+ unsigned short hw_type; /* hardware type(ARPHRD_ETHER) */
+ unsigned short p_type; /* protocol type(ETH_P_IP) */
+ unsigned char hw_len; /* hardware address length */
+ unsigned char p_len; /* protocol address length */
+ unsigned short operation; /* ARP opcode */
+ unsigned char s_hwaddr[MAC_ADDRESS_LENGTH]; /* sender hardware address */
+ unsigned char s_IPaddr[IP_ADDRESS_LENGTH]; /* sender IP address */
+ unsigned char t_hwaddr[MAC_ADDRESS_LENGTH]; /* target hardware address */
+ unsigned char t_IPaddr[IP_ADDRESS_LENGTH]; /* target IP address */
+ unsigned char pad[18]; /* pad for min. Ethernet payload (60 bytes) */
+};
+
+typedef enum {
+ IP_CONFLICT_MODE_EXPONENTIAL = 0x00,
+ IP_CONFLICT_MODE_PERIODIC,
+ IP_CONFLICT_MODE_MAX
+} ip_conflict_mode_e;
+
+typedef enum {
+ NETCONFIG_IP_CONFLICT_STATE_UNKNOWN,
+ NETCONFIG_IP_CONFLICT_STATE_CONFLICT_NOT_DETECTED,
+ NETCONFIG_IP_CONFLICT_STATE_CONFLICT_DETECTED
+} ip_conflict_state_e;
+
+struct timer_data {
+ guint initial_time;
+ guint timeout;
+ ip_conflict_mode_e mode;
+};
+static struct timer_data td = {
+ MIN_ARP_SEND_TIME, MIN_ARP_SEND_TIME, IP_CONFLICT_MODE_PERIODIC
+};
+
+int ioctl_sock;
+bool is_ip_conflict_detect_enabled = true;
+static gboolean send_arp(gpointer data);
+static void __netconfig_wifi_notify_ip_conflict(char *state, char *mac);
+ip_conflict_state_e conflict_state = NETCONFIG_IP_CONFLICT_STATE_CONFLICT_NOT_DETECTED;
+
+struct sock_data *sd;
+
+typedef unsigned int in_addr_t;
+
+union uchar_to_uint {
+ unsigned int uint;
+ unsigned char uchar[IP_ADDRESS_LENGTH];
+};
+
+static unsigned int __convert_uchar_to_uint(unsigned char b[IP_ADDRESS_LENGTH])
+{
+ int idx = 0;
+ union uchar_to_uint u;
+ for (; idx < IP_ADDRESS_LENGTH; ++idx)
+ u.uchar[idx] = b[idx];
+
+ return u.uint;
+}
+
+static gboolean __arp_reply_timeout_cb(gpointer data)
+{
+ sd->iteration++;
+ sd->arp_reply_timer = -1;
+ if (sd->timer_id != -1)
+ g_source_remove(sd->timer_id);
+
+ if (conflict_state != NETCONFIG_IP_CONFLICT_STATE_CONFLICT_NOT_DETECTED && sd->iteration == 5) {
+ sd->iteration = 0;
+ conflict_state = NETCONFIG_IP_CONFLICT_STATE_CONFLICT_NOT_DETECTED;
+ __netconfig_wifi_notify_ip_conflict("resolved", GRATUITOUS_ARP_MAC_ADDR);
+ }
+
+ sd->timer_id = g_timeout_add(sd->timeout, send_arp, sd);
+ return G_SOURCE_REMOVE;
+}
+
+static gboolean __netconfig_check_arp_receive(GIOChannel *source,
+ GIOCondition condition, gpointer data)
+{
+ struct sock_data *sd = data;
+ gchar buffer[ARP_PACKET_SIZE] = {0, };
+ gsize bytes_read = 0;
+ struct arp_message arp_recv;
+ char sbuf[WLAN_MAC_ADDR_MAX];
+ char tbuf[WLAN_MAC_ADDR_MAX];
+ const char *default_ip = NULL;
+
+ if (g_io_channel_read_chars(source, buffer, ARP_PACKET_SIZE,
+ &bytes_read, NULL) == G_IO_STATUS_NORMAL) {
+ unsigned int target_ip = 0;
+
+ memset(&arp_recv, 0, sizeof(arp_recv));
+ memcpy(&arp_recv, buffer, sizeof(buffer));
+
+ default_ip = netconfig_get_default_ipaddress();
+ if (default_ip == NULL) {
+ INFO("ip address is not set yet");
+ goto out;
+ }
+ target_ip = inet_addr(default_ip);
+
+
+ /* Only handle ARP replies */
+ if (arp_recv.operation != htons(ARPOP_REPLY))
+ goto out;
+
+ UCHAR_TO_ADDRESS(arp_recv.t_hwaddr, tbuf);
+ UCHAR_TO_ADDRESS(arp_recv.s_hwaddr, sbuf);
+
+ int zero_mac = strcmp(tbuf , GRATUITOUS_ARP_MAC_ADDR);
+ if (zero_mac == 0) {
+ DBG("Broadcast packet.\n");
+ goto skip;
+ }
+ DBG("our mac= %s source mac= %s target mac= %s", netconfig_get_default_mac_address(), sbuf, tbuf);
+ int mac_cmp = strcmp(tbuf , netconfig_get_default_mac_address());
+ if (mac_cmp != 0) {
+ INFO("Packet not intended to us.\n");
+ goto out;
+ }
+skip:
+ mac_cmp = strcmp(sbuf, netconfig_get_default_mac_address());
+ DBG("target ip = %d source ip = %d", target_ip, __convert_uchar_to_uint(arp_recv.s_IPaddr));
+ if ((mac_cmp != 0) && (__convert_uchar_to_uint(arp_recv.s_IPaddr) == target_ip)) {
+ sd->iteration = 0;
+ if (conflict_state != NETCONFIG_IP_CONFLICT_STATE_CONFLICT_DETECTED) {
+ INFO("ip conflict is detected !\n");
+ conflict_state = NETCONFIG_IP_CONFLICT_STATE_CONFLICT_DETECTED;
+ __netconfig_wifi_notify_ip_conflict("conflict", sbuf);
+ }
+
+ if (sd->arp_reply_timer != -1) {
+ g_source_remove(sd->arp_reply_timer);
+ sd->arp_reply_timer = -1;
+ }
+
+ if (sd->timer_id)
+ g_source_remove(sd->timer_id);
+ sd->timer_id = g_timeout_add(sd->timeout, send_arp, sd);
+
+ }
+ }
+
+out:
+ return TRUE;
+}
+
+static gboolean send_arp(gpointer data)
+{
+ struct sock_data *sd = data;
+ struct ether_addr *source_mac = NULL;
+ struct arp_message arp;
+ char error_buf[MAX_SIZE_ERROR_BUFFER] = {0, };
+ unsigned int source_ip = 0;
+ unsigned int target_ip = 0;
+ const unsigned char broadcast_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ struct sockaddr_ll addr = {0};
+ struct ifreq net_ifr;
+ int ifindex = 0;
+ errno = 0;
+ const char *default_ip = NULL;
+
+ const char *mac = netconfig_get_default_mac_address();
+ if (mac == NULL)
+ goto err;
+ source_mac = ether_aton(mac);
+ if (source_mac == NULL) {
+ INFO("Mac address is NULL");
+ goto err;
+ }
+
+ memset(&arp, 0, sizeof(arp));
+
+ unsigned char broadcast_mac_addr[MAC_ADDRESS_LENGTH];
+ memset(broadcast_mac_addr, 0xff, sizeof(broadcast_mac_addr));
+ memcpy(arp.h_dest, broadcast_mac_addr, MAC_ADDRESS_LENGTH); /* MAC dest */
+ memcpy(arp.h_source, source_mac, MAC_ADDRESS_LENGTH); /* MAC source */
+
+ arp.h_proto = htons(ETH_P_ARP); /* protocol type (Ethernet) */
+ arp.hw_type = htons(ARPHRD_ETHER); /* hardware type */
+ arp.p_type = htons(ETH_P_IP); /* protocol type (ARP message) */
+ arp.hw_len = MAC_ADDRESS_LENGTH; /* hardware address length */
+ arp.p_len = IP_ADDRESS_LENGTH; /* protocol address length */
+ arp.operation = htons(ARPOP_REQUEST); /* ARP op code */
+ default_ip = netconfig_get_default_ipaddress();
+ if (default_ip == NULL) {
+ INFO("ip address is not set yet");
+ goto err;
+ }
+
+ source_ip = inet_addr(default_ip);
+ target_ip = inet_addr(default_ip);
+ memcpy(arp.s_IPaddr, &source_ip, IP_ADDRESS_LENGTH); /* source IP address */
+ memcpy(arp.s_hwaddr, source_mac, MAC_ADDRESS_LENGTH); /* source hardware address */
+ memcpy(arp.t_IPaddr, &target_ip, IP_ADDRESS_LENGTH); /* target IP addressshek" */
+
+ memset(&net_ifr, 0, sizeof(net_ifr));
+ /* ifreq structure creation */
+ size_t if_name_len = strlen(netconfig_get_default_ifname());
+ if (if_name_len < sizeof(net_ifr.ifr_name)) {
+ memcpy(net_ifr.ifr_name, netconfig_get_default_ifname(), if_name_len);
+ net_ifr.ifr_name[if_name_len] = 0;
+ } else {
+ INFO("Error : Interface name is too long");
+ goto err;
+ }
+
+ if (ioctl(sd->chk_conflict_sd, SIOCGIFINDEX, &net_ifr) == -1) {
+ strerror_r(errno, error_buf, MAX_SIZE_ERROR_BUFFER);
+ INFO("ioctl Failed. Error..... = %s\n", error_buf);
+ goto err;
+ }
+
+ ifindex = net_ifr.ifr_ifindex;
+ /* Construct the destination address */
+ addr.sll_family = AF_PACKET;
+ addr.sll_ifindex = ifindex;
+ addr.sll_halen = ETHER_ADDR_LEN;
+ addr.sll_protocol = htons(ETH_P_ARP);
+ memcpy(addr.sll_addr, broadcast_addr, ETHER_ADDR_LEN);
+
+ if (sendto(sd->chk_conflict_sd, &arp, sizeof(arp), 0, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
+ strerror_r(errno, error_buf, MAX_SIZE_ERROR_BUFFER);
+ INFO("Sending ARP Packet Failed. Error. = %s\n", error_buf);
+ /* close socket */
+ if (-1 < sd->chk_conflict_sd) {
+ close(sd->chk_conflict_sd);
+ sd->chk_conflict_sd = -1;
+ }
+
+ /* reopen socket */
+ if ((sd->chk_conflict_sd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP))) == -1) {
+ INFO("socket %d", sd->chk_conflict_sd);
+ strerror_r(errno, error_buf, MAX_SIZE_ERROR_BUFFER);
+ INFO("socket Failed. Error = %s\n", error_buf);
+ }
+ goto err;
+ } else {
+ DBG("Sent ARP Packet \n");
+ }
+
+ g_source_remove(sd->timer_id);
+ if (td.mode == IP_CONFLICT_MODE_PERIODIC)
+ sd->timeout = td.initial_time;
+ else if (td.mode == IP_CONFLICT_MODE_EXPONENTIAL && 2*sd->timeout < MAX_ARP_SEND_TIME)
+ sd->timeout *= 2;
+
+ /* Adding timeout callback for arp request */
+ sd->arp_reply_timer = g_timeout_add(1000, __arp_reply_timeout_cb,
+ (gpointer) &sd->arp_reply_timer);
+ return FALSE;
+err:
+ if (sd->timer_id)
+ g_source_remove(sd->timer_id);
+ sd->timer_id = g_timeout_add(sd->timeout, send_arp, sd);
+ return FALSE;
+}
+
+struct sock_data * start_ip_conflict_mon(void)
+{
+ if (is_ip_conflict_detect_enabled == false) {
+ INFO("detection mode is set to false");
+ return NULL;
+ }
+
+ char error_buf[MAX_SIZE_ERROR_BUFFER] = {0, };
+
+ sd = g_try_malloc0(sizeof(struct sock_data));
+ if (sd == NULL) {
+ INFO("Failed to malloc sock_data");
+ return NULL;
+ }
+ sd->chk_conflict_data_id = -1;
+ sd->chk_conflict_sd = -1;
+ sd->timer_id = 0;
+ sd->iteration = 0;
+
+ if ((sd->chk_conflict_sd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP))) == -1) {
+ strerror_r(errno, error_buf, MAX_SIZE_ERROR_BUFFER);
+ INFO("socket Failed. Error = %s\n", error_buf);
+ g_free(sd);
+ return NULL;
+ } else {
+ sd->chk_conflict_sock_io = g_io_channel_unix_new(sd->chk_conflict_sd);
+ if (sd->chk_conflict_sock_io == NULL) {
+ INFO("Failed to create channel");
+ INFO("Exit");
+ g_free(sd);
+ return NULL;
+ }
+
+ g_io_channel_set_close_on_unref(sd->chk_conflict_sock_io, TRUE);
+
+ if (G_IO_STATUS_NORMAL != g_io_channel_set_encoding(sd->chk_conflict_sock_io,
+ NULL, NULL))
+ INFO("Failed to set encoding NULL on io channel");
+ if (G_IO_STATUS_NORMAL != g_io_channel_set_flags(sd->chk_conflict_sock_io,
+ G_IO_FLAG_NONBLOCK, NULL))
+ INFO("Failed to set flags on io channel");
+ sd->chk_conflict_data_id = g_io_add_watch(sd->chk_conflict_sock_io, G_IO_IN,
+ __netconfig_check_arp_receive, sd);
+ DBG("socket %d", sd->chk_conflict_sd);
+
+ sd->timeout = td.initial_time;
+ send_arp(sd);
+ return sd;
+ }
+}
+
+void stop_ip_conflict_mon()
+{
+ INFO("+");
+ GError* error = NULL;
+ if (sd == NULL) {
+ INFO("sd is NULL");
+ return;
+ }
+ if (-1 < sd->chk_conflict_sd) {
+ if (G_IO_STATUS_NORMAL !=
+ g_io_channel_shutdown(sd->chk_conflict_sock_io, FALSE,
+ &error)) {
+ INFO("Failure received while shutdown io channel[%d]:[%s]", error->code, error->message);
+ g_error_free(error);
+ }
+ g_io_channel_unref(sd->chk_conflict_sock_io);
+ g_source_remove(sd->chk_conflict_data_id);
+ sd->chk_conflict_data_id = -1;
+ close(sd->chk_conflict_sd);
+ sd->chk_conflict_sd = -1;
+ }
+ if (sd->timer_id > 0) {
+ g_source_remove(sd->timer_id);
+ sd->timer_id = 0;
+ }
+ g_free(sd);
+ sd = NULL;
+ INFO("Monitoring stopped");
+}
+
+static void __netconfig_wifi_notify_ip_conflict(char *state, char *mac)
+{
+ GVariantBuilder *builder = NULL;
+
+ builder = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
+ g_variant_builder_add(builder, "{sv}", "state", g_variant_new_string(state));
+ g_variant_builder_add(builder, "{sv}", "mac", g_variant_new_string(mac));
+
+ wifi_emit_ip_conflict_event((Wifi *)get_wifi_object(), g_variant_builder_end(builder));
+ g_variant_builder_unref(builder);
+
+ return;
+}
+
+gboolean handle_ip_conflict_set_enable(Wifi *wifi, GDBusMethodInvocation *context,
+ bool detect)
+{
+ g_return_val_if_fail(wifi != NULL, FALSE);
+
+
+ if (detect == false) {
+ is_ip_conflict_detect_enabled = false;
+ conflict_state = NETCONFIG_IP_CONFLICT_STATE_UNKNOWN;
+ if (sd != NULL)
+ stop_ip_conflict_mon();
+ else {
+ netconfig_error_dbus_method_return(context, NETCONFIG_ERROR_INTERNAL, "AlreadyExists");
+ wifi_complete_ip_conflict_set_enable(wifi, context);
+ return FALSE;
+ }
+ } else {
+ is_ip_conflict_detect_enabled = true;
+ conflict_state = NETCONFIG_IP_CONFLICT_STATE_CONFLICT_NOT_DETECTED;
+ if (sd == NULL) {
+ if (start_ip_conflict_mon() == NULL) {
+ INFO("Failed to start IP conflict monitoring");
+ netconfig_error_dbus_method_return(context,
+ NETCONFIG_ERROR_INTERNAL, "Failed");
+ wifi_complete_ip_conflict_set_enable(wifi, context);
+ return FALSE;
+ }
+ } else {
+ netconfig_error_dbus_method_return(context, NETCONFIG_ERROR_INTERNAL, "AlreadyExists");
+ wifi_complete_ip_conflict_set_enable(wifi, context);
+ return FALSE;
+ }
+ }
+
+ wifi_complete_ip_conflict_set_enable(wifi, context);
+ return TRUE;
+}
+
+gboolean handle_is_ip_conflict_detect_enabled(Wifi *wifi, GDBusMethodInvocation *context)
+{
+ g_return_val_if_fail(wifi != NULL, FALSE);
+ GVariant *param = NULL;
+ param = g_variant_new("(b)", is_ip_conflict_detect_enabled);
+ g_dbus_method_invocation_return_value(context, param);
+ return TRUE;
+}
+
+gboolean handle_set_ip_conflict_mode(Wifi *wifi, GDBusMethodInvocation *context,
+ guint mode, guint initial_time)
+{
+ g_return_val_if_fail(wifi != NULL, FALSE);
+ if (initial_time < MAX_ARP_SEND_TIME && initial_time > MIN_ARP_SEND_TIME)
+ return FALSE;
+
+ td.initial_time = 1000 * initial_time;
+ td.mode = mode;
+ // remove timer
+ stop_ip_conflict_mon();
+ start_ip_conflict_mon();
+ wifi_complete_set_ip_conflict_mode(wifi, context);
+ return TRUE;
+}
+
+gboolean handle_get_ip_conflict_state(Wifi *wifi, GDBusMethodInvocation *context)
+{
+ g_return_val_if_fail(wifi != NULL, FALSE);
+ GVariant *param = NULL;
+ param = g_variant_new("(u)", conflict_state);
+ g_dbus_method_invocation_return_value(context, param);
+ return TRUE;
+}