]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
Copy ESN enabled replay state during update_sa, if supported
authorMartin Willi <martin@revosec.ch>
Mon, 18 Apr 2011 15:57:59 +0000 (17:57 +0200)
committerMartin Willi <martin@revosec.ch>
Wed, 20 Apr 2011 10:26:58 +0000 (12:26 +0200)
src/libhydra/plugins/kernel_netlink/kernel_netlink_ipsec.c

index 10d53db6f59f5f8f1dd6ca646ecc5c8c3474bd87..4bf30fa32efa9de0385204c500d3acea121b56e4 100644 (file)
@@ -353,9 +353,14 @@ struct private_kernel_netlink_ipsec_t {
        bool install_routes;
 
        /**
-        * Size of the replay window
+        * Size of the replay window, in packets
         */
        u_int32_t replay_window;
+
+       /**
+        * Size of the replay window bitmap, in bytes
+        */
+       u_int32_t replay_bmp;
 };
 
 /**
@@ -1199,8 +1204,7 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
 
                        replay = (struct xfrm_replay_state_esn*)RTA_DATA(rthdr);
                        /* bmp_len contains number uf __u32's */
-                       replay->bmp_len = (this->replay_window + sizeof(u_int32_t) * 8 - 1)
-                                                               / (sizeof(u_int32_t) * 8);
+                       replay->bmp_len = this->replay_bmp;
                        replay->replay_window = this->replay_window;
 
                        rthdr = XFRM_RTA_NEXT(rthdr);
@@ -1232,11 +1236,14 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
 }
 
 /**
- * Get the replay state (i.e. sequence numbers) of an SA.
+ * Get the ESN replay state (i.e. sequence numbers) of an SA.
+ *
+ * Allocates into one the replay state structure we get from the kernel.
  */
