implemented more flexible iterator hook API
authorMartin Willi <martin@strongswan.org>
Fri, 15 Jun 2007 13:23:18 +0000 (13:23 -0000)
committerMartin Willi <martin@strongswan.org>
Fri, 15 Jun 2007 13:23:18 +0000 (13:23 -0000)
kernel interface handles interface changes and updates address list

src/charon/control/interfaces/stroke_interface.c
src/charon/kernel/kernel_interface.c
src/charon/sa/ike_sa_manager.c
src/libstrongswan/utils/iterator.h
src/libstrongswan/utils/linked_list.c

index 9bae908..09846fe 100755 (executable)
@@ -1216,8 +1216,7 @@ static void stroke_status(stroke_msg_t *msg, FILE *out, bool all)
                                charon->scheduler->get_job_load(charon->scheduler));
                iterator = charon->kernel_interface->create_address_iterator(
                                                                                                        charon->kernel_interface);
-               fprintf(out, "Listening on %d IP addresses:\n",
-                               iterator->get_count(iterator));
+               fprintf(out, "Listening IP addresses:\n");
                while (iterator->iterate(iterator, (void**)&host))
                {
                        fprintf(out, "  %H\n", host);
index f00cbab..8212841 100644 (file)
@@ -242,29 +242,32 @@ static void vip_entry_destroy(vip_entry_t *this)
        free(this);
 }
 
-typedef struct address_entry_t address_entry_t;
+typedef struct interface_entry_t interface_entry_t;
 
 /**
- * an address found on the system, containg address and interface info 
+ * A network interface on this system, containing addresses
  */
