+#if defined TIZEN_EXT
+ err = __connman_ipconfig_gateway_add(ipconfig_ipv4, service);
+#else
+ err = __connman_ipconfig_gateway_add(ipconfig_ipv4);
+#endif
+ if (err < 0)
+ goto err;
+
+ __connman_service_save(service);
+
+ return;
+
+err:
+ connman_network_set_error(network,
+ CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL);
+}
+
+static void dhcp_failure(struct connman_network *network)
+{
+ struct connman_service *service;
+ struct connman_ipconfig *ipconfig_ipv4;
+
+ service = connman_service_lookup_from_network(network);
+ if (!service)
+ return;
+
+ ipconfig_ipv4 = __connman_service_get_ip4config(service);
+
+ DBG("lease lost for ipconfig %p", ipconfig_ipv4);
+
+ if (!ipconfig_ipv4)
+ return;
+
+ __connman_ipconfig_address_remove(ipconfig_ipv4);
+ __connman_ipconfig_gateway_remove(ipconfig_ipv4);
+}
+
+static void dhcp_callback(struct connman_ipconfig *ipconfig,
+ struct connman_network *network,
+ bool success, gpointer data)
+{
+ network->connecting = false;
+
+ if (success)
+ dhcp_success(network);
+ else
+ dhcp_failure(network);
+}
+
+static int set_connected_manual(struct connman_network *network)
+{
+ int err = 0;
+ struct connman_service *service;
+ struct connman_ipconfig *ipconfig;
+
+ DBG("network %p", network);
+
+ network->connecting = false;
+
+ service = connman_service_lookup_from_network(network);
+ ipconfig = __connman_service_get_ip4config(service);
+ __connman_ipconfig_enable(ipconfig);
+
+ if (!__connman_ipconfig_get_local(ipconfig))
+ __connman_service_read_ip4config(service);
+
+ if (connman_setting_get_bool("AddressConflictDetection")) {
+ err = start_acd(network);
+ if (!err)
+ return 0;
+
+ /* On error proceed without ACD. */
+ }
+
+ err = __connman_ipconfig_address_add(ipconfig);
+ if (err < 0)
+ goto err;
+
+#if defined TIZEN_EXT
+ err = __connman_ipconfig_gateway_add(ipconfig, service);
+#else
+ err = __connman_ipconfig_gateway_add(ipconfig);
+#endif
+ if (err < 0)
+ goto err;
+
+err:
+ return err;
+}
+
+static void remove_dhcp_timeout(struct connman_network *network)
+{
+ if (network->dhcp_timeout > 0) {
+ g_source_remove(network->dhcp_timeout);
+ network->dhcp_timeout = 0;
+ }
+}
+
+static int set_connected_dhcp(struct connman_network *network)
+{
+ struct connman_service *service;
+ struct connman_ipconfig *ipconfig_ipv4;
+ int err;
+
+ DBG("network %p", network);
+ remove_dhcp_timeout(network);
+
+ service = connman_service_lookup_from_network(network);
+ ipconfig_ipv4 = __connman_service_get_ip4config(service);
+ __connman_ipconfig_enable(ipconfig_ipv4);
+
+ err = __connman_dhcp_start(ipconfig_ipv4, network,
+ dhcp_callback, NULL);
+ if (err < 0) {
+ connman_error("Can not request DHCP lease");
+ return err;
+ }
+
+ return 0;
+}
+
+static gboolean set_connected_dhcp_timout(gpointer data)
+{
+ struct connman_network *network = data;
+ struct connman_service *service;
+ struct connman_ipconfig *ipconfig;
+ enum connman_ipconfig_method method;
+
+ network->dhcp_timeout = 0;
+
+ service = connman_service_lookup_from_network(network);
+ if (!service)
+ return FALSE;
+
+ ipconfig = __connman_service_get_ip4config(service);
+ if (!ipconfig)
+ return FALSE;
+
+ /* Method is still DHCP? */
+ method = __connman_ipconfig_get_method(ipconfig);
+ if (method == CONNMAN_IPCONFIG_METHOD_DHCP)
+ set_connected_dhcp(network);
+
+ return FALSE;
+}
+
+void connman_network_set_connected_dhcp_later(struct connman_network *network,
+ uint32_t sec)
+{
+ remove_dhcp_timeout(network);
+
+ network->dhcp_timeout =
+ g_timeout_add_seconds_full(G_PRIORITY_HIGH,
+ sec,
+ set_connected_dhcp_timout,
+ network,
+ NULL);
+}
+
+static int manual_ipv6_set(struct connman_network *network,
+ struct connman_ipconfig *ipconfig_ipv6)
+{
+ struct connman_service *service;
+ int err;
+
+ DBG("network %p ipv6 %p", network, ipconfig_ipv6);
+
+ service = connman_service_lookup_from_network(network);
+ if (!service)
+ return -EINVAL;
+
+ if (!__connman_ipconfig_get_local(ipconfig_ipv6))
+ __connman_service_read_ip6config(service);
+
+ __connman_ipconfig_enable_ipv6(ipconfig_ipv6);
+
+ err = __connman_ipconfig_address_add(ipconfig_ipv6);
+ if (err < 0) {
+ connman_network_set_error(network,
+ CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL);
+ return err;
+ }
+
+#if defined TIZEN_EXT
+ err = __connman_ipconfig_gateway_add(ipconfig_ipv6, service);
+#else
+ err = __connman_ipconfig_gateway_add(ipconfig_ipv6);
+#endif
+ if (err < 0)
+ return err;
+
+ __connman_device_set_network(network->device, network);
+
+ connman_network_set_associating(network, false);
+
+ network->connecting = false;
+
+ return 0;
+}
+
+static void stop_dhcpv6(struct connman_network *network)
+{
+ network->connecting = false;
+
+ __connman_dhcpv6_stop(network);
+}
+
+static void dhcpv6_release_callback(struct connman_network *network,
+ enum __connman_dhcpv6_status status,
+ gpointer data)
+{
+ DBG("status %d", status);
+
+ stop_dhcpv6(network);
+}
+
+static void release_dhcpv6(struct connman_network *network)
+{
+ __connman_dhcpv6_start_release(network, dhcpv6_release_callback);
+ stop_dhcpv6(network);
+}
+
+static void dhcpv6_info_callback(struct connman_network *network,
+ enum __connman_dhcpv6_status status,
+ gpointer data)
+{
+ DBG("status %d", status);
+
+ stop_dhcpv6(network);
+}
+
+static int dhcpv6_set_addresses(struct connman_network *network)
+{
+ struct connman_service *service;
+ struct connman_ipconfig *ipconfig_ipv6;
+ int err = -EINVAL;
+
+ service = connman_service_lookup_from_network(network);
+ if (!service)
+ goto err;
+
+ network->connecting = false;
+
+ ipconfig_ipv6 = __connman_service_get_ip6config(service);
+#if defined TIZEN_EXT
+ if (!ipconfig_ipv6)
+ goto err;
+#endif
+ err = __connman_ipconfig_address_add(ipconfig_ipv6);
+ if (err < 0)
+ goto err;
+
+ return 0;
+
+err:
+ connman_network_set_error(network,
+ CONNMAN_NETWORK_ERROR_CONFIGURE_FAIL);
+ return err;
+}
+
+static void autoconf_ipv6_set(struct connman_network *network);
+static void dhcpv6_callback(struct connman_network *network,
+ enum __connman_dhcpv6_status status, gpointer data);
+
+/*
+ * Have a separate callback for renew so that we do not do autoconf
+ * in wrong phase as the dhcpv6_callback() is also called when doing
+ * DHCPv6 solicitation.
+ */
+static void dhcpv6_renew_callback(struct connman_network *network,
+ enum __connman_dhcpv6_status status,
+ gpointer data)
+{
+ switch (status) {
+ case CONNMAN_DHCPV6_STATUS_SUCCEED:
+ dhcpv6_callback(network, status, data);
+ break;
+ case CONNMAN_DHCPV6_STATUS_FAIL:
+ case CONNMAN_DHCPV6_STATUS_RESTART:
+ stop_dhcpv6(network);
+
+ /* restart and do solicit again. */
+ autoconf_ipv6_set(network);
+ break;
+ }
+}
+
+static void dhcpv6_callback(struct connman_network *network,
+ enum __connman_dhcpv6_status status, gpointer data)
+{
+ DBG("status %d", status);
+
+ /* Start the renew process if necessary */
+ if (status == CONNMAN_DHCPV6_STATUS_SUCCEED) {
+
+ if (dhcpv6_set_addresses(network) < 0) {
+ stop_dhcpv6(network);
+ return;
+ }
+
+ if (__connman_dhcpv6_start_renew(network,
+ dhcpv6_renew_callback) == -ETIMEDOUT)
+ dhcpv6_renew_callback(network,
+ CONNMAN_DHCPV6_STATUS_FAIL,
+ data);
+
+ } else if (status == CONNMAN_DHCPV6_STATUS_RESTART) {
+ stop_dhcpv6(network);
+ autoconf_ipv6_set(network);
+ } else
+ stop_dhcpv6(network);
+}
+
+static void check_dhcpv6(struct nd_router_advert *reply,
+ unsigned int length, void *user_data)
+{
+ struct connman_network *network = user_data;
+ struct connman_service *service;
+ GSList *prefixes;
+
+ DBG("reply %p", reply);
+
+ if (!reply) {
+ /*
+ * Router solicitation message seem to get lost easily so
+ * try to send it again.
+ */
+ if (network->router_solicit_count > 0) {
+ DBG("re-send router solicitation %d",
+ network->router_solicit_count);
+ network->router_solicit_count--;
+ __connman_inet_ipv6_send_rs(network->index, RTR_SOLICITATION_INTERVAL,
+ check_dhcpv6, network);
+ return;
+ }
+#if defined TIZEN_EXT
+ DBG("RA message is not received from server in reply of RS.");
+#endif
+ connman_network_unref(network);
+ return;
+ }
+
+ network->router_solicit_count = 0;
+
+ /*
+ * If we were disconnected while waiting router advertisement,
+ * we just quit and do not start DHCPv6
+ */
+ if (!network->connected) {
+ connman_network_unref(network);
+#if defined TIZEN_EXT
+ DBG("Network is not connected");
+#endif
+ return;
+ }
+
+ prefixes = __connman_inet_ipv6_get_prefixes(reply, length);
+
+ /*
+ * If IPv6 config is missing from service, then create it.
+ * The ipconfig might be missing if we got a rtnl message
+ * that disabled IPv6 config and thus removed it. This
+ * can happen if we are switching from one service to
+ * another in the same interface. The only way to get IPv6
+ * config back is to re-create it here.
+ */
+ service = connman_service_lookup_from_network(network);
+ if (service) {
+ connman_service_create_ip6config(service, network->index);
+
+ connman_network_set_associating(network, false);
+
+ __connman_service_ipconfig_indicate_state(service,
+ CONNMAN_SERVICE_STATE_CONFIGURATION,
+ CONNMAN_IPCONFIG_TYPE_IPV6);
+ }
+
+ /*
+ * We do stateful/stateless DHCPv6 if router advertisement says so.
+ */
+ if (reply->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) {
+#if defined TIZEN_EXT
+ DBG("IPv6 ND_RA_FLAG_MANAGED");
+#endif
+ __connman_dhcpv6_start(network, prefixes, dhcpv6_callback);
+ } else {
+ if (reply->nd_ra_flags_reserved & ND_RA_FLAG_OTHER)
+#if defined TIZEN_EXT
+ {
+ DBG("IPv6 ND_RA_FLAG_OTHER");
+#endif
+ __connman_dhcpv6_start_info(network,
+ dhcpv6_info_callback);
+#if defined TIZEN_EXT
+ }
+#endif
+
+ g_slist_free_full(prefixes, g_free);
+ network->connecting = false;
+ }
+
+ connman_network_unref(network);
+}
+
+static void receive_refresh_rs_reply(struct nd_router_advert *reply,
+ unsigned int length, void *user_data)
+{
+ struct connman_network *network = user_data;
+
+ DBG("reply %p", reply);
+
+ if (!reply) {
+ /*
+ * Router solicitation message seem to get lost easily so
+ * try to send it again.
+ */
+ if (network->router_solicit_refresh_count > 1) {
+ network->router_solicit_refresh_count--;
+ DBG("re-send router solicitation %d",
+ network->router_solicit_refresh_count);
+ __connman_inet_ipv6_send_rs(network->index,
+ RS_REFRESH_TIMEOUT,
+ receive_refresh_rs_reply,
+ network);
+ return;
+ }
+ }
+
+ /* RS refresh not in progress anymore */
+ network->router_solicit_refresh_count = 0;
+
+ connman_network_unref(network);
+}
+
+int __connman_network_refresh_rs_ipv6(struct connman_network *network,
+ int index)
+{
+ int ret = 0;
+
+ DBG("network %p index %d", network, index);
+
+ /* Send only one RS for all RDNSS entries which are about to expire */
+ if (network->router_solicit_refresh_count > 0) {
+ DBG("RS refresh already started");
+ return 0;
+ }
+
+ network->router_solicit_refresh_count = RS_REFRESH_COUNT;
+
+ connman_network_ref(network);
+
+ ret = __connman_inet_ipv6_send_rs(index, RS_REFRESH_TIMEOUT,
+ receive_refresh_rs_reply, network);
+ return ret;
+}
+
+static void autoconf_ipv6_set(struct connman_network *network)
+{
+ struct connman_service *service;
+ struct connman_ipconfig *ipconfig;
+ int index;
+
+ DBG("network %p", network);
+
+ if (network->router_solicit_count > 0) {
+ /*
+ * The autoconfiguration is already pending and we have sent
+ * router solicitation messages and are now waiting answers.
+ * There is no need to continue any further.
+ */
+ DBG("autoconfiguration already started");
+ return;
+ }
+
+ __connman_device_set_network(network->device, network);
+
+#if defined TIZEN_EXT
+ if(network->type == CONNMAN_NETWORK_TYPE_CELLULAR)
+ return;
+#endif
+
+ service = connman_service_lookup_from_network(network);
+ if (!service)
+ return;
+
+ ipconfig = __connman_service_get_ip6config(service);
+ if (!ipconfig)
+ return;
+
+ __connman_ipconfig_enable(ipconfig);
+
+ __connman_ipconfig_enable_ipv6(ipconfig);
+
+ __connman_ipconfig_address_remove(ipconfig);
+
+ index = __connman_ipconfig_get_index(ipconfig);
+
+ connman_network_ref(network);
+
+ /* Try to get stateless DHCPv6 information, RFC 3736 */
+ network->router_solicit_count = 3;
+ __connman_inet_ipv6_send_rs(index, RTR_SOLICITATION_INTERVAL,
+ check_dhcpv6, network);
+}
+
+static void set_connected(struct connman_network *network)
+{
+ struct connman_ipconfig *ipconfig_ipv4, *ipconfig_ipv6;
+ struct connman_service *service;
+
+ if (network->connected)
+ return;
+
+ connman_network_set_associating(network, false);
+
+ network->connected = true;
+
+ service = connman_service_lookup_from_network(network);
+
+ ipconfig_ipv4 = __connman_service_get_ip4config(service);
+ ipconfig_ipv6 = __connman_service_get_ip6config(service);
+
+ DBG("service %p ipv4 %p ipv6 %p", service, ipconfig_ipv4,
+ ipconfig_ipv6);
+
+ __connman_network_enable_ipconfig(network, ipconfig_ipv4);
+ __connman_network_enable_ipconfig(network, ipconfig_ipv6);
+}
+
+static void set_disconnected(struct connman_network *network)
+{
+ struct connman_ipconfig *ipconfig_ipv4, *ipconfig_ipv6;
+ enum connman_ipconfig_method ipv4_method, ipv6_method;
+ enum connman_service_state state;
+ struct connman_service *service;
+
+ service = connman_service_lookup_from_network(network);
+
+ ipconfig_ipv4 = __connman_service_get_ip4config(service);
+ ipconfig_ipv6 = __connman_service_get_ip6config(service);
+
+ DBG("service %p ipv4 %p ipv6 %p", service, ipconfig_ipv4,
+ ipconfig_ipv6);
+
+ ipv4_method = __connman_ipconfig_get_method(ipconfig_ipv4);
+ ipv6_method = __connman_ipconfig_get_method(ipconfig_ipv6);
+
+ DBG("method ipv4 %d ipv6 %d", ipv4_method, ipv6_method);
+
+ /*
+ * Resetting solicit count here will prevent the RS resend loop
+ * from sending packets in check_dhcpv6()
+ */
+ network->router_solicit_count = 0;
+
+ __connman_device_set_network(network->device, NULL);
+
+ if (network->connected) {
+ switch (ipv6_method) {
+ case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+ case CONNMAN_IPCONFIG_METHOD_OFF:
+ case CONNMAN_IPCONFIG_METHOD_FIXED:
+ case CONNMAN_IPCONFIG_METHOD_MANUAL:
+ break;
+ case CONNMAN_IPCONFIG_METHOD_DHCP:
+ case CONNMAN_IPCONFIG_METHOD_AUTO:
+ release_dhcpv6(network);
+ break;
+ }
+
+ switch (ipv4_method) {
+ case CONNMAN_IPCONFIG_METHOD_UNKNOWN:
+ case CONNMAN_IPCONFIG_METHOD_OFF:
+ case CONNMAN_IPCONFIG_METHOD_FIXED:
+ case CONNMAN_IPCONFIG_METHOD_MANUAL:
+ break;
+ case CONNMAN_IPCONFIG_METHOD_AUTO:
+ /*
+ * If the current method is AUTO then next time we
+ * try first DHCP. DHCP also needs to be stopped
+ * in this case because if we fell in AUTO means
+ * that DHCP was launched for IPv4 but it failed.
+ */
+ __connman_ipconfig_set_method(ipconfig_ipv4,
+ CONNMAN_IPCONFIG_METHOD_DHCP);
+ __connman_service_notify_ipv4_configuration(service);
+ /* fall through */
+ case CONNMAN_IPCONFIG_METHOD_DHCP:
+ remove_dhcp_timeout(network);
+ __connman_dhcp_stop(ipconfig_ipv4);
+ break;
+ }
+ }
+
+ /*
+ * We only set the disconnect state if we were not in idle
+ * or in failure. It does not make sense to go to disconnect
+ * state if we were not connected.
+ */
+ state = __connman_service_ipconfig_get_state(service,
+ CONNMAN_IPCONFIG_TYPE_IPV4);
+ if (state != CONNMAN_SERVICE_STATE_IDLE &&
+ state != CONNMAN_SERVICE_STATE_FAILURE)
+ __connman_service_ipconfig_indicate_state(service,
+ CONNMAN_SERVICE_STATE_DISCONNECT,
+ CONNMAN_IPCONFIG_TYPE_IPV4);
+
+ state = __connman_service_ipconfig_get_state(service,
+ CONNMAN_IPCONFIG_TYPE_IPV6);
+ if (state != CONNMAN_SERVICE_STATE_IDLE &&
+ state != CONNMAN_SERVICE_STATE_FAILURE)
+ __connman_service_ipconfig_indicate_state(service,
+ CONNMAN_SERVICE_STATE_DISCONNECT,
+ CONNMAN_IPCONFIG_TYPE_IPV6);
+
+ if (network->connected) {
+#if defined TIZEN_EXT
+ /**
+ * Do not remove gateway and its address,
+ * if there are connected profiles that use same interface (multiple PDN)
+ */
+ if (connman_service_get_type(service) != CONNMAN_SERVICE_TYPE_CELLULAR ||
+ __connman_service_get_connected_count_of_iface(service) <= 0) {
+#endif
+ __connman_connection_gateway_remove(service,
+ CONNMAN_IPCONFIG_TYPE_ALL);
+
+ __connman_ipconfig_address_unset(ipconfig_ipv4);
+ __connman_ipconfig_address_unset(ipconfig_ipv6);
+
+#if defined TIZEN_EXT
+ }
+#endif
+ /*
+ * Special handling for IPv6 autoconfigured address.
+ * The simplest way to remove autoconfigured routes is to
+ * disable IPv6 temporarily so that kernel will do the cleanup
+ * automagically.
+ */
+ if (ipv6_method == CONNMAN_IPCONFIG_METHOD_AUTO) {
+ __connman_ipconfig_disable_ipv6(ipconfig_ipv6);
+ __connman_ipconfig_enable_ipv6(ipconfig_ipv6);
+ }
+ }
+
+ __connman_service_ipconfig_indicate_state(service,
+ CONNMAN_SERVICE_STATE_IDLE,
+ CONNMAN_IPCONFIG_TYPE_IPV4);
+
+ __connman_service_ipconfig_indicate_state(service,
+ CONNMAN_SERVICE_STATE_IDLE,
+ CONNMAN_IPCONFIG_TYPE_IPV6);
+
+ network->connecting = false;
+ network->connected = false;
+
+ connman_network_set_associating(network, false);
+}
+
+
+
+static int network_probe(struct connman_network *network)
+{
+ GSList *list;
+ struct connman_network_driver *driver = NULL;
+
+ DBG("network %p name %s", network, network->name);
+
+ if (network->driver)
+ return -EALREADY;
+
+ for (list = driver_list; list; list = list->next) {
+ driver = list->data;
+
+ if (!match_driver(network, driver)) {
+ driver = NULL;
+ continue;
+ }
+#if defined TIZEN_EXT
+ if (!simplified_log)
+#endif
+ DBG("driver %p name %s", driver, driver->name);
+
+ if (driver->probe(network) == 0)
+ break;
+
+ driver = NULL;
+ }
+
+ if (!driver)
+ return -ENODEV;
+
+ if (!network->group)
+ return -EINVAL;
+
+ switch (network->type) {
+ case CONNMAN_NETWORK_TYPE_UNKNOWN:
+ case CONNMAN_NETWORK_TYPE_VENDOR:
+ return 0;
+ case CONNMAN_NETWORK_TYPE_ETHERNET:
+ case CONNMAN_NETWORK_TYPE_GADGET:
+ case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN:
+ case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN:
+ case CONNMAN_NETWORK_TYPE_CELLULAR:
+ case CONNMAN_NETWORK_TYPE_WIFI:
+ network->driver = driver;
+ if (!__connman_service_create_from_network(network)) {
+ network->driver = NULL;
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static void network_remove(struct connman_network *network)
+{
+ DBG("network %p name %s", network, network->name);
+
+ if (!network->driver)
+ return;
+
+ if (network->connected)
+ set_disconnected(network);
+
+ switch (network->type) {
+ case CONNMAN_NETWORK_TYPE_UNKNOWN:
+ case CONNMAN_NETWORK_TYPE_VENDOR:
+ break;
+ case CONNMAN_NETWORK_TYPE_ETHERNET:
+ case CONNMAN_NETWORK_TYPE_GADGET:
+ case CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN:
+ case CONNMAN_NETWORK_TYPE_BLUETOOTH_DUN:
+ case CONNMAN_NETWORK_TYPE_CELLULAR:
+ case CONNMAN_NETWORK_TYPE_WIFI:
+ if (network->group) {
+ __connman_service_remove_from_network(network);
+
+ g_free(network->group);
+ network->group = NULL;
+ }
+ break;
+ }
+
+ if (network->driver->remove)
+ network->driver->remove(network);
+
+ network->driver = NULL;
+}
+
+static void probe_driver(struct connman_network_driver *driver)
+{
+ GSList *list;
+
+ DBG("driver %p name %s", driver, driver->name);
+
+ for (list = network_list; list; list = list->next) {
+ struct connman_network *network = list->data;
+
+ if (network->driver)
+ continue;
+
+ if (driver->type != network->type)
+ continue;
+
+ if (driver->probe(network) < 0)
+ continue;
+
+ network->driver = driver;
+ }
+}
+
+static gint compare_priority(gconstpointer a, gconstpointer b)
+{
+ const struct connman_network_driver *driver1 = a;
+ const struct connman_network_driver *driver2 = b;
+
+ return driver2->priority - driver1->priority;
+}
+
+/**
+ * connman_network_driver_register:
+ * @driver: network driver definition
+ *
+ * Register a new network driver
+ *
+ * Returns: %0 on success
+ */
+int connman_network_driver_register(struct connman_network_driver *driver)
+{
+ DBG("driver %p name %s", driver, driver->name);
+
+ driver_list = g_slist_insert_sorted(driver_list, driver,
+ compare_priority);
+
+ probe_driver(driver);
+
+ return 0;
+}
+
+/**
+ * connman_network_driver_unregister:
+ * @driver: network driver definition
+ *
+ * Remove a previously registered network driver
+ */
+void connman_network_driver_unregister(struct connman_network_driver *driver)
+{
+ GSList *list;
+
+ DBG("driver %p name %s", driver, driver->name);
+
+ driver_list = g_slist_remove(driver_list, driver);
+
+ for (list = network_list; list; list = list->next) {
+ struct connman_network *network = list->data;
+
+ if (network->driver == driver)
+ network_remove(network);
+ }
+}
+
+static void network_destruct(struct connman_network *network)
+{
+ DBG("network %p name %s", network, network->name);
+
+ g_free(network->wifi.ssid);
+ g_free(network->wifi.mode);
+ g_free(network->wifi.security);
+ g_free(network->wifi.passphrase);
+ g_free(network->wifi.eap);
+ g_free(network->wifi.identity);
+ g_free(network->wifi.anonymous_identity);
+ g_free(network->wifi.agent_identity);
+ g_free(network->wifi.ca_cert_path);
+ g_free(network->wifi.subject_match);
+ g_free(network->wifi.altsubject_match);
+ g_free(network->wifi.domain_suffix_match);
+ g_free(network->wifi.domain_match);
+ g_free(network->wifi.client_cert_path);
+ g_free(network->wifi.private_key_path);
+ g_free(network->wifi.private_key_passphrase);
+ g_free(network->wifi.phase2_auth);
+ g_free(network->wifi.pin_wps);
+
+#if defined TIZEN_EXT
+ g_slist_free_full(network->wifi.vsie_list, g_free);
+ g_slist_free_full(network->wifi.bssid_list, g_free);
+#endif
+ g_free(network->path);
+ g_free(network->group);
+ g_free(network->node);
+ g_free(network->name);
+ g_free(network->identifier);
+ acd_host_free(network->acd_host);
+
+ network->device = NULL;
+
+ g_free(network);
+}
+
+/**
+ * connman_network_create:
+ * @identifier: network identifier (for example an unique name)
+ *
+ * Allocate a new network and assign the #identifier to it.
+ *