summaryrefslogtreecommitdiff
path: root/cesar/bsu/src
diff options
context:
space:
mode:
authorYacine Belkadi2013-04-03 13:11:05 +0200
committerYacine Belkadi2013-05-23 09:07:13 +0200
commite4b7731e1827efcc1af0a721cb5415039ae6f459 (patch)
tree7fb89129008aaca5551f888a90fb10ad49f7e74c /cesar/bsu/src
parenta5821e5b218bb56400f0ef888a47ad9651484df2 (diff)
cesar/{bsu,cp,mac/pbproc}: change NEK management
Two slots were used to store the NEKs. One for the current NEK, the other for the next NEK. The CP could update any slot at any time, by setting the EKS to MAC_EKS_CLEAR first. One problem is that the pbproc doesn't expect someone to change a key that it is in use. If the cp sets the EKS to MAC_EKS_CLEAR for a key that is in use, may make the pbproc send data unencrypted, which is a problem. Another problem may occur during a NEK change, if the new NEK is not received via the SET_KEY_CNF. In that case, when the countdown starts, the cp sends a GET_KEY_CNF to get the new NEK. When it receives it, it puts the new key in the next slot. To identify the next slot the cp uses bsu_nek_index_next(). This is not reliable around a NEK change: - the cp receives the new NEK - the cp starts setting the new NEK - the cp is suspended - the bsu changes nek_switch - the cp is resumed and calls bsu_nek_index_next() which returns the index of the current NEK instead of the next NEK - the cp writes the new NEK in the current slot instead of the next slot - the pbproc may send packets unencrypted - the NEK change occurs - the new NEK is used in Rx (because the new NEK is in one of the two slots) but not in Tx (because it's not the in the current slot) To fix that, change the way the NEKs are managed: Use pointers and indirection to be able to atomically change a NEK transparently from the pbproc's point of view. Make the cp store the NEKs and set the current NEK, but let the bsu grab the next NEK if it's available.
Diffstat (limited to 'cesar/bsu/src')
-rw-r--r--cesar/bsu/src/bsu.c128
1 files changed, 107 insertions, 21 deletions
diff --git a/cesar/bsu/src/bsu.c b/cesar/bsu/src/bsu.c
index 1a0bb1273a..f5449ad3aa 100644
--- a/cesar/bsu/src/bsu.c
+++ b/cesar/bsu/src/bsu.c
@@ -688,12 +688,43 @@ bsu_handle_key_change (bsu_t *ctx)
{
dbg_assert (ctx);
bsu_avln_t *avln = ctx->sta_avln;
+ mac_nek_mgr_t *nek_mgr = &ctx->mac_config->nek_mgr;
if (avln->beacon.bmis.eks.present)
{
- if ((avln->beacon.bmis.eks.kbc == BSU_BEACON_EKS_KBC_NEK)
- && (avln->beacon.bmis.eks.kccd == 1))
- ctx->nek_switch = !ctx->nek_switch;
+ if (avln->beacon.bmis.eks.kbc == BSU_BEACON_EKS_KBC_NEK)
+ {
+ /* Do we have the new NEK? */
+ if (nek_mgr->next_nek
+ && (nek_mgr->next_nek->eks == avln->beacon.bmis.eks.new_eks))
+ {
+ /* We have it. Set it as next. */
+ mac_nek_t *old_next_nek = nek_mgr->use[!ctx->nek_switch];
+ /* No need to set it, if it's already set. */
+ if (nek_mgr->next_nek != old_next_nek)
+ {
+ nek_mgr->next_nek->in_use = true;
+ arch_reorder_barrier ();
+ nek_mgr->use[!ctx->nek_switch] = nek_mgr->next_nek;
+ arch_reorder_barrier ();
+ if (old_next_nek != nek_mgr->use[ctx->nek_switch])
+ old_next_nek->in_use = false;
+ }
+ }
+ /* else:
+ * The next NEK is not available yet. So either:
+ * - we currently have the current NEK set in the next slot too.
+ * Because that's what bsu_nek_use() does.
+ * - or, we have previously set a NEK in the next slot, but in the
+ * meantime we received an MME with an EKS different from
+ * new_eks. (Seems unlikely, but...).
+ * In both cases, there will be a NEK in the next slot, so the
+ * pbproc will find a NEK to use.
+ */
+
+ if (avln->beacon.bmis.eks.kccd == 1)
+ ctx->nek_switch = !ctx->nek_switch;
+ }
/* Prepare for next beacon period, in case we don't get the beacon. */
if (avln->beacon.bmis.eks.kccd > 1)
@@ -710,10 +741,19 @@ bsu_handle_key_change (bsu_t *ctx)
/* On the previous run of this function, we changed nek_switch.
* This means that a NEK change occured at the start of the current
* beacon period. The new NEK (if we had it) should now be in use,
- * and we can remove the old NEK. */
- ctx->mac_config->nek[!ctx->nek_switch].eks = MAC_EKS_CLEAR;
-
- ctx->prev_nek_switch = ctx->nek_switch;
+ * and we can remove the old NEK.
+ * But first: did we have the new NEK? */
+ if (nek_mgr->use[ctx->nek_switch]
+ != nek_mgr->use[!ctx->nek_switch])
+ {
+ mac_nek_t *prev_nek = nek_mgr->use[!ctx->nek_switch];
+ nek_mgr->use[!ctx->nek_switch] = nek_mgr->use[ctx->nek_switch];
+ /* Use a barrier to be sure that the NEK is not used anymore by
+ * the pbproc when we mark it as not used. */
+ arch_reorder_barrier ();
+ prev_nek->in_use = false;
+ }
+ ctx->prev_nek_switch = ctx->nek_switch;
}
}
}
@@ -898,6 +938,66 @@ bsu_timer_event_process__cco (bsu_t *ctx)
}
}
+mac_nek_t *
+bsu_nek_get_current (const bsu_t *ctx)
+{
+ dbg_assert (ctx);
+ return ctx->mac_config->nek_mgr.use[ctx->nek_switch];
+}
+
+void
+bsu_nek_set_next (bsu_t *ctx, mac_nek_t *nek)
+{
+ dbg_assert (ctx);
+ dbg_assert (nek);
+ ctx->mac_config->nek_mgr.next_nek = nek;
+}
+
+void
+bsu_nek_use (bsu_t *ctx, mac_nek_t *nek)
+{
+ dbg_assert (ctx);
+ dbg_assert (nek);
+
+ /* To start using a NEK, both the next and the current NEK slots are
+ * changed. The next NEK is changed for two reasons:
+ * - In case there is a programmed NEK change, not updating the next NEK
+ * will render the call to this function useless, because the passed NEK
+ * won't be used after the NEK change.
+ * - When using one NEK, the choice has been made to put it in both slots.
+ * This way, the pbproc easily finds the NEK in Rx.
+ */
+ mac_nek_t *old_current;
+ mac_nek_t *old_next;
+ mac_nek_mgr_t *nek_mgr = &ctx->mac_config->nek_mgr;
+
+ /* Protect ourself from the bsu running in dsr context. */
+ arch_dsr_lock ();
+
+ /* Suppose that a NEK change is about to happen on the next beacon period.
+ * Let n1 be the current NEK. n2 the next NEK. n3 the NEK passed to this
+ * function.
+ * - The current NEK slot is set to n3. So the pbproc starts using it.
+ * - New beacon period, so the pbproc starts using n2.
+ * - The next NEK slot is set to n3. So the pbproc starts using n3.
+ * So, the pbproc used n3, then n2, then n3. To prevent the temporary use of
+ * n2, the next NEK slot is updated first, then the current NEK slot.
+ */
+ old_next = nek_mgr->use[!ctx->nek_switch];
+ old_current = nek_mgr->use[ctx->nek_switch];
+ nek->in_use = true;
+ arch_reorder_barrier ();
+ nek_mgr->use[!ctx->nek_switch] = nek;
+ arch_reorder_barrier ();
+ nek_mgr->use[ctx->nek_switch] = nek;
+ arch_reorder_barrier ();
+
+ arch_dsr_unlock ();
+
+ old_next->in_use = false;
+ old_current->in_use = false;
+}
+
/**
* BSU timer expires.
* \param ud the module context.
@@ -1424,20 +1524,6 @@ bsu_activate (bsu_t *ctx, bool status)
arch_dsr_unlock ();
}
-uint
-bsu_nek_index_current (bsu_t *ctx)
-{
- dbg_assert (ctx);
- return ctx->nek_switch;
-}
-
-uint
-bsu_nek_index_next (bsu_t *ctx)
-{
- dbg_assert (ctx);
- return !ctx->nek_switch;
-}
-
void
bsu_update_discover_info (
bsu_t *ctx, bsu_beacon_bmi_discover_info_t *discover)