Changeset View
Standalone View
sys/dev/wg/if_wg.c
| Show First 20 Lines • Show All 939 Lines • ▼ Show 20 Lines | out: | ||||
| if (ret) | if (ret) | ||||
| DPRINTF(sc, "Unable to send packet: %d\n", ret); | DPRINTF(sc, "Unable to send packet: %d\n", ret); | ||||
| } | } | ||||
| /* Timers */ | /* Timers */ | ||||
| static void | static void | ||||
| wg_timers_enable(struct wg_peer *peer) | wg_timers_enable(struct wg_peer *peer) | ||||
| { | { | ||||
| ck_pr_store_bool(&peer->p_enabled, true); | peer->p_enabled = true; | ||||
| wg_timers_run_persistent_keepalive(peer); | wg_timers_run_persistent_keepalive(peer); | ||||
| } | } | ||||
| static void | static void | ||||
| wg_timers_disable(struct wg_peer *peer) | wg_timers_disable(struct wg_peer *peer) | ||||
| { | { | ||||
| /* By setting p_enabled = false, then calling NET_EPOCH_WAIT, we can be | /* By setting p_enabled = false, then calling NET_EPOCH_WAIT, we can be | ||||
| * sure no new handshakes are created after the wait. This is because | * sure no new handshakes are created after the wait. This is because | ||||
| * all callout_resets (scheduling the callout) are guarded by | * all callout_resets (scheduling the callout) are guarded by | ||||
| * p_enabled. We can be sure all sections that read p_enabled and then | * p_enabled. We can be sure all sections that read p_enabled and then | ||||
| * optionally call callout_reset are finished as they are surrounded by | * optionally call callout_reset are finished as they are surrounded by | ||||
| * NET_EPOCH_{ENTER,EXIT}. | * NET_EPOCH_{ENTER,EXIT}. | ||||
jhb: Hmm, I guess this comment is the reason for the NET_EPOCH around the callout_reset you asked… | |||||
markjUnsubmitted Not Done Inline ActionsOh. That's weird. I'm not familiar enough with these timers to say whether a mutex will be less efficient per se, but certainly contention could be a problem if the callout_pending() checks are done under a mutex. markj: Oh. That's weird. I'm not familiar enough with these timers to say whether a mutex will be less… | |||||
jhbAuthorUnsubmitted Done Inline ActionsWith the mutex you wouldn't need the callout_pending() calls at all. You would protect p_enabled with the mutex and could just use callout_stop under the lock in this function. Maybe I will mock up a version that uses callout_init_mtx and upload it for you to see how it looks instead. jhb: With the mutex you wouldn't need the callout_pending() calls at all. You would protect… | |||||
| * | * | ||||
| * However, as new callouts may be scheduled during NET_EPOCH_WAIT (but | * However, as new callouts may be scheduled during NET_EPOCH_WAIT (but | ||||
| * not after), we stop all callouts leaving no callouts active. | * not after), we stop all callouts leaving no callouts active. | ||||
| * | * | ||||
| * We should also pull NET_EPOCH_WAIT out of the FOREACH(peer) loops, but the | * We should also pull NET_EPOCH_WAIT out of the FOREACH(peer) loops, but the | ||||
| * performance impact is acceptable for the time being. */ | * performance impact is acceptable for the time being. */ | ||||
| ck_pr_store_bool(&peer->p_enabled, false); | peer->p_enabled = false; | ||||
| NET_EPOCH_WAIT(); | NET_EPOCH_WAIT(); | ||||
| ck_pr_store_bool(&peer->p_need_another_keepalive, false); | peer->p_need_another_keepalive = false; | ||||
| callout_stop(&peer->p_new_handshake); | callout_stop(&peer->p_new_handshake); | ||||
| callout_stop(&peer->p_send_keepalive); | callout_stop(&peer->p_send_keepalive); | ||||
| callout_stop(&peer->p_retry_handshake); | callout_stop(&peer->p_retry_handshake); | ||||
| callout_stop(&peer->p_persistent_keepalive); | callout_stop(&peer->p_persistent_keepalive); | ||||
| callout_stop(&peer->p_zero_key_material); | callout_stop(&peer->p_zero_key_material); | ||||
| } | } | ||||
| static void | static void | ||||
| wg_timers_set_persistent_keepalive(struct wg_peer *peer, uint16_t interval) | wg_timers_set_persistent_keepalive(struct wg_peer *peer, uint16_t interval) | ||||
| { | { | ||||
| struct epoch_tracker et; | struct epoch_tracker et; | ||||
| if (interval != peer->p_persistent_keepalive_interval) { | if (interval != peer->p_persistent_keepalive_interval) { | ||||
| ck_pr_store_16(&peer->p_persistent_keepalive_interval, interval); | peer->p_persistent_keepalive_interval = interval; | ||||
| NET_EPOCH_ENTER(et); | NET_EPOCH_ENTER(et); | ||||
| if (ck_pr_load_bool(&peer->p_enabled)) | if (peer->p_enabled) | ||||
| wg_timers_run_persistent_keepalive(peer); | wg_timers_run_persistent_keepalive(peer); | ||||
| NET_EPOCH_EXIT(et); | NET_EPOCH_EXIT(et); | ||||
| } | } | ||||
| } | } | ||||
| static void | static void | ||||
| wg_timers_get_last_handshake(struct wg_peer *peer, struct wg_timespec64 *time) | wg_timers_get_last_handshake(struct wg_peer *peer, struct wg_timespec64 *time) | ||||
| { | { | ||||
| mtx_lock(&peer->p_handshake_mtx); | mtx_lock(&peer->p_handshake_mtx); | ||||
| time->tv_sec = peer->p_handshake_complete.tv_sec; | time->tv_sec = peer->p_handshake_complete.tv_sec; | ||||
| time->tv_nsec = peer->p_handshake_complete.tv_nsec; | time->tv_nsec = peer->p_handshake_complete.tv_nsec; | ||||
| mtx_unlock(&peer->p_handshake_mtx); | mtx_unlock(&peer->p_handshake_mtx); | ||||
| } | } | ||||
| static void | static void | ||||
| wg_timers_event_data_sent(struct wg_peer *peer) | wg_timers_event_data_sent(struct wg_peer *peer) | ||||
| { | { | ||||
| struct epoch_tracker et; | struct epoch_tracker et; | ||||
| NET_EPOCH_ENTER(et); | NET_EPOCH_ENTER(et); | ||||
| if (ck_pr_load_bool(&peer->p_enabled) && !callout_pending(&peer->p_new_handshake)) | if (peer->p_enabled && !callout_pending(&peer->p_new_handshake)) | ||||
| callout_reset(&peer->p_new_handshake, MSEC_2_TICKS( | callout_reset(&peer->p_new_handshake, MSEC_2_TICKS( | ||||
| NEW_HANDSHAKE_TIMEOUT * 1000 + | NEW_HANDSHAKE_TIMEOUT * 1000 + | ||||
| arc4random_uniform(REKEY_TIMEOUT_JITTER)), | arc4random_uniform(REKEY_TIMEOUT_JITTER)), | ||||
| wg_timers_run_new_handshake, peer); | wg_timers_run_new_handshake, peer); | ||||
| NET_EPOCH_EXIT(et); | NET_EPOCH_EXIT(et); | ||||
| } | } | ||||
| static void | static void | ||||
| wg_timers_event_data_received(struct wg_peer *peer) | wg_timers_event_data_received(struct wg_peer *peer) | ||||
| { | { | ||||
| struct epoch_tracker et; | struct epoch_tracker et; | ||||
| NET_EPOCH_ENTER(et); | NET_EPOCH_ENTER(et); | ||||
| if (ck_pr_load_bool(&peer->p_enabled)) { | if (peer->p_enabled) { | ||||
| if (!callout_pending(&peer->p_send_keepalive)) | if (!callout_pending(&peer->p_send_keepalive)) | ||||
| callout_reset(&peer->p_send_keepalive, | callout_reset(&peer->p_send_keepalive, | ||||
| MSEC_2_TICKS(KEEPALIVE_TIMEOUT * 1000), | MSEC_2_TICKS(KEEPALIVE_TIMEOUT * 1000), | ||||
| wg_timers_run_send_keepalive, peer); | wg_timers_run_send_keepalive, peer); | ||||
| else | else | ||||
| ck_pr_store_bool(&peer->p_need_another_keepalive, true); | peer->p_need_another_keepalive = true; | ||||
| } | } | ||||
| NET_EPOCH_EXIT(et); | NET_EPOCH_EXIT(et); | ||||
| } | } | ||||
| static void | static void | ||||
| wg_timers_event_any_authenticated_packet_sent(struct wg_peer *peer) | wg_timers_event_any_authenticated_packet_sent(struct wg_peer *peer) | ||||
| { | { | ||||
| callout_stop(&peer->p_send_keepalive); | callout_stop(&peer->p_send_keepalive); | ||||
| } | } | ||||
| static void | static void | ||||
| wg_timers_event_any_authenticated_packet_received(struct wg_peer *peer) | wg_timers_event_any_authenticated_packet_received(struct wg_peer *peer) | ||||
| { | { | ||||
| callout_stop(&peer->p_new_handshake); | callout_stop(&peer->p_new_handshake); | ||||
| } | } | ||||
| static void | static void | ||||
| wg_timers_event_any_authenticated_packet_traversal(struct wg_peer *peer) | wg_timers_event_any_authenticated_packet_traversal(struct wg_peer *peer) | ||||
| { | { | ||||
| struct epoch_tracker et; | struct epoch_tracker et; | ||||
| uint16_t interval; | uint16_t interval; | ||||
| NET_EPOCH_ENTER(et); | NET_EPOCH_ENTER(et); | ||||
| interval = ck_pr_load_16(&peer->p_persistent_keepalive_interval); | interval = peer->p_persistent_keepalive_interval; | ||||
| if (ck_pr_load_bool(&peer->p_enabled) && interval > 0) | if (peer->p_enabled && interval > 0) | ||||
markjUnsubmitted Not Done Inline ActionsI think this is an example of a place where we want to use atomic_load: there's no synchronization with writers so the values being loaded are immediately stale. That's not a problem in itself but it should be made obvious to anyone reading the code. I wrote an implementation of plain atomic_load/store_bool(), will post it shortly. What exactly does the net epoch section do for us here? markj: I think this is an example of a place where we want to use atomic_load: there's no… | |||||
jhbAuthorUnsubmitted Done Inline ActionsI pointed to a comment above. It is abusing the epoch to serialize with the code that disables the peer. jhb: I pointed to a comment above. It is abusing the epoch to serialize with the code that disables… | |||||
markjUnsubmitted Not Done Inline ActionsOk. So I think the atomic_load/store_*s are not required for correctness, but would still serve as mildly helpful annotations. We do have atomic_load/store_bool in main now, BTW. markj: Ok. So I think the atomic_load/store_*s are not required for correctness, but would still serve… | |||||
| callout_reset(&peer->p_persistent_keepalive, | callout_reset(&peer->p_persistent_keepalive, | ||||
| MSEC_2_TICKS(interval * 1000), | MSEC_2_TICKS(interval * 1000), | ||||
| wg_timers_run_persistent_keepalive, peer); | wg_timers_run_persistent_keepalive, peer); | ||||
| NET_EPOCH_EXIT(et); | NET_EPOCH_EXIT(et); | ||||
| } | } | ||||
| static void | static void | ||||
| wg_timers_event_handshake_initiated(struct wg_peer *peer) | wg_timers_event_handshake_initiated(struct wg_peer *peer) | ||||
| { | { | ||||
| struct epoch_tracker et; | struct epoch_tracker et; | ||||
| NET_EPOCH_ENTER(et); | NET_EPOCH_ENTER(et); | ||||
| if (ck_pr_load_bool(&peer->p_enabled)) | if (peer->p_enabled) | ||||
| callout_reset(&peer->p_retry_handshake, MSEC_2_TICKS( | callout_reset(&peer->p_retry_handshake, MSEC_2_TICKS( | ||||
| REKEY_TIMEOUT * 1000 + | REKEY_TIMEOUT * 1000 + | ||||
| arc4random_uniform(REKEY_TIMEOUT_JITTER)), | arc4random_uniform(REKEY_TIMEOUT_JITTER)), | ||||
| wg_timers_run_retry_handshake, peer); | wg_timers_run_retry_handshake, peer); | ||||
| NET_EPOCH_EXIT(et); | NET_EPOCH_EXIT(et); | ||||
| } | } | ||||
| static void | static void | ||||
| wg_timers_event_handshake_complete(struct wg_peer *peer) | wg_timers_event_handshake_complete(struct wg_peer *peer) | ||||
| { | { | ||||
| struct epoch_tracker et; | struct epoch_tracker et; | ||||
| NET_EPOCH_ENTER(et); | NET_EPOCH_ENTER(et); | ||||
| if (ck_pr_load_bool(&peer->p_enabled)) { | if (peer->p_enabled) { | ||||
| mtx_lock(&peer->p_handshake_mtx); | mtx_lock(&peer->p_handshake_mtx); | ||||
| callout_stop(&peer->p_retry_handshake); | callout_stop(&peer->p_retry_handshake); | ||||
| peer->p_handshake_retries = 0; | peer->p_handshake_retries = 0; | ||||
| getnanotime(&peer->p_handshake_complete); | getnanotime(&peer->p_handshake_complete); | ||||
| mtx_unlock(&peer->p_handshake_mtx); | mtx_unlock(&peer->p_handshake_mtx); | ||||
| wg_timers_run_send_keepalive(peer); | wg_timers_run_send_keepalive(peer); | ||||
| } | } | ||||
| NET_EPOCH_EXIT(et); | NET_EPOCH_EXIT(et); | ||||
| } | } | ||||
| static void | static void | ||||
| wg_timers_event_session_derived(struct wg_peer *peer) | wg_timers_event_session_derived(struct wg_peer *peer) | ||||
| { | { | ||||
| struct epoch_tracker et; | struct epoch_tracker et; | ||||
| NET_EPOCH_ENTER(et); | NET_EPOCH_ENTER(et); | ||||
| if (ck_pr_load_bool(&peer->p_enabled)) | if (peer->p_enabled) | ||||
| callout_reset(&peer->p_zero_key_material, | callout_reset(&peer->p_zero_key_material, | ||||
| MSEC_2_TICKS(REJECT_AFTER_TIME * 3 * 1000), | MSEC_2_TICKS(REJECT_AFTER_TIME * 3 * 1000), | ||||
| wg_timers_run_zero_key_material, peer); | wg_timers_run_zero_key_material, peer); | ||||
| NET_EPOCH_EXIT(et); | NET_EPOCH_EXIT(et); | ||||
| } | } | ||||
| static void | static void | ||||
| wg_timers_event_want_initiation(struct wg_peer *peer) | wg_timers_event_want_initiation(struct wg_peer *peer) | ||||
| { | { | ||||
| struct epoch_tracker et; | struct epoch_tracker et; | ||||
| NET_EPOCH_ENTER(et); | NET_EPOCH_ENTER(et); | ||||
| if (ck_pr_load_bool(&peer->p_enabled)) | if (peer->p_enabled) | ||||
| wg_timers_run_send_initiation(peer, false); | wg_timers_run_send_initiation(peer, false); | ||||
| NET_EPOCH_EXIT(et); | NET_EPOCH_EXIT(et); | ||||
| } | } | ||||
| static void | static void | ||||
| wg_timers_run_send_initiation(struct wg_peer *peer, bool is_retry) | wg_timers_run_send_initiation(struct wg_peer *peer, bool is_retry) | ||||
| { | { | ||||
| if (!is_retry) | if (!is_retry) | ||||
| Show All 23 Lines | if (peer->p_handshake_retries <= MAX_TIMER_HANDSHAKES) { | ||||
| DPRINTF(peer->p_sc, "Handshake for peer %" PRIu64 " did not complete " | DPRINTF(peer->p_sc, "Handshake for peer %" PRIu64 " did not complete " | ||||
| "after %d retries, giving up\n", peer->p_id, | "after %d retries, giving up\n", peer->p_id, | ||||
| MAX_TIMER_HANDSHAKES + 2); | MAX_TIMER_HANDSHAKES + 2); | ||||
| callout_stop(&peer->p_send_keepalive); | callout_stop(&peer->p_send_keepalive); | ||||
| wg_queue_purge(&peer->p_stage_queue); | wg_queue_purge(&peer->p_stage_queue); | ||||
| NET_EPOCH_ENTER(et); | NET_EPOCH_ENTER(et); | ||||
| if (ck_pr_load_bool(&peer->p_enabled) && | if (peer->p_enabled && | ||||
| !callout_pending(&peer->p_zero_key_material)) | !callout_pending(&peer->p_zero_key_material)) | ||||
| callout_reset(&peer->p_zero_key_material, | callout_reset(&peer->p_zero_key_material, | ||||
| MSEC_2_TICKS(REJECT_AFTER_TIME * 3 * 1000), | MSEC_2_TICKS(REJECT_AFTER_TIME * 3 * 1000), | ||||
| wg_timers_run_zero_key_material, peer); | wg_timers_run_zero_key_material, peer); | ||||
| NET_EPOCH_EXIT(et); | NET_EPOCH_EXIT(et); | ||||
| } | } | ||||
| } | } | ||||
| static void | static void | ||||
| wg_timers_run_send_keepalive(void *_peer) | wg_timers_run_send_keepalive(void *_peer) | ||||
| { | { | ||||
| struct epoch_tracker et; | struct epoch_tracker et; | ||||
| struct wg_peer *peer = _peer; | struct wg_peer *peer = _peer; | ||||
| wg_send_keepalive(peer); | wg_send_keepalive(peer); | ||||
| NET_EPOCH_ENTER(et); | NET_EPOCH_ENTER(et); | ||||
| if (ck_pr_load_bool(&peer->p_enabled) && | if (peer->p_enabled && peer->p_need_another_keepalive) { | ||||
| ck_pr_load_bool(&peer->p_need_another_keepalive)) { | peer->p_need_another_keepalive = false; | ||||
markjUnsubmitted Not Done Inline ActionsHere's a situation where atomics would be helpful. There is zero synchronization between writers and readers of p_need_another_keepalive. markj: Here's a situation where atomics would be helpful. There is zero synchronization between… | |||||
| ck_pr_store_bool(&peer->p_need_another_keepalive, false); | |||||
| callout_reset(&peer->p_send_keepalive, | callout_reset(&peer->p_send_keepalive, | ||||
| MSEC_2_TICKS(KEEPALIVE_TIMEOUT * 1000), | MSEC_2_TICKS(KEEPALIVE_TIMEOUT * 1000), | ||||
| wg_timers_run_send_keepalive, peer); | wg_timers_run_send_keepalive, peer); | ||||
| } | } | ||||
| NET_EPOCH_EXIT(et); | NET_EPOCH_EXIT(et); | ||||
| } | } | ||||
| static void | static void | ||||
| Show All 20 Lines | wg_timers_run_zero_key_material(void *_peer) | ||||
| noise_remote_keypairs_clear(peer->p_remote); | noise_remote_keypairs_clear(peer->p_remote); | ||||
| } | } | ||||
| static void | static void | ||||
| wg_timers_run_persistent_keepalive(void *_peer) | wg_timers_run_persistent_keepalive(void *_peer) | ||||
| { | { | ||||
| struct wg_peer *peer = _peer; | struct wg_peer *peer = _peer; | ||||
| if (ck_pr_load_16(&peer->p_persistent_keepalive_interval) > 0) | if (peer->p_persistent_keepalive_interval > 0) | ||||
| wg_send_keepalive(peer); | wg_send_keepalive(peer); | ||||
| } | } | ||||
| /* TODO Handshake */ | /* TODO Handshake */ | ||||
| static void | static void | ||||
| wg_peer_send_buf(struct wg_peer *peer, uint8_t *buf, size_t len) | wg_peer_send_buf(struct wg_peer *peer, uint8_t *buf, size_t len) | ||||
| { | { | ||||
| struct wg_endpoint endpoint; | struct wg_endpoint endpoint; | ||||
| ▲ Show 20 Lines • Show All 1,878 Lines • Show Last 20 Lines | |||||
Hmm, I guess this comment is the reason for the NET_EPOCH around the callout_reset you asked about. It is not clear to me that a per-peer mutex used for managing callouts would be significantly less efficient? It would be easier to understand I think.