-static status_t get_replay_state(private_kernel_netlink_ipsec_t *this,
-                                                 u_int32_t spi, u_int8_t protocol, host_t *dst,
-                                                 struct xfrm_replay_state *replay)
+static void get_replay_state(private_kernel_netlink_ipsec_t *this,
+                                                        u_int32_t spi, u_int8_t protocol, host_t *dst,
+                                                        struct xfrm_replay_state_esn **replay_esn,
+                                                        struct xfrm_replay_state **replay)
 {
        netlink_buf_t request;
        struct nlmsghdr *hdr, *out = NULL;
@@ -1247,7 +1254,8 @@ static status_t get_replay_state(private_kernel_netlink_ipsec_t *this,
 
        memset(&request, 0, sizeof(request));
 
-       DBG2(DBG_KNL, "querying replay state from SAD entry with SPI %.8x", ntohl(spi));
+       DBG2(DBG_KNL, "querying replay state from SAD entry with SPI %.8x",
+                ntohl(spi));
 
        hdr = (struct nlmsghdr*)request;
        hdr->nlmsg_flags = NLM_F_REQUEST;
@@ -1291,32 +1299,30 @@ static status_t get_replay_state(private_kernel_netlink_ipsec_t *this,
                }
        }
 
-       if (out_aevent == NULL)
-       {
-               DBG1(DBG_KNL, "unable to query replay state from SAD entry with SPI %.8x",
-                                         ntohl(spi));
-               free(out);
-               return FAILED;
-       }
-
-       rta = XFRM_RTA(out, struct xfrm_aevent_id);
-       rtasize = XFRM_PAYLOAD(out, struct xfrm_aevent_id);
-       while(RTA_OK(rta, rtasize))
+       if (out_aevent)
        {
-               if (rta->rta_type == XFRMA_REPLAY_VAL &&
-                       RTA_PAYLOAD(rta) == sizeof(struct xfrm_replay_state))
+               rta = XFRM_RTA(out, struct xfrm_aevent_id);
+               rtasize = XFRM_PAYLOAD(out, struct xfrm_aevent_id);
+               while (RTA_OK(rta, rtasize))
                {
-                       memcpy(replay, RTA_DATA(rta), RTA_PAYLOAD(rta));
-                       free(out);
-                       return SUCCESS;
+                       if (rta->rta_type == XFRMA_REPLAY_VAL &&
+                               RTA_PAYLOAD(rta) == sizeof(**replay))
+                       {
+                               *replay = malloc(RTA_PAYLOAD(rta));
+                               memcpy(*replay, RTA_DATA(rta), RTA_PAYLOAD(rta));
+                               break;
+                       }
+                       if (rta->rta_type == XFRMA_REPLAY_ESN_VAL &&
+                               RTA_PAYLOAD(rta) >= sizeof(**replay_esn) + this->replay_bmp)
+                       {
+                               *replay_esn = malloc(RTA_PAYLOAD(rta));
+                               memcpy(*replay_esn, RTA_DATA(rta), RTA_PAYLOAD(rta));
+                               break;
+                       }
+                       rta = RTA_NEXT(rta, rtasize);
                }
-               rta = RTA_NEXT(rta, rtasize);
        }
-
-       DBG1(DBG_KNL, "unable to query replay state from SAD entry with SPI %.8x",
-                                 ntohl(spi));
        free(out);
-       return FAILED;
 }
 
 METHOD(kernel_ipsec_t, query_sa, status_t,
@@ -1515,8 +1521,9 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
        struct rtattr *rta;
        size_t rtasize;
        struct xfrm_encap_tmpl* tmpl = NULL;
-       bool got_replay_state = FALSE;
-       struct xfrm_replay_state replay;
+       struct xfrm_replay_state *replay = NULL;
+       struct xfrm_replay_state_esn *replay_esn = NULL;
+       status_t status = FAILED;
 
        /* if IPComp is used, we first update the IPComp SA */
        if (cpi)
@@ -1572,22 +1579,16 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
        if (out_sa == NULL)
        {
                DBG1(DBG_KNL, "unable to update SAD entry with SPI %.8x", ntohl(spi));
-               free(out);
-               return FAILED;
+               goto failed;
        }
 
-       /* try to get the replay state */
-       if (get_replay_state(this, spi, protocol, dst, &replay) == SUCCESS)
-       {
-               got_replay_state = TRUE;
-       }
+       get_replay_state(this, spi, protocol, dst, &replay_esn, &replay);
 
        /* delete the old SA (without affecting the IPComp SA) */
        if (del_sa(this, src, dst, spi, protocol, 0, mark) != SUCCESS)
        {
                DBG1(DBG_KNL, "unable to delete old SAD entry with SPI %.8x", ntohl(spi));
-               free(out);
-               return FAILED;
+               goto failed;
        }
 
        DBG2(DBG_KNL, "updating SAD entry with SPI %.8x from %#H..%#H to %#H..%#H",
@@ -1640,7 +1641,7 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
                hdr->nlmsg_len += RTA_ALIGN(rta->rta_len);
                if (hdr->nlmsg_len > sizeof(request))
                {
-                       return FAILED;
+                       goto failed;
                }
 
                tmpl = (struct xfrm_encap_tmpl*)RTA_DATA(rta);
@@ -1652,30 +1653,55 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
                rta = XFRM_RTA_NEXT(rta);
        }
 
-       if (got_replay_state)
-       {       /* copy the replay data if available */
+       if (replay_esn)
+       {
+               rta->rta_type = XFRMA_REPLAY_ESN_VAL;
+               rta->rta_len = RTA_LENGTH(sizeof(struct xfrm_replay_state_esn) +
+                                                                 this->replay_bmp);
+
+               hdr->nlmsg_len += RTA_ALIGN(rta->rta_len);
+               if (hdr->nlmsg_len > sizeof(request))
+               {
+                       goto failed;
+               }
+               memcpy(RTA_DATA(rta), replay_esn,
+                          sizeof(struct xfrm_replay_state_esn) + this->replay_bmp);
+
+               rta = XFRM_RTA_NEXT(rta);
+       }
+       else if (replay)
+       {
                rta->rta_type = XFRMA_REPLAY_VAL;
                rta->rta_len = RTA_LENGTH(sizeof(struct xfrm_replay_state));
 
                hdr->nlmsg_len += RTA_ALIGN(rta->rta_len);
                if (hdr->nlmsg_len > sizeof(request))
                {
-                       return FAILED;
+                       goto failed;
                }
-               memcpy(RTA_DATA(rta), &replay, sizeof(replay));
+               memcpy(RTA_DATA(rta), replay, sizeof(replay));
 
                rta = XFRM_RTA_NEXT(rta);
        }
+       else
+       {
+               DBG1(DBG_KNL, "unable to copy replay state from old SAD entry "
+                        "with SPI %.8x", ntohl(spi));
+       }
 
        if (this->socket_xfrm->send_ack(this->socket_xfrm, hdr) != SUCCESS)
        {
                DBG1(DBG_KNL, "unable to update SAD entry with SPI %.8x", ntohl(spi));
-               free(out);
-               return FAILED;
+               goto failed;
        }
+
+       status = SUCCESS;
+failed:
+       free(replay);
+       free(replay_esn);
        free(out);
 
-       return SUCCESS;
+       return status;
 }
 
 METHOD(kernel_ipsec_t, add_policy, status_t,
@@ -2243,6 +2269,9 @@ kernel_netlink_ipsec_t *kernel_netlink_ipsec_create()
                                        "%s.replay_window", DEFAULT_REPLAY_WINDOW, hydra->daemon),
        );
 
+       this->replay_bmp = (this->replay_window + sizeof(u_int32_t) * 8 - 1) /
+                                                                                                       (sizeof(u_int32_t) * 8);
+
        if (streq(hydra->daemon, "pluto"))
        {       /* no routes for pluto, they are installed via updown script */
                this->install_routes = FALSE;