-struct address_entry_t {
-
-       /** address of this entry */
-       host_t *host;
+struct interface_entry_t {
        
        /** interface index */
        int ifindex;
        
-       /** name of the index */
+       /** name of the interface */
        char ifname[IFNAMSIZ];
+       
+       /** interface flags, as in netdevice(7) SIOCGIFFLAGS */
+       u_int flags;
+       
+       /** list of addresses as host_t */
+       linked_list_t *addresses;
 };
 
 /**
- * destroy an address entry
+ * destroy an interface entry
  */
-static void address_entry_destroy(address_entry_t *this)
+static void interface_entry_destroy(interface_entry_t *this)
 {
-       this->host->destroy(this->host);
+       this->addresses->destroy_offset(this->addresses, offsetof(host_t, destroy));
        free(this);
 }
 
@@ -280,14 +283,14 @@ struct private_kernel_interface_t {
        kernel_interface_t public;
        
        /**
-        * List of installed policies (kernel_entry_t)
+        * mutex to lock access to the various lists
         */
-       linked_list_t *policies;
+       pthread_mutex_t mutex;
        
        /**
-        * Mutex locks access to policies
+        * List of installed policies (kernel_entry_t)
         */
-       pthread_mutex_t policies_mutex;
+       linked_list_t *policies;
        
        /**
         * List of installed virtual IPs. (vip_entry_t)
@@ -295,26 +298,26 @@ struct private_kernel_interface_t {
        linked_list_t *vips;
        
        /**
-        * Mutex to lock access to vips.
-        */
-       pthread_mutex_t vips_mutex;
-       
-       /**
-        * Cached list of IP adresses (address_entry_t)
+        * Cached list of interfaces and its adresses (interface_entry_t)
         */
-       linked_list_t *addrs;
+       linked_list_t *interfaces;
        
        /**
-        * Mutex to lock access to addr.
+        * iterator used in hook()
         */
-       pthread_mutex_t addrs_mutex;
+       iterator_t *hiter;
         
        /**
-        * job receiving xfrm events
+        * job receiving netlink events
         */
        callback_job_t *job;
        
        /**
+        * current sequence number for netlink request
+        */
+       int seq;
+       
+       /**
         * Netlink xfrm socket (IPsec)
         */
        int socket_xfrm;
@@ -517,92 +520,110 @@ static void process_expire(private_kernel_interface_t *this, struct nlmsghdr *hd
 }
 
 /**
- * process a RTM_NEWADDR from kernel
+ * process RTM_NEWLINK/RTM_DELLINK from kernel
  */
-static void process_newaddr(private_kernel_interface_t *this,
-                                                       struct nlmsghdr *hdr, bool initial)
+static void process_link(private_kernel_interface_t *this,
+                                                struct nlmsghdr *hdr, bool event)
 {
-       struct ifaddrmsg* msg = (struct ifaddrmsg*)(NLMSG_DATA(hdr));
-       struct rtattr *rta = IFA_RTA(msg);
-       size_t rtasize = IFA_PAYLOAD (hdr);
-       host_t *host = NULL;
+       struct ifinfomsg* msg = (struct ifinfomsg*)(NLMSG_DATA(hdr));
+       struct rtattr *rta = IFLA_RTA(msg);
+       size_t rtasize = IFLA_PAYLOAD (hdr);
+       iterator_t *iterator;
+       interface_entry_t *current, *entry = NULL;
        char *name = NULL;
-       chunk_t local = chunk_empty, address = chunk_empty;
-               
+       
        while(RTA_OK(rta, rtasize))
        {
                switch (rta->rta_type)
                {
-                       case IFA_LOCAL:
-                               local.ptr = RTA_DATA(rta);
-                               local.len = RTA_PAYLOAD(rta);
-                               break;
-                       case IFA_ADDRESS:
-                               address.ptr = RTA_DATA(rta);
-                               address.len = RTA_PAYLOAD(rta);
-                               break;
-                       case IFA_LABEL:
+                       case IFLA_IFNAME:
                                name = RTA_DATA(rta);
                                break;
                }
                rta = RTA_NEXT(rta, rtasize);
        }
-       
-       /* For PPP interfaces, we need the IFA_LOCAL address,
-        * IFA_ADDRESS is the peers address. But IFA_LOCAL is
-        * not included in all cases, so fallback to IFA_ADDRESS. */
-       if (local.ptr)
-       {
-               host = host_create_from_chunk(msg->ifa_family, local, 0);
-       }
-       else if (address.ptr)
+       if (!name)
        {
-               host = host_create_from_chunk(msg->ifa_family, address, 0);
+               name = "(unknown)";
        }
        
-       if (host)
+       switch (hdr->nlmsg_type)
        {
-               address_entry_t *entry;
-               
-               entry = malloc_thing(address_entry_t);
-               entry->host = host;
-               entry->ifindex = msg->ifa_index;
-               if (name)
+               case RTM_NEWLINK:
                {
+                       if (msg->ifi_flags & IFF_LOOPBACK)
+                       {       /* ignore loopback interfaces */
+                               break;
+                       }
+                       iterator = this->interfaces->create_iterator_locked(this->interfaces,
+                                                                                                                               &this->mutex);
+                       while (iterator->iterate(iterator, (void**)&current))
+                       {
+                               if (current->ifindex == msg->ifi_index)
+                               {
+                                       entry = current;
+                                       break;
+                               }
+                       }
+                       if (!entry)
+                       {
+                               entry = malloc_thing(interface_entry_t);
+                               entry->ifindex = msg->ifi_index;
+                               entry->flags = 0;
+                               entry->addresses = linked_list_create();
+                               this->interfaces->insert_last(this->interfaces, entry);
+                       }
                        memcpy(entry->ifname, name, IFNAMSIZ);
+                       entry->ifname[IFNAMSIZ-1] = '\0';
+                       if (event)
+                       {
+                               if (!(entry->flags & IFF_UP) && (msg->ifi_flags & IFF_UP))
+                               {
+                                       DBG1(DBG_KNL, "interface %s activated", name);
+                               }
+                               if ((entry->flags & IFF_UP) && !(msg->ifi_flags & IFF_UP))
+                               {
+                                       DBG1(DBG_KNL, "interface %s deactivated", name);
+                               }
+                       }
+                       entry->flags = msg->ifi_flags;
+                       iterator->destroy(iterator);
+                       break;
                }
-               else
-               {
-                       strcpy(entry->ifname, "(unknown)");
-               }
-               
-               if (initial)
-               {
-                       DBG1(DBG_KNL, "  %H on %s", host, entry->ifname);
-               }
-               else
+               case RTM_DELLINK:
                {
-                       DBG1(DBG_KNL, "%H appeared on %s", host, entry->ifname);
+                       iterator = this->interfaces->create_iterator_locked(this->interfaces,
+                                                                                                                               &this->mutex);
+                       while (iterator->iterate(iterator, (void**)&current))
+                       {
+                               if (current->ifindex == msg->ifi_index)
+                               {
+                                       /* we do not remove it, as an address may be added to a 
+                                        * "down" interface and we wan't to know that. */
+                                       current->flags = msg->ifi_flags;
+                                       break;
+                               }
+                       }
+                       iterator->destroy(iterator);
+                       break;
                }
-               
-               pthread_mutex_lock(&this->addrs_mutex);
-               this->addrs->insert_last(this->addrs, entry);
-               pthread_mutex_unlock(&this->addrs_mutex);
        }
 }
 
-
 /**
- * process RTM_DELADDR from kernel
+ * process RTM_NEWADDR/RTM_DELADDR from kernel
  */
-static void process_deladdr(private_kernel_interface_t *this, struct nlmsghdr *hdr)
+static void process_addr(private_kernel_interface_t *this,
+                                                struct nlmsghdr *hdr, bool event)
 {
        struct ifaddrmsg* msg = (struct ifaddrmsg*)(NLMSG_DATA(hdr));
        struct rtattr *rta = IFA_RTA(msg);
        size_t rtasize = IFA_PAYLOAD (hdr);
        host_t *host = NULL;
+       iterator_t *iterator;
+       interface_entry_t *entry;
        chunk_t local = chunk_empty, address = chunk_empty;
-               
+       
        while(RTA_OK(rta, rtasize))
        {
                switch (rta->rta_type)
@@ -619,7 +640,9 @@ static void process_deladdr(private_kernel_interface_t *this, struct nlmsghdr *h
                rta = RTA_NEXT(rta, rtasize);
        }
        
-       /* same stuff as in newaddr */
+       /* For PPP interfaces, we need the IFA_LOCAL address,
+        * IFA_ADDRESS is the peers address. But IFA_LOCAL is
+        * not included in all cases, so fallback to IFA_ADDRESS. */
        if (local.ptr)
        {
                host = host_create_from_chunk(msg->ifa_family, local, 0);
@@ -629,26 +652,66 @@ static void process_deladdr(private_kernel_interface_t *this, struct nlmsghdr *h
                host = host_create_from_chunk(msg->ifa_family, address, 0);
        }
        
-       if (host)
+       if (host == NULL)
+       {       /* bad family? */
+               return;
+       }
+       
+       switch (hdr->nlmsg_type)
        {
-               address_entry_t *entry;
-               iterator_t *iterator;
-               
-               iterator = this->addrs->create_iterator_locked(this->addrs,
-                                                                                                          &this->addrs_mutex);
-               while (iterator->iterate(iterator, (void**)&entry))
+               case RTM_NEWADDR:
                {
-                       if (entry->ifindex == msg->ifa_index &&
-                               host->equals(host, entry->host))
+                       iterator = this->interfaces->create_iterator_locked(this->interfaces,
+                                                                                                                               &this->mutex);
+                       while (iterator->iterate(iterator, (void**)&entry))
                        {
-                               iterator->remove(iterator);
-                               DBG1(DBG_KNL, "%H disappeared from %s", host, entry->ifname);
-                               address_entry_destroy(entry);
+                               if (entry->ifindex == msg->ifa_index)
+                               {
+                                       entry->addresses->insert_last(entry->addresses,
+                                                                                                 host->clone(host));
+                                       if (event)
+                                       {
+                                               DBG1(DBG_KNL, "%H appeared on %s", host, entry->ifname);
+                                       }
+                                       break;
+                               }
                        }
+                       iterator->destroy(iterator);
+                       break;
                }
-               iterator->destroy(iterator);
-               host->destroy(host);
+               case RTM_DELADDR:
+               {
+                       iterator = this->interfaces->create_iterator_locked(this->interfaces,
+                                                                                                               &this->mutex);
+                       while (iterator->iterate(iterator, (void**)&entry))
+                       {
+                               if (entry->ifindex == msg->ifa_index)
+                               {
+                                       iterator_t *addresses;
+                                       host_t *current;
+                                       
+                                       addresses = entry->addresses->create_iterator(
+                                                                                                       entry->addresses, TRUE);
+                                       while (addresses->iterate(addresses, (void**)&current))
+                                       {
+                                               if (current->equals(current, host))
+                                               {
+                                                       addresses->remove(addresses);
+                                                       current->destroy(current);
+                                                       DBG1(DBG_KNL, "%H disappeared from %s", 
+                                                                host, entry->ifname);
+                                               }
+                                       }
+                                       addresses->destroy(addresses);
+                               }
+                       }
+                       iterator->destroy(iterator);
+                       break;
+               }
+               default:
+                       break;
        }
+       host->destroy(host);
 }
 
 /**
@@ -656,7 +719,7 @@ static void process_deladdr(private_kernel_interface_t *this, struct nlmsghdr *h
  */
 static job_requeue_t receive_events(private_kernel_interface_t *this)
 {
-       char response[512];
+       char response[1024];
        struct nlmsghdr *hdr = (struct nlmsghdr*)response;
        struct sockaddr_nl addr;
        socklen_t addr_len = sizeof(addr);
@@ -734,17 +797,18 @@ static job_requeue_t receive_events(private_kernel_interface_t *this)
                        switch (hdr->nlmsg_type)
                        {
                                case RTM_NEWADDR:
-                                       process_newaddr(this, hdr, FALSE);
-                                       break;
                                case RTM_DELADDR:
-                                       process_deladdr(this, hdr);
+                                       process_addr(this, hdr, TRUE);
+                                       break;
+                               case RTM_NEWLINK:
+                               case RTM_DELLINK:
+                                       process_link(this, hdr, TRUE);
                                        break;
                                default:
                                        break;
                        }
                }
                hdr = NLMSG_NEXT(hdr, len);
-               
        }
        return JOB_REQUEUE_DIRECT;
 }
@@ -752,7 +816,8 @@ static job_requeue_t receive_events(private_kernel_interface_t *this)
 /**
  * send a netlink message and wait for a reply
  */
-static status_t netlink_send(int socket, struct nlmsghdr *in,
+static status_t netlink_send(private_kernel_interface_t *this,
+                                                        int socket, struct nlmsghdr *in,
                                                         struct nlmsghdr **out, size_t *out_len)
 {
        int len, addr_len;
@@ -760,13 +825,9 @@ static status_t netlink_send(int socket, struct nlmsghdr *in,
        chunk_t result = chunk_empty, tmp;
        struct nlmsghdr *msg, peek;
        
-       static int seq = 200;
-       static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
-       
-       
-       pthread_mutex_lock(&mutex);
+       pthread_mutex_lock(&this->mutex);
        
-       in->nlmsg_seq = ++seq;
+       in->nlmsg_seq = ++this->seq;
        in->nlmsg_pid = getpid();
        
        memset(&addr, 0, sizeof(addr));
@@ -786,7 +847,7 @@ static status_t netlink_send(int socket, struct nlmsghdr *in,
                                /* interrupted, try again */
                                continue;
                        }
-                       pthread_mutex_unlock(&mutex);
+                       pthread_mutex_unlock(&this->mutex);
                        DBG1(DBG_KNL, "error sending to netlink socket: %s", strerror(errno));
                        return FAILED;
                }
@@ -818,23 +879,23 @@ static status_t netlink_send(int socket, struct nlmsghdr *in,
                                continue;
                        }
                        DBG1(DBG_IKE, "error reading from netlink socket: %s", strerror(errno));
-                       pthread_mutex_unlock(&mutex);
+                       pthread_mutex_unlock(&this->mutex);
                        return FAILED;
                }
                if (!NLMSG_OK(msg, len))
                {
                        DBG1(DBG_IKE, "received corrupted netlink message");
-                       pthread_mutex_unlock(&mutex);
+                       pthread_mutex_unlock(&this->mutex);
                        return FAILED;
                }
-               if (msg->nlmsg_seq != seq)
+               if (msg->nlmsg_seq != this->seq)
                {
                        DBG1(DBG_IKE, "received invalid netlink sequence number");
-                       if (msg->nlmsg_seq < seq)
+                       if (msg->nlmsg_seq < this->seq)
                        {
                                continue;
                        }
-                       pthread_mutex_unlock(&mutex);
+                       pthread_mutex_unlock(&this->mutex);
                        return FAILED;
                }
                
@@ -846,7 +907,7 @@ static status_t netlink_send(int socket, struct nlmsghdr *in,
                len = recvfrom(socket, &peek, sizeof(peek), MSG_PEEK | MSG_DONTWAIT,
                                           (struct sockaddr*)&addr, &addr_len);
                
-               if (len == sizeof(peek) && peek.nlmsg_seq == seq)
+               if (len == sizeof(peek) && peek.nlmsg_seq == this->seq)
                {
                        /* seems to be multipart */
                        continue;
@@ -857,7 +918,7 @@ static status_t netlink_send(int socket, struct nlmsghdr *in,
        *out_len = result.len;
        *out = (struct nlmsghdr*)clalloc(result.ptr, result.len);
        
-       pthread_mutex_unlock(&mutex);
+       pthread_mutex_unlock(&this->mutex);
        
        return SUCCESS;
 }
@@ -865,12 +926,13 @@ static status_t netlink_send(int socket, struct nlmsghdr *in,
 /**
  * send a netlink message and wait for its acknowlegde
  */
-static status_t netlink_send_ack(int socket, struct nlmsghdr *in)
+static status_t netlink_send_ack(private_kernel_interface_t *this,
+                                                                int socket, struct nlmsghdr *in)
 {
        struct nlmsghdr *out, *hdr;
        size_t len;
 
-       if (netlink_send(socket, in, &out, &len) != SUCCESS)
+       if (netlink_send(this, socket, in, &out, &len) != SUCCESS)
        {
                return FAILED;
        }
@@ -909,61 +971,116 @@ static status_t netlink_send_ack(int socket, struct nlmsghdr *in)
 /**
  * Initialize a list of local addresses.
  */
-static void init_address_list(private_kernel_interface_t *this)
+static status_t init_address_list(private_kernel_interface_t *this)
 {
        char request[BUFFER_SIZE];
-       struct nlmsghdr *out, *hdr;
+       struct nlmsghdr *out, *current, *in;
        struct rtgenmsg *msg;
        size_t len;
+       iterator_t *i_iface, *i_addr;
+       host_t *address;
+       interface_entry_t *entry;
        
        DBG1(DBG_IKE, "listening on interfaces:");
        
        memset(&request, 0, sizeof(request));
 
-       hdr = (struct nlmsghdr*)&request;
-       hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
-       hdr->nlmsg_type = RTM_GETADDR;
-       hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_MATCH | NLM_F_ROOT;
-       msg = (struct rtgenmsg*)NLMSG_DATA(hdr);
+       in = (struct nlmsghdr*)&request;
+       in->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg));
+       in->nlmsg_flags = NLM_F_REQUEST | NLM_F_MATCH | NLM_F_ROOT;
+       msg = (struct rtgenmsg*)NLMSG_DATA(in);
        msg->rtgen_family = AF_UNSPEC;
-               
-       if (netlink_send(this->socket_rt, hdr, &out, &len) == SUCCESS)
+       
+       /* get all links */
+       in->nlmsg_type = RTM_GETLINK;
+       if (netlink_send(this, this->socket_rt, in, &out, &len) != SUCCESS)
        {
-               hdr = out;
-               while (NLMSG_OK(hdr, len))
+               return FAILED;
+       }
+       current = out;
+       while (NLMSG_OK(current, len))
+       {
+               switch (current->nlmsg_type)
                {
-                       switch (hdr->nlmsg_type)
-                       {
-                               case RTM_NEWADDR:
-                               {
-                                       process_newaddr(this, hdr, TRUE);
-                                       hdr = NLMSG_NEXT(hdr, len);
-                                       continue;
-                               }
-                               default:
-                                       hdr = NLMSG_NEXT(hdr, len);
-                                       continue;
-                               case NLMSG_DONE:
-                                       break;
-                       }
-                       break;
+                       case NLMSG_DONE:
+                               break;
+                       case RTM_NEWLINK:
+                               process_link(this, current, FALSE);
+                               /* fall through */
+                       default:
+                               current = NLMSG_NEXT(current, len);
+                               continue;
                }
-               free(out);
+               break;
        }
-       else
+       free(out);
+       
+       /* get all interface addresses */
+       in->nlmsg_type = RTM_GETADDR;
+       if (netlink_send(this, this->socket_rt, in, &out, &len) != SUCCESS)
        {
-               DBG1(DBG_IKE, "unable to get local address list");
+               return FAILED;
        }
+       current = out;
+       while (NLMSG_OK(current, len))
+       {
+               switch (current->nlmsg_type)
+               {
+                       case NLMSG_DONE:
+                               break;
+                       case RTM_NEWADDR:
+                               process_addr(this, current, FALSE);
+                               /* fall through */
+                       default:
+                               current = NLMSG_NEXT(current, len);
+                               continue;
+               }
+               break;
+       }
+       free(out);
+       
+       i_iface = this->interfaces->create_iterator_locked(this->interfaces,
+                                                                                                          &this->mutex);
+       while (i_iface->iterate(i_iface, (void**)&entry))
+       {
+               if (entry->flags & IFF_UP)
+               {
+                       DBG1(DBG_KNL, "  %s", entry->ifname);
+                       i_addr = entry->addresses->create_iterator(entry->addresses, TRUE);
+                       while (i_addr->iterate(i_addr, (void**)&address))
+                       {
+                               DBG1(DBG_KNL, "    %H", address);
+                       }
+                       i_addr->destroy(i_addr);
+               }
+       }
+       i_iface->destroy(i_iface);
+       
+       return SUCCESS;
 }
 
 /**
  * iterator hook to return address, not address_entry_t
  */
-bool address_iterator_hook(private_kernel_interface_t *this,
-                                                  address_entry_t *in, host_t **out)
+static hook_result_t hook(private_kernel_interface_t *this,
+                                                 interface_entry_t *in, host_t **out)
 {
-       *out = in->host;
-       return TRUE;
+       if (!(in->flags & IFF_UP))
+       {       /* skip interfaces not up */
+               return HOOK_SKIP;
+       }
+
+       if (this->hiter == NULL)
+       {
+               this->hiter = in->addresses->create_iterator(in->addresses, TRUE);
+       }
+       while (this->hiter->iterate(this->hiter, (void**)out))
+       {
+               return HOOK_AGAIN;
+       }
+       this->hiter->destroy(this->hiter);
+       this->hiter = NULL;
+       return HOOK_SKIP;
 }
 
 /**
@@ -973,9 +1090,9 @@ static iterator_t *create_address_iterator(private_kernel_interface_t *this)
 {
        iterator_t *iterator;
        
-       iterator = this->addrs->create_iterator_locked(this->addrs, &this->addrs_mutex);
-       iterator->set_iterator_hook(iterator,
-                                                               (iterator_hook_t*)address_iterator_hook, this);
+       iterator = this->interfaces->create_iterator_locked(this->interfaces,
+                                                                                                               &this->mutex);
+       iterator->set_iterator_hook(iterator, (iterator_hook_t*)hook, this);
        return iterator;
 }
 
@@ -984,18 +1101,29 @@ static iterator_t *create_address_iterator(private_kernel_interface_t *this)
  */
 static char *get_interface_name(private_kernel_interface_t *this, host_t* ip)
 {
-       iterator_t *iterator;
-       address_entry_t *entry;
+       iterator_t *iterator, *addresses;
+       interface_entry_t *entry;
+       host_t *host;
        char *name = NULL;
        
        DBG2(DBG_IKE, "getting interface name for %H", ip);
        
-       iterator = this->addrs->create_iterator_locked(this->addrs, &this->addrs_mutex);
+       iterator = this->interfaces->create_iterator_locked(this->interfaces,   
+                                                                                                               &this->mutex);
        while (iterator->iterate(iterator, (void**)&entry))
        {
-               if (ip->ip_equals(ip, entry->host))
+               addresses = entry->addresses->create_iterator(entry->addresses, TRUE);
+               while (addresses->iterate(addresses, (void**)&host))
+               {
+                       if (ip->ip_equals(ip, host))
+                       {
+                               name = strdup(entry->ifname);
+                               break;
+                       }
+               }
+               addresses->destroy(addresses);
+               if (name)
                {
-                       name = strdup(entry->ifname);
                        break;
                }
        }
@@ -1019,8 +1147,8 @@ static char *get_interface_name(private_kernel_interface_t *this, host_t* ip)
 static status_t get_address_by_ts(private_kernel_interface_t *this,
                                                                  traffic_selector_t *ts, host_t **ip)
 {
-       iterator_t *iterator;
-       address_entry_t *entry;
+       iterator_t *iterator, *addresses;
+       interface_entry_t *entry;
        host_t *host;
        int family;
        bool found = FALSE;
@@ -1049,13 +1177,23 @@ static status_t get_address_by_ts(private_kernel_interface_t *this,
        }
        host->destroy(host);
        
-       iterator = this->addrs->create_iterator_locked(this->addrs, &this->addrs_mutex);
+       iterator = this->interfaces->create_iterator_locked(this->interfaces,   
+                                                                                                               &this->mutex);
        while (iterator->iterate(iterator, (void**)&entry))
        {
-               if (ts->includes(ts, entry->host))
+               addresses = entry->addresses->create_iterator(entry->addresses, TRUE);
+               while (addresses->iterate(addresses, (void**)&host))
+               {
+                       if (ts->includes(ts, host))
+                       {
+                               found = TRUE;
+                               *ip = host->clone(host);
+                               break;
+                       }
+               }
+               addresses->destroy(addresses);
+               if (found)
                {
-                       found = TRUE;
-                       *ip = entry->host->clone(entry->host);
                        break;
                }
        }
@@ -1075,23 +1213,34 @@ static status_t get_address_by_ts(private_kernel_interface_t *this,
  */
 static int get_interface_index(private_kernel_interface_t *this, host_t* ip)
 {
-       iterator_t *iterator;
-       address_entry_t *entry;
+       iterator_t *iterator, *addresses;
+       interface_entry_t *entry;
+       host_t *host;
        int ifindex = 0;
        
        DBG2(DBG_IKE, "getting iface for %H", ip);
        
-       iterator = this->addrs->create_iterator_locked(this->addrs, &this->addrs_mutex);
+       iterator = this->interfaces->create_iterator_locked(this->interfaces,   
+                                                                                                               &this->mutex);
        while (iterator->iterate(iterator, (void**)&entry))
        {
-               if (ip->ip_equals(ip, entry->host))
+               addresses = entry->addresses->create_iterator(entry->addresses, TRUE);
+               while (addresses->iterate(addresses, (void**)&host))
+               {
+                       if (ip->ip_equals(ip, host))
+                       {
+                               ifindex = entry->ifindex;
+                               break;
+                       }
+               }
+               addresses->destroy(addresses);
+               if (ifindex)
                {
-                       ifindex = entry->ifindex;
                        break;
                }
        }
        iterator->destroy(iterator);
-       
+
        if (ifindex == 0)
        {
                DBG1(DBG_IKE, "unable to get interface for %H", ip);
@@ -1129,7 +1278,7 @@ static status_t manage_ipaddr(private_kernel_interface_t *this, int nlmsg_type,
        
        add_attribute(hdr, IFA_LOCAL, chunk, sizeof(request));
 
-       return netlink_send_ack(this->socket_rt, hdr);
+       return netlink_send_ack(this, this->socket_rt, hdr);
 }
 
 /**
@@ -1190,7 +1339,7 @@ static status_t manage_srcroute(private_kernel_interface_t *this, int nlmsg_type
        chunk.len = sizeof(route->if_index);
        add_attribute(hdr, RTA_OIF, chunk, sizeof(request));
 
-       return netlink_send_ack(this->socket_rt, hdr);
+       return netlink_send_ack(this, this->socket_rt, hdr);
 }
 
 
@@ -1215,7 +1364,7 @@ static status_t add_ip(private_kernel_interface_t *this,
        }
 
        /* beware of deadlocks (e.g. send/receive packets while holding the lock) */
-       iterator = this->vips->create_iterator_locked(this->vips, &(this->vips_mutex));
+       iterator = this->vips->create_iterator_locked(this->vips, &this->mutex);
        while (iterator->iterate(iterator, (void**)&listed))
        {
                if (listed->if_index == targetif &&
@@ -1269,7 +1418,7 @@ static status_t del_ip(private_kernel_interface_t *this,
        }
 
        /* beware of deadlocks (e.g. send/receive packets while holding the lock) */
-       iterator = this->vips->create_iterator_locked(this->vips, &(this->vips_mutex));
+       iterator = this->vips->create_iterator_locked(this->vips, &this->mutex);
        while (iterator->iterate(iterator, (void**)&listed))
        {
                if (listed->if_index == targetif &&
@@ -1328,7 +1477,7 @@ static status_t get_spi(private_kernel_interface_t *this,
        userspi->min = 0xc0000000;
        userspi->max = 0xcFFFFFFF;
        
-       if (netlink_send(this->socket_xfrm, hdr, &out, &len) == SUCCESS)
+       if (netlink_send(this, this->socket_xfrm, hdr, &out, &len) == SUCCESS)
        {
                hdr = out;
                while (NLMSG_OK(hdr, len))
@@ -1506,7 +1655,7 @@ static status_t add_sa(private_kernel_interface_t *this,
                rthdr = XFRM_RTA_NEXT(rthdr);
        }
        
-       if (netlink_send_ack(this->socket_xfrm, hdr) != SUCCESS)
+       if (netlink_send_ack(this, this->socket_xfrm, hdr) != SUCCESS)
        {
                DBG1(DBG_KNL, "unalbe to add SAD entry with SPI 0x%x", spi);
                return FAILED;
@@ -1544,7 +1693,7 @@ static status_t update_sa(private_kernel_interface_t *this,
        sa_id->proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH;
        sa_id->family = dst->get_family(dst);
        
-       if (netlink_send(this->socket_xfrm, hdr, &out, &len) == SUCCESS)
+       if (netlink_send(this, this->socket_xfrm, hdr, &out, &len) == SUCCESS)
        {
                hdr = out;
                while (NLMSG_OK(hdr, len))
@@ -1613,7 +1762,7 @@ static status_t update_sa(private_kernel_interface_t *this,
                        rtattr = RTA_NEXT(rtattr, rtsize);
                }
        }
-       if (netlink_send_ack(this->socket_xfrm, hdr) != SUCCESS)
+       if (netlink_send_ack(this, this->socket_xfrm, hdr) != SUCCESS)
        {
                DBG1(DBG_KNL, "unalbe to update SAD entry with SPI 0x%x", spi);
                free(out);
@@ -1655,7 +1804,7 @@ static status_t query_sa(private_kernel_interface_t *this, host_t *dst,
        sa_id->proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH;
        sa_id->family = dst->get_family(dst);
        
-       if (netlink_send(this->socket_xfrm, hdr, &out, &len) == SUCCESS)
+       if (netlink_send(this, this->socket_xfrm, hdr, &out, &len) == SUCCESS)
        {
                hdr = out;
                while (NLMSG_OK(hdr, len))
@@ -1721,7 +1870,7 @@ static status_t del_sa(private_kernel_interface_t *this, host_t *dst,
        sa_id->proto = (protocol == PROTO_ESP) ? KERNEL_ESP : KERNEL_AH;
        sa_id->family = dst->get_family(dst);
        
-       if (netlink_send_ack(this->socket_xfrm, hdr) != SUCCESS)
+       if (netlink_send_ack(this, this->socket_xfrm, hdr) != SUCCESS)
        {
                DBG1(DBG_KNL, "unalbe to delete SAD entry with SPI 0x%x", spi);
                return FAILED;
@@ -1755,7 +1904,7 @@ static status_t add_policy(private_kernel_interface_t *this,
        policy->direction = direction;
        
        /* find the policy, which matches EXACTLY */
-       pthread_mutex_lock(&this->policies_mutex);
+       pthread_mutex_lock(&this->mutex);
        iterator = this->policies->create_iterator(this->policies, TRUE);
        while (iterator->iterate(iterator, (void**)&current))
        {
@@ -1800,7 +1949,7 @@ static status_t add_policy(private_kernel_interface_t *this,
        policy_info->priority -= policy->sel.sport_mask ? 1 : 0;
        policy_info->action = XFRM_POLICY_ALLOW;
        policy_info->share = XFRM_SHARE_ANY;
-       pthread_mutex_unlock(&this->policies_mutex);
+       pthread_mutex_unlock(&this->mutex);
        
        /* policies don't expire */
        policy_info->lft.soft_byte_limit = XFRM_INF;
@@ -1834,7 +1983,7 @@ static status_t add_policy(private_kernel_interface_t *this,
        host2xfrm(src, &tmpl->saddr);
        host2xfrm(dst, &tmpl->id.daddr);
        
-       if (netlink_send_ack(this->socket_xfrm, hdr) != SUCCESS)
+       if (netlink_send_ack(this, this->socket_xfrm, hdr) != SUCCESS)
        {
                DBG1(DBG_KNL, "unable to add policy %R===%R", src_ts, dst_ts);
                return FAILED;
@@ -1904,7 +2053,7 @@ static status_t query_policy(private_kernel_interface_t *this,
        policy_id->sel = ts2selector(src_ts, dst_ts);
        policy_id->dir = direction;
        
-       if (netlink_send(this->socket_xfrm, hdr, &out, &len) == SUCCESS)
+       if (netlink_send(this, this->socket_xfrm, hdr, &out, &len) == SUCCESS)
        {
                hdr = out;
                while (NLMSG_OK(hdr, len))
@@ -1968,8 +2117,7 @@ static status_t del_policy(private_kernel_interface_t *this,
        policy.direction = direction;
        
        /* find the policy */
-       pthread_mutex_lock(&this->policies_mutex);
-       iterator = this->policies->create_iterator(this->policies, TRUE);
+       iterator = this->policies->create_iterator_locked(this->policies, &this->mutex);
        while (iterator->iterate(iterator, (void**)&current))
        {
                if (memcmp(&current->sel, &policy.sel, sizeof(struct xfrm_selector)) == 0 &&
@@ -1981,7 +2129,6 @@ static status_t del_policy(private_kernel_interface_t *this,
                                /* is used by more SAs, keep in kernel */
                                DBG2(DBG_KNL, "policy still used by another CHILD_SA, not removed");
                                iterator->destroy(iterator);
-                               pthread_mutex_unlock(&this->policies_mutex);
                                return SUCCESS;
                        }
                        /* remove if last reference */
@@ -1990,7 +2137,6 @@ static status_t del_policy(private_kernel_interface_t *this,
                }
        }
        iterator->destroy(iterator);
-       pthread_mutex_unlock(&this->policies_mutex);
        if (!to_delete)
        {
                DBG1(DBG_KNL, "deleting policy %R===%R failed, not found", src_ts, dst_ts);
@@ -2011,7 +2157,7 @@ static status_t del_policy(private_kernel_interface_t *this,
        route = to_delete->route;
        free(to_delete);
        
-       if (netlink_send_ack(this->socket_xfrm, hdr) != SUCCESS)
+       if (netlink_send_ack(this, this->socket_xfrm, hdr) != SUCCESS)
        {
                DBG1(DBG_KNL, "unable to delete policy %R===%R", src_ts, dst_ts);
                return FAILED;
@@ -2041,7 +2187,7 @@ static void destroy(private_kernel_interface_t *this)
        close(this->socket_rt);
        this->vips->destroy(this->vips);
        this->policies->destroy(this->policies);
-       this->addrs->destroy_function(this->addrs, (void*)address_entry_destroy);
+       this->interfaces->destroy_function(this->interfaces, (void*)interface_entry_destroy);
        free(this);
 }
 
@@ -2053,6 +2199,7 @@ kernel_interface_t *kernel_interface_create()
        private_kernel_interface_t *this = malloc_thing(private_kernel_interface_t);
        struct sockaddr_nl addr;
        
+       
        /* public functions */
        this->public.get_spi = (status_t(*)(kernel_interface_t*,host_t*,host_t*,protocol_id_t,u_int32_t,u_int32_t*))get_spi;
        this->public.add_sa  = (status_t(*)(kernel_interface_t *,host_t*,host_t*,u_int32_t,protocol_id_t,u_int32_t,u_int64_t,u_int64_t,algorithm_t*,algorithm_t*,prf_plus_t*,natt_conf_t*,mode_t,bool))add_sa;
@@ -2072,10 +2219,10 @@ kernel_interface_t *kernel_interface_create()
        /* private members */
        this->vips = linked_list_create();
        this->policies = linked_list_create();
-       this->addrs = linked_list_create();
-       pthread_mutex_init(&this->policies_mutex,NULL);
-       pthread_mutex_init(&this->vips_mutex,NULL);
-       pthread_mutex_init(&this->addrs_mutex,NULL);
+       this->interfaces = linked_list_create();
+       this->hiter = NULL;
+       this->seq = 200;
+       pthread_mutex_init(&this->mutex,NULL);
        
        memset(&addr, 0, sizeof(addr));
        addr.nl_family = AF_NETLINK;
@@ -2092,13 +2239,13 @@ kernel_interface_t *kernel_interface_create()
                charon->kill(charon, "unable to bind RT netlink socket");
        }
        
-       /* create and bind RT socket for events (address changes) */
+       /* create and bind RT socket for events (address/interface changes) */
        this->socket_rt_events = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
        if (this->socket_rt_events <= 0)
        {
                charon->kill(charon, "unable to create RT event socket");
        }
-       addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR;
+       addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | RTMGRP_LINK;
        if (bind(this->socket_rt_events, (struct sockaddr*)&addr, sizeof(addr)))
        {
                charon->kill(charon, "unable to bind RT event socket");
@@ -2132,7 +2279,10 @@ kernel_interface_t *kernel_interface_create()
                                                                        this, NULL, NULL);
        charon->processor->queue_job(charon->processor, (job_t*)this->job);
        
-       init_address_list(this);
+       if (init_address_list(this) != SUCCESS)
+       {
+               charon->kill(charon, "unable to get interface list");
+       }
        
        return &this->public;
 }
index a62ec5e..bc90940 100644 (file)
@@ -682,16 +682,16 @@ static ike_sa_t* checkout_by_name(private_ike_sa_manager_t *this, char *name,
 /**
  * Iterator hook for iterate, gets ike_sas instead of entries
  */
-static bool iterator_hook(private_ike_sa_manager_t* this, entry_t *in,
-                                                 ike_sa_t **out)
+static hook_result_t iterator_hook(private_ike_sa_manager_t* this, entry_t *in,
+                                                                  ike_sa_t **out)
 {
        /* check out entry */
        if (wait_for_entry(this, in))
        {
                *out = in->ike_sa;
-               return TRUE;
+               return HOOK_NEXT;
        }
-       return FALSE;
+       return HOOK_SKIP;
 }
 
 /**
index 02a15c5..b4ff85b 100644 (file)
 
 #include <library.h>
 
+typedef enum hook_result_t hook_result_t;
+
+/**
+ * @brief Return value of an iterator hook.
+ *
+ * Returning HOOK_AGAIN is useful to "inject" additional elements in an
+ * iteration, HOOK_NEXT is the normal iterator behavior, and HOOK_SKIP may
+ * be used to filter elements out.
+ *
+ * @ingroup utils
+ */
+enum hook_result_t {
+
+       /**
+        * A value was placed in out, hook is called again with the same "in"
+        */
+       HOOK_AGAIN,
+       
+       /**
+        * A value was placed in out, hook is called again with next "in" (if any)
+        */
+       HOOK_NEXT,
+       
+       /**
+        * No value in out, call again with next "in" (if any)
+        */
+       HOOK_SKIP,
+};
+
 /**
  * @brief Iterator hook function prototype.
  *
  * @param param                user supplied parameter
  * @param in           the value the hook receives from the iterator
  * @param out          the value supplied as a result to the iterator
- * @return                     TRUE to return "out", FALSE to skip this value
+ * @return                     a hook_result_t
+ *
+ * @ingroup utils
  */
-typedef bool (iterator_hook_t)(void *param, void *in, void **out);
+typedef hook_result_t (iterator_hook_t)(void *param, void *in, void **out);
 
 
 typedef struct iterator_t iterator_t;
@@ -45,8 +76,6 @@ typedef struct iterator_t iterator_t;
  * iterator_t defines an interface for iterating over collections.
  * It allows searching, deleting, updating and inserting.
  *
- * Thanks to JMP for iterator lessons :-)
- *
  * @b Constructors:
  * - via linked_list_t.create_iterator, or
  * - any other class which supports the iterator_t interface
@@ -84,8 +113,11 @@ struct iterator_t {
         * Sometimes it is useful to hook in an iterator. The hook function is
         * called before any successful return of iterate(). It takes the
         * iterator value, may manipulate it (or the references object), and returns
-        * the value that the iterate() function returns.
+        * the value that the iterate() function returns. Depending on the hook
+        * return value, the hook is called again, called with next, or skipped.
         * A value of NULL deactivates the iterator hook.
+        * If an iterator is hooked, only the iterate() method is valid,
+        * all other methods behave undefined.
         * 
         * @param this                  calling object
         * @param hook                  iterator hook which manipulates the iterated value
index de043a0..de52ea4 100644 (file)
@@ -151,10 +151,10 @@ static int get_list_count(private_iterator_t *this)
 /**
  * default iterator hook which does nothing
  */
-static bool iterator_hook(void *param, void *in, void **out)
+static hook_result_t iterator_hook(void *param, void *in, void **out)
 {
        *out = in;
-       return TRUE;
+       return HOOK_NEXT;
 }
 
 /**
@@ -180,40 +180,43 @@ static void set_iterator_hook(private_iterator_t *this, iterator_hook_t *hook,
  */
 static bool iterate(private_iterator_t *this, void** value)
 {
-       if (this->list->count == 0)
-       {
-               return FALSE;
-       }
-       if (this->current == NULL)
+       while (TRUE)
        {
-               this->current = (this->forward) ? this->list->first : this->list->last;
-               if (!this->hook(this->hook_param, this->current->value, value))
+               if (this->forward)
                {
-                       return iterate(this, value);
+                       this->current = this->current ? this->current->next : this->list->first;
                }
-               return TRUE;
-       }
-       if (this->forward)
-       {
-               if (this->current->next == NULL)
+               else
+               {
+                       this->current = this->current ? this->current->previous : this->list->last;
+               }
+
+               if (this->current == NULL)
                {
                        return FALSE;
                }
-               this->current = this->current->next;
-               if (!this->hook(this->hook_param, this->current->value, value))
+       
+               switch (this->hook(this->hook_param, this->current->value, value))
                {
-                       return iterate(this, value);
+                       case HOOK_AGAIN:
+                               /* rewind */
+                               if (this->forward)
+                               {
+                                       this->current = this->current->previous;
+                               }
+                               else
+                               {
+                                       this->current = this->current->next;
+                               }
+                               break;
+                       case HOOK_NEXT:
+                               /* normal iteration */
+                               break;
+                       case HOOK_SKIP:
+                               /* advance */
+                               continue;
                }
-               return TRUE;
-       }
-       if (this->current->previous == NULL)
-       {
-               return FALSE;
-       }
-       this->current = this->current->previous;
-       if (!this->hook(this->hook_param, this->current->value, value))
-       {
-               return iterate(this, value);
+               break;
        }
        return TRUE;
 }