implemented updown script to handle firewalling
authorMartin Willi <martin@strongswan.org>
Tue, 12 Sep 2006 13:50:14 +0000 (13:50 -0000)
committerMartin Willi <martin@strongswan.org>
Tue, 12 Sep 2006 13:50:14 +0000 (13:50 -0000)
src/charon/config/traffic_selector.c
src/charon/network/socket.c
src/charon/network/socket.h
src/charon/sa/child_sa.c
src/charon/sa/child_sa.h
src/charon/sa/ike_sa.c
src/charon/sa/transactions/create_child_sa.c
src/charon/sa/transactions/ike_auth.c
src/charon/threads/stroke_interface.c

index e567c6e..ba68031 100644 (file)
@@ -177,10 +177,7 @@ static void update_string(private_traffic_selector_t *this)
                
                /* build network mask string */
                mask = calc_netbits(this);
-               if (mask != 32)
-               {
-                       snprintf(mask_str, sizeof(mask_str), "/%d", mask);
-               }
+               snprintf(mask_str, sizeof(mask_str), "/%d", mask);
        }
        else
        {
@@ -191,10 +188,7 @@ static void update_string(private_traffic_selector_t *this)
                
                /* build network mask string */
                mask = calc_netbits(this);
-               if (mask != 128)
-               {
-                       snprintf(mask_str, sizeof(mask_str), "/%d", mask);
-               }
+               snprintf(mask_str, sizeof(mask_str), "/%d", mask);
        }
        
        /* build protocol string */
index 81d9d63..adac639 100644 (file)
@@ -398,7 +398,7 @@ status_t sender(private_socket_t *this, packet_t *packet)
 /**
  * implements socket_t.is_local_address
  */
-static bool is_local_address(private_socket_t *this, host_t *host)
+static bool is_local_address(private_socket_t *this, host_t *host, char **dev)
 {
        struct ifaddrs *list;
        struct ifaddrs *cur;
@@ -455,6 +455,11 @@ static bool is_local_address(private_socket_t *this, host_t *host)
                
                if (found)
                {
+                       if (dev && cur->ifa_name)
+                       {
+                               /* return interface name, if requested */
+                               *dev = strdup(cur->ifa_name);
+                       }
                        break;
                }
        }
@@ -768,7 +773,7 @@ socket_t *socket_create(u_int16_t port, u_int16_t natt_port)
        /* public functions */
        this->public.send = (status_t(*)(socket_t*, packet_t*))sender;
        this->public.receive = (status_t(*)(socket_t*, packet_t**))receiver;
-       this->public.is_local_address = (bool(*)(socket_t*, host_t*))is_local_address;
+       this->public.is_local_address = (bool(*)(socket_t*, host_t*,char**))is_local_address;
        this->public.create_local_address_list = (linked_list_t*(*)(socket_t*))create_local_address_list;
        this->public.destroy = (void(*)(socket_t*)) destroy;
 
index 34a06c0..431d9f2 100644 (file)
@@ -93,11 +93,15 @@ struct socket_t {
        /**
         * @brief Check if an address is an address of this host.
         *
+        * If the name parameter is not NULL, a string is allocated which
+        * holds the interfaces name. 
+        *
         * @param this                  socket_t object to work on
         * @param host                  address to check
+        * @param name[out]             interface name on which address is used
         * @return                              TRUE if local address, FALSE otherwise
         */
-       bool (*is_local_address) (socket_t *this, host_t *host);
+       bool (*is_local_address) (socket_t *this, host_t *host, char **name);
        
        /**
         * @brief Create a list of hosts with all local addresses.
index 79cff7a..b1335fa 100644 (file)
@@ -80,6 +80,8 @@ struct private_child_sa_t {
        struct {
                /** address of peer */
                host_t *addr;
+               /** id of peer */
+               identification_t *id;
                /** actual used SPI, 0 if unused */
                u_int32_t spi;
        } me, other;
@@ -155,14 +157,14 @@ struct private_child_sa_t {
        void *rekeying_transaction;
 
        /**
-        * Specifies if NAT traversal is used
+        * Updown script
         */
-       bool use_natt;
+       char *script;
 
        /**
-        * Specifies if CHILD_SA goes to ROUTED state if DPD detected
+        * Specifies if NAT traversal is used
         */
-       bool stays_routed;
+       bool use_natt;
        
        /**
         * CHILD_SAs own logger
@@ -230,11 +232,156 @@ static child_sa_state_t get_state(private_child_sa_t *this)
 }
 
 /**
+ * Run the up/down script
+ */
+static void updown(private_child_sa_t *this, bool up)
+{
+       iterator_t *iterator;
+       
+       if (this->script == NULL)
+       {
+               return;
+       }
+       
+       iterator = this->policies->create_iterator(this->policies, TRUE);
+       while (iterator->has_next(iterator))
+       {
+               sa_policy_t *policy;
+               char command[1024];
+               char *ifname = NULL;
+               char *my_str, *other_str;
+               char *my_client, *other_client, *my_client_mask, *other_client_mask;
+               char *pos;
+               FILE *shell;
+               
+               /* get ts strings */
+               iterator->current(iterator, (void**)&policy);
+               my_str = policy->my_ts->get_string(policy->my_ts);
+               other_str = policy->other_ts->get_string(policy->other_ts);
+               
+               /* get subnet/bits from string */
+               my_client = strdup(my_str);
+               pos = strchr(my_client, '/');
+               *pos = '\0';
+               my_client_mask = pos + 1;
+               pos = strchr(my_client_mask, '[');
+               if (pos)
+               {
+                       *pos = '\0';
+               }
+               other_client = strdup(other_str);
+               pos = strchr(other_client, '/');
+               *pos = '\0';
+               other_client_mask = pos + 1;
+               pos = strchr(other_client_mask, '[');
+               if (pos)
+               {
+                       *pos = '\0';
+               }
+               
+               charon->socket->is_local_address(charon->socket, this->me.addr, &ifname);
+               
+               /* build the command with all env variables.
+               * TODO: PLUTO_MY_SRCIP, PLUTO_PEER_CA and PLUTO_NEXT_HOP 
+               * are currently missing */
+               snprintf(command, sizeof(command),
+                                "2>&1 "
+                               "PLUTO_VERSION='1.1' "
+                               "PLUTO_VERB='%s%s%s' "
+                               "PLUTO_CONNECTION='%s' "
+                               "PLUTO_INTERFACE='%s' "
+                               "PLUTO_REQID='%u' "
+                               "PLUTO_ME='%s' "
+                               "PLUTO_MY_ID='%s' "
+                               "PLUTO_MY_CLIENT='%s/%s' "
+                               "PLUTO_MY_CLIENT_NET='%s' "
+                               "PLUTO_MY_CLIENT_MASK='%s' "
+                               "PLUTO_MY_PORT='%u' "
+                               "PLUTO_MY_PROTOCOL='%u' "
+                               "PLUTO_PEER='%s' "
+                               "PLUTO_PEER_ID='%s' "
+                               "PLUTO_PEER_CLIENT='%s/%s' "
+                               "PLUTO_PEER_CLIENT_NET='%s' "
+                               "PLUTO_PEER_CLIENT_MASK='%s' "
+                               "PLUTO_PEER_PORT='%u' "
+                               "PLUTO_PEER_PROTOCOL='%u' "
+                               "%s",
+                                up ? "up" : "down",
+                                streq(this->me.addr->get_string(this->me.addr),
+                                          my_client) ? "-host" : "-client",
+                                this->me.addr->get_family(this->me.addr) == AF_INET ? "" : "-ipv6",
+                                this->name,
+                                ifname,
+                                this->reqid,
+                                this->me.addr->get_string(this->me.addr),
+                                this->me.id->get_string(this->me.id),
+                                my_client, my_client_mask,
+                                my_client, my_client_mask,
+                                policy->my_ts->get_from_port(policy->my_ts),
+                                policy->my_ts->get_protocol(policy->my_ts),
+                                this->other.addr->get_string(this->other.addr),
+                                this->other.id->get_string(this->other.id),
+                                other_client, other_client_mask,
+                                other_client, other_client_mask,
+                                policy->other_ts->get_from_port(policy->other_ts),
+                                policy->other_ts->get_protocol(policy->other_ts),
+                                this->script);
+               free(ifname);
+               free(my_client);
+               free(other_client);
+               
+               shell = popen(command, "r");
+
+               if (shell == NULL)
+               {
+                       this->logger->log(this->logger, ERROR,
+                                                         "could not execute updown script '%s'",
+                                                         this->script);
+                       return;
+               }
+               
+               while (TRUE)
+               {
+                       char resp[128];
+                       
+                       if (fgets(resp, sizeof(resp), shell) == NULL)
+                       {
+                               if (ferror(shell))
+                               {
+                                       this->logger->log(this->logger, ERROR,
+                                                                         "error reading output from updown script");
+                                       return;
+                               }
+                               else
+                               {
+                                       break;
+                               }
+                       }
+                       else
+                       {
+                               char *e = resp + strlen(resp);
+                               if (e > resp && e[-1] == '\n')
+                               {       /* trim trailing '\n' */
+                                       e[-1] = '\0';
+                               }
+                               this->logger->log(this->logger, ERROR, "updown: %s", resp);
+                       }
+               }
+               pclose(shell);
+       }
+       iterator->destroy(iterator);
+}
+
+/**
  * Implements child_sa_t.set_state
  */
 static void set_state(private_child_sa_t *this, child_sa_state_t state)
 {
        this->state = state;
+       if (state == CHILD_INSTALLED)
+       {
+               updown(this, TRUE);
+       }
 }
 
 /**
@@ -440,8 +587,6 @@ static status_t add(private_child_sa_t *this, proposal_t *proposal, prf_plus_t *
        }
        proposal->set_spi(proposal, inbound_spi);
        
-       this->state = CHILD_INSTALLED;
-       
        return SUCCESS;
 }
 
@@ -466,8 +611,6 @@ static status_t update(private_child_sa_t *this, proposal_t *proposal, prf_plus_
                return FAILED;
        }
        
-       this->state = CHILD_INSTALLED;
-       
        return SUCCESS;
 }
 
@@ -903,6 +1046,11 @@ static void destroy(private_child_sa_t *this)
 {
        sa_policy_t *policy;
        
+       if (this->state == CHILD_DELETING || this->state == CHILD_INSTALLED)
+       {
+               updown(this, FALSE);
+       }
+       
        /* delete SAs in the kernel, if they are set up */
        if (this->me.spi)
        {
@@ -950,16 +1098,20 @@ static void destroy(private_child_sa_t *this)
        this->other_ts->destroy(this->other_ts);
        this->me.addr->destroy(this->me.addr);
        this->other.addr->destroy(this->other.addr);
+       this->me.id->destroy(this->me.id);
+       this->other.id->destroy(this->other.id);
        free(this->name);
+       free(this->script);
        free(this);
 }
 
 /*
  * Described in header.
  */
-child_sa_t * child_sa_create(u_int32_t rekey, host_t *me, host_t* other, 
+child_sa_t * child_sa_create(u_int32_t rekey, host_t *me, host_t* other,
+                                                        identification_t *my_id, identification_t *other_id,
                                                         u_int32_t soft_lifetime, u_int32_t hard_lifetime,
-                                                        bool use_natt)
+                                                        char *script, bool use_natt)
 {
        static u_int32_t reqid = REQID_START;
        private_child_sa_t *this = malloc_thing(private_child_sa_t);
@@ -990,10 +1142,13 @@ child_sa_t * child_sa_create(u_int32_t rekey, host_t *me, host_t* other,
        this->name = strdup("(uninitialized)");
        this->me.addr = me->clone(me);
        this->other.addr = other->clone(other);
+       this->me.id = my_id->clone(my_id);
+       this->other.id = other_id->clone(other_id);
        this->me.spi = 0;
        this->other.spi = 0;
        this->alloc_ah_spi = 0;
        this->alloc_esp_spi = 0;
+       this->script = script ? strdup(script) : NULL;
        this->use_natt = use_natt;
        this->soft_lifetime = soft_lifetime;
        this->hard_lifetime = hard_lifetime;
index 5129ffe..e8e97c6 100644 (file)
@@ -306,15 +306,19 @@ struct child_sa_t {
  * @param rekey_reqid  reqid of old CHILD_SA when rekeying, 0 otherwise
  * @param me                   own address
  * @param other                        remote address
+ * @param my_id                        id of own peer
+ * @param other_id             id of remote peer
  * @param soft_lifetime        time before rekeying
  * @param hard_lifteime        time before delete
+ * @param script               updown script to use when calling child_sa_t.script()
  * @param use_natt             TRUE if NAT traversal is used
  * @return                             child_sa_t object
  * 
  * @ingroup sa
  */
-child_sa_t * child_sa_create(u_int32_t rekey_reqid, host_t *me, host_t *other, 
+child_sa_t * child_sa_create(u_int32_t rekey_reqid, host_t *me, host_t *other,
+                                                        identification_t *my_id, identification_t* other_id,
                                                         u_int32_t soft_lifetime, u_int32_t hard_lifetime,
-                                                        bool use_natt);
+                                                        char *script, bool use_natt);
 
 #endif /*CHILD_SA_H_*/
index 55c3302..f829986 100644 (file)
@@ -993,7 +993,6 @@ static status_t acquire(private_ike_sa_t *this, u_int32_t reqid)
        {
                if (current->get_reqid(current) == reqid)
                {
-                       //iterator->remove(iterator);
                        child_sa = current;
                        break;
                }
@@ -1013,7 +1012,6 @@ static status_t acquire(private_ike_sa_t *this, u_int32_t reqid)
                                                                                  this->my_id, this->other_id, 
                                                                                  my_ts, other_ts, 
                                                                                  this->my_host, this->other_host);
-       //child_sa->destroy(child_sa);
        if (policy == NULL)
        {
                this->logger->log(this->logger, ERROR, 
@@ -1200,7 +1198,8 @@ static status_t route(private_ike_sa_t *this, connection_t *connection, policy_t
        }
        
        child_sa = child_sa_create(0, this->my_host, this->other_host,
-                                                          0, 0, FALSE);
+                                                          this->my_id, this->other_id,
+                                                          0, 0, NULL, FALSE);
        child_sa->set_name(child_sa, policy->get_name(policy));
        my_ts = policy->get_my_traffic_selectors(policy, this->my_host);
        other_ts = policy->get_other_traffic_selectors(policy, this->other_host);
index d88c234..154a8e8 100644 (file)
@@ -221,6 +221,7 @@ static status_t get_request(private_create_child_sa_t *this, message_t **result)
 {
        message_t *request;
        host_t *me, *other;
+       identification_t *my_id, *other_id;
        
        /* check if we already have built a message (retransmission) */
        if (this->message)
@@ -250,6 +251,8 @@ static status_t get_request(private_create_child_sa_t *this, message_t **result)
        
        me = this->ike_sa->get_my_host(this->ike_sa);
        other = this->ike_sa->get_other_host(this->ike_sa);
+       my_id = this->ike_sa->get_my_id(this->ike_sa);
+       other_id = this->ike_sa->get_other_id(this->ike_sa);
        
        /* build the request */
        request = message_create();
@@ -295,9 +298,10 @@ static status_t get_request(private_create_child_sa_t *this, message_t **result)
                
                proposals = this->policy->get_proposals(this->policy);
                use_natt = this->ike_sa->is_natt_enabled(this->ike_sa);
-               this->child_sa = child_sa_create(this->reqid, me, other,
+               this->child_sa = child_sa_create(this->reqid, me, other, my_id, other_id,
                                                        this->policy->get_soft_lifetime(this->policy),
                                                        this->policy->get_hard_lifetime(this->policy),
+                                                       this->policy->get_updown(this->policy),
                                                        use_natt);
                this->child_sa->set_name(this->child_sa, this->policy->get_name(this->policy));
                if (this->child_sa->alloc(this->child_sa, proposals) != SUCCESS)
@@ -501,7 +505,9 @@ static status_t install_child_sa(private_create_child_sa_t *this, bool initiator
        {
                return DESTROY_ME;
        }
+       
        /* add to IKE_SA, and remove from transaction */
+       this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
        this->ike_sa->add_child_sa(this->ike_sa, this->child_sa);
        this->child_sa = NULL;
        return SUCCESS;
@@ -514,6 +520,7 @@ static status_t get_response(private_create_child_sa_t *this, message_t *request
                                                         message_t **result, transaction_t **next)
 {
        host_t *me, *other;
+       identification_t *my_id, *other_id;
        message_t *response;
        status_t status;
        iterator_t *payloads;
@@ -532,6 +539,8 @@ static status_t get_response(private_create_child_sa_t *this, message_t *request
        
        me = this->ike_sa->get_my_host(this->ike_sa);
        other = this->ike_sa->get_other_host(this->ike_sa);
+       my_id = this->ike_sa->get_my_id(this->ike_sa);
+       other_id = this->ike_sa->get_other_id(this->ike_sa);
        this->message_id = request->get_message_id(request);
        
        /* set up response */
@@ -705,8 +714,9 @@ static status_t get_response(private_create_child_sa_t *this, message_t *request
                        soft_lifetime = this->policy->get_soft_lifetime(this->policy);
                        hard_lifetime = this->policy->get_hard_lifetime(this->policy);
                        use_natt = this->ike_sa->is_natt_enabled(this->ike_sa);
-                       this->child_sa = child_sa_create(this->reqid, me, other,
+                       this->child_sa = child_sa_create(this->reqid, me, other, my_id, other_id,
                                                                                         soft_lifetime, hard_lifetime,
+                                                                                        this->policy->get_updown(this->policy),
                                                                                         use_natt);
                        this->child_sa->set_name(this->child_sa, this->policy->get_name(this->policy));
                        if (install_child_sa(this, FALSE) != SUCCESS)
index 0632fe4..c30dde2 100644 (file)
@@ -318,8 +318,9 @@ static status_t get_request(private_ike_auth_t *this, message_t **result)
                soft_lifetime = this->policy->get_soft_lifetime(this->policy);
                hard_lifetime = this->policy->get_hard_lifetime(this->policy);
                enable_natt = this->ike_sa->is_natt_enabled(this->ike_sa);
-               this->child_sa = child_sa_create(this->reqid, me, other, 
+               this->child_sa = child_sa_create(this->reqid, me, other, my_id, other_id,
                                                                                 soft_lifetime, hard_lifetime,
+                                                                                this->policy->get_updown(this->policy),
                                                                                 enable_natt);
                this->child_sa->set_name(this->child_sa, this->policy->get_name(this->policy));
                if (this->child_sa->alloc(this->child_sa, proposal_list) != SUCCESS)
@@ -519,7 +520,9 @@ static status_t install_child_sa(private_ike_auth_t *this, bool initiator)
        {
                return DESTROY_ME;
        }
+       
        /* add to IKE_SA, and remove from transaction */
+       this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
        this->ike_sa->add_child_sa(this->ike_sa, this->child_sa);
        this->child_sa = NULL;
        return SUCCESS;
@@ -784,8 +787,9 @@ static status_t get_response(private_ike_auth_t *this, message_t *request,
                        soft_lifetime = this->policy->get_soft_lifetime(this->policy);
                        hard_lifetime = this->policy->get_hard_lifetime(this->policy);
                        use_natt = this->ike_sa->is_natt_enabled(this->ike_sa);
-                       this->child_sa = child_sa_create(this->reqid, me, other, 
+                       this->child_sa = child_sa_create(this->reqid, me, other, my_id, other_id,
                                                                                         soft_lifetime, hard_lifetime, 
+                                                                                        this->policy->get_updown(this->policy),
                                                                                         use_natt);
                        this->child_sa->set_name(this->child_sa, this->policy->get_name(this->policy));
                        if (install_child_sa(this, FALSE) != SUCCESS)
index 3a39990..3c84415 100755 (executable)
@@ -222,7 +222,7 @@ static void stroke_add_conn(private_stroke_t *this, stroke_msg_t *msg)
                return;
        }
 
-       if (charon->socket->is_local_address(charon->socket, other_host))
+       if (charon->socket->is_local_address(charon->socket, other_host, NULL))
        {
                stroke_end_t tmp_end;
                host_t *tmp_host;
@@ -238,7 +238,7 @@ static void stroke_add_conn(private_stroke_t *this, stroke_msg_t *msg)
                msg->add_conn.me = msg->add_conn.other;
                msg->add_conn.other = tmp_end;
        }
-       else if (!charon->socket->is_local_address(charon->socket, my_host))
+       else if (!charon->socket->is_local_address(charon->socket, my_host, NULL))
        {
                this->stroke_logger->log(this->stroke_logger, ERROR, 
                                                                 "left nor right host is our side, aborting");