#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/sockio.h>
#include <sys/time.h>
#include <ctype.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet6/in6_var.h>
#include <syslog.h>
#include "globals.h"
#include "timer.h"
#include "configthreads_common.h"
#include "ip6config_utils.h"
typedef struct {
struct in6_addr our_addr;
int our_prefixlen;
int our_flags;
timer_callout_t *timer;
} Service_manual_t;
static void
manual_cancel_pending_events(Service_t * service_p)
{
Service_manual_t * manual = (Service_manual_t *)service_p->private;
if (manual == NULL)
return;
if (manual->timer) {
timer_cancel(manual->timer);
}
return;
}
static void
manual_inactive(Service_t * service_p)
{
service_remove_addresses(service_p);
service_publish_failure(service_p, ip6config_status_media_inactive_e,
NULL);
return;
}
static void
manual_link_timer(void * arg0, void * arg1, void * arg2)
{
manual_inactive((Service_t *) arg0);
return;
}
static ip6config_status_t
validate_method_data_addresses(config_data_t * cfg, ip6config_method_t method,
char * ifname)
{
int i;
char str[INET6_ADDRSTRLEN];
if (cfg->data->n_ip6 < 1) {
my_log(LOG_DEBUG, "%s %s: no IP6 addresses specified",
ip6config_method_string(method), ifname);
return (ip6config_status_invalid_parameter_e);
}
for (i = 0; i < cfg->data->n_ip6; i++) {
if (ip_valid(&cfg->data->ip6[i].addr) == FALSE) {
my_log(LOG_DEBUG, "%s %s: invalid IP6 %s",
ip6config_method_string(method), ifname,
inet_ntop(AF_INET6, &cfg->data->ip6[i].addr, str, sizeof(str)));
return (ip6config_status_invalid_parameter_e);
}
}
return (ip6config_status_success_e);
}
static void
manual_start(Service_t * service_p)
{
Service_manual_t * manual = (Service_manual_t *)service_p->private;
manual_cancel_pending_events(service_p);
if (service_link_status(service_p)->valid == TRUE
&& service_link_status(service_p)->active == FALSE) {
manual_inactive(service_p);
return;
}
(void)service_set_address(service_p, &manual->our_addr,
manual->our_prefixlen,
manual->our_flags);
return;
}
__private_extern__ ip6config_status_t
manual_thread(Service_t * service_p, IFEventID_t evid, void * event_data)
{
interface_t * if_p = service_interface(service_p);
Service_manual_t * manual = (Service_manual_t *)service_p->private;
ip6config_status_t status = ip6config_status_success_e;
switch (evid) {
case IFEventID_start_e: {
start_event_data_t * evdata = ((start_event_data_t *)event_data);
ip6config_method_data_t *ipcfg = evdata->config.data;
my_log(LOG_DEBUG, "MANUAL_THREAD %s: STARTING", if_name(if_p));
if (manual) {
my_log(LOG_DEBUG, "MANUAL_THREAD %s: re-entering start state",
if_name(if_p));
status = ip6config_status_internal_error_e;
break;
}
status = validate_method_data_addresses(&evdata->config,
ip6config_method_manual_e, if_name(if_p));
if (status != ip6config_status_success_e) {
my_log(LOG_DEBUG, "MANUAL_THREAD: validate_method_data_addresses returned error");
break;
}
manual = calloc(1, sizeof(*manual));
if (manual == NULL) {
my_log(LOG_ERR, "MANUAL_THREAD %s: calloc failed",
if_name(if_p));
status = ip6config_status_allocation_failed_e;
break;
}
service_p->private = manual;
memcpy(&manual->our_addr, &ipcfg->ip6[0].addr, sizeof(struct in6_addr));
manual->our_prefixlen = ipcfg->ip6[0].prefixLen;
manual->our_flags = ipcfg->ip6[0].flags;
if (if_flags(if_p) & IFF_LOOPBACK) {
(void)service_set_address(service_p, &manual->our_addr,
manual->our_prefixlen,
manual->our_flags);
service_publish_success(service_p);
break;
}
manual->timer = timer_callout_init();
if (manual->timer == NULL) {
my_log(LOG_ERR, "MANUAL_THREAD %s: timer_callout_init failed",
if_name(if_p));
status = ip6config_status_allocation_failed_e;
goto stop;
}
my_log(LOG_DEBUG, "MANUAL_THREAD %s: starting", if_name(if_p));
manual_start(service_p);
break;
}
stop:
case IFEventID_stop_e: {
my_log(LOG_DEBUG, "MANUAL_THREAD %s: STOPPING", if_name(if_p));
if (manual == NULL) {
my_log(LOG_DEBUG, "MANUAL %s: private data is NULL",
if_name(if_p));
status = ip6config_status_internal_error_e;
break;
}
service_remove_addresses(service_p);
if (manual->timer)
timer_callout_free(&manual->timer);
free(manual);
service_p->private = NULL;
break;
}
case IFEventID_change_e: {
change_event_data_t * evdata = ((change_event_data_t *)event_data);
ip6config_method_data_t * ipcfg = evdata->config.data;
my_log(LOG_DEBUG, "MANUAL_THREAD %s: CHANGING", if_name(if_p));
if (manual == NULL) {
my_log(LOG_DEBUG, "MANUAL %s: private data is NULL",
if_name(if_p));
status = ip6config_status_internal_error_e;
break;
}
status = validate_method_data_addresses(&evdata->config,
ip6config_method_manual_e, if_name(if_p));
if (status != ip6config_status_success_e)
break;
evdata->needs_stop = FALSE;
if (!IN6_ARE_ADDR_EQUAL(&ipcfg->ip6[0].addr, &manual->our_addr) ||
(ipcfg->ip6[0].prefixLen - manual->our_prefixlen != 0)) {
evdata->needs_stop = TRUE;
break;
}
break;
}
case IFEventID_state_change_e: {
int i, j;
ip6_addrinfo_list_t * ip6_addrs = ((ip6_addrinfo_list_t *)event_data);
ip6_addrinfo_list_t * addrslist_p = &service_p->info.addrs;
my_log(LOG_DEBUG, "MANUAL_THREAD %s: STATE CHANGE", if_name(if_p));
if (manual == NULL) {
my_log(LOG_DEBUG, "MANUAL %s: private data is NULL",
if_name(if_p));
status = ip6config_status_internal_error_e;
break;
}
for (i = 0; i < ip6_addrs->n_addrs; i++) {
int done = 0;
ip6_addrinfo_t *new_addr = ip6_addrs->addr_list + i;
if (IN6_IS_ADDR_LINKLOCAL(&new_addr->addr)
|| (new_addr->flags & IN6_IFF_AUTOCONF)) {
continue;
}
for (j = 0; j < addrslist_p->n_addrs; j++) {
if (IN6_ARE_ADDR_EQUAL(&new_addr->addr, &addrslist_p->addr_list[j].addr)) {
if (new_addr->flags & IN6_IFF_DUPLICATED) {
char msg[128];
snprintf(msg, sizeof(msg),
IP6_FORMAT " is a duplicate address on interface %s",
IP6_LIST(&addrslist_p->addr_list[j].addr), if_name(if_p));
my_log(LOG_ERR, "MANUAL %s: %s", if_name(if_p), msg);
service_report_conflict(service_p, &addrslist_p->addr_list[j].addr);
service_remove_addresses(service_p);
service_publish_failure(service_p,
ip6config_status_address_in_use_e,
msg);
break;
}
service_publish_success(service_p);
done++;
break;
}
}
if (done)
break;
}
break;
}
case IFEventID_media_e: {
if (manual == NULL)
return (ip6config_status_internal_error_e);
if (service_link_status(service_p)->valid == TRUE) {
if (service_link_status(service_p)->active == TRUE) {
manual_start(service_p);
}
else {
struct timeval tv;
manual_cancel_pending_events(service_p);
tv.tv_sec = LINK_INACTIVE_WAIT_SECS;
tv.tv_usec = 0;
timer_set_relative(manual->timer, tv,
(timer_func_t *)manual_link_timer,
service_p, NULL, NULL);
}
}
break;
}
default:
break;
}
return (status);
}