/* NHRP virtual connection * Copyright (c) 2014-2015 Timo Teräs * * This file is free software: you may copy, redistribute and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. */ #include "zebra.h" #include "memory.h" #include "stream.h" #include "hash.h" #include "thread.h" #include "jhash.h" #include "nhrpd.h" #include "os.h" struct child_sa { uint32_t id; struct nhrp_vc *vc; struct list_head childlist_entry; }; static struct hash *nhrp_vc_hash; static struct list_head childlist_head[512]; static unsigned int nhrp_vc_key(void *peer_data) { struct nhrp_vc *vc = peer_data; return jhash_2words( sockunion_hash(&vc->local.nbma), sockunion_hash(&vc->remote.nbma), 0); } static int nhrp_vc_cmp(const void *cache_data, const void *key_data) { const struct nhrp_vc *a = cache_data; const struct nhrp_vc *b = key_data; return sockunion_same(&a->local.nbma, &b->local.nbma) && sockunion_same(&a->remote.nbma, &b->remote.nbma); } static void *nhrp_vc_alloc(void *data) { struct nhrp_vc *vc, *key = data; vc = XMALLOC(MTYPE_NHRP_VC, sizeof(struct nhrp_vc)); if (vc) { *vc = (struct nhrp_vc) { .local.nbma = key->local.nbma, .remote.nbma = key->remote.nbma, .notifier_list = NOTIFIER_LIST_INITIALIZER(&vc->notifier_list), }; } return vc; } static void nhrp_vc_free(void *data) { XFREE(MTYPE_NHRP_VC, data); } struct nhrp_vc *nhrp_vc_get(const union sockunion *src, const union sockunion *dst, int create) { struct nhrp_vc key; key.local.nbma = *src; key.remote.nbma = *dst; return hash_get(nhrp_vc_hash, &key, create ? nhrp_vc_alloc : 0); } static void nhrp_vc_check_delete(struct nhrp_vc *vc) { if (vc->updating || vc->ipsec || notifier_active(&vc->notifier_list)) return; hash_release(nhrp_vc_hash, vc); nhrp_vc_free(vc); } static void nhrp_vc_update(struct nhrp_vc *vc, long cmd) { vc->updating = 1; notifier_call(&vc->notifier_list, cmd); vc->updating = 0; nhrp_vc_check_delete(vc); } static void nhrp_vc_ipsec_reset(struct nhrp_vc *vc) { vc->local.id[0] = 0; vc->local.certlen = 0; vc->remote.id[0] = 0; vc->remote.certlen = 0; } int nhrp_vc_ipsec_updown(uint32_t child_id, struct nhrp_vc *vc) { char buf[2][SU_ADDRSTRLEN]; struct child_sa *sa = NULL, *lsa; uint32_t child_hash = child_id % ZEBRA_NUM_OF(childlist_head); int abort_migration = 0; list_for_each_entry(lsa, &childlist_head[child_hash], childlist_entry) { if (lsa->id == child_id) { sa = lsa; break; } } if (!sa) { if (!vc) return 0; sa = XMALLOC(MTYPE_NHRP_VC, sizeof(struct child_sa)); if (!sa) return 0; *sa = (struct child_sa) { .id = child_id, .childlist_entry = LIST_INITIALIZER(sa->childlist_entry), .vc = NULL, }; list_add_tail(&sa->childlist_entry, &childlist_head[child_hash]); } if (sa->vc == vc) return 0; if (vc) { /* Attach first to new VC */ vc->ipsec++; nhrp_vc_update(vc, NOTIFY_VC_IPSEC_CHANGED); } if (sa->vc && vc) { /* Notify old VC of migration */ sa->vc->abort_migration = 0; debugf(NHRP_DEBUG_COMMON, "IPsec NBMA change of %s to %s", sockunion2str(&sa->vc->remote.nbma, buf[0], sizeof buf[0]), sockunion2str(&vc->remote.nbma, buf[1], sizeof buf[1])); nhrp_vc_update(sa->vc, NOTIFY_VC_IPSEC_UPDATE_NBMA); abort_migration = sa->vc->abort_migration; } if (sa->vc) { /* Deattach old VC */ sa->vc->ipsec--; if (!sa->vc->ipsec) nhrp_vc_ipsec_reset(sa->vc); nhrp_vc_update(sa->vc, NOTIFY_VC_IPSEC_CHANGED); } /* Update */ sa->vc = vc; if (!vc) { list_del(&sa->childlist_entry); XFREE(MTYPE_NHRP_VC, sa); } return abort_migration; } void nhrp_vc_notify_add(struct nhrp_vc *vc, struct notifier_block *n, notifier_fn_t action) { notifier_add(n, &vc->notifier_list, action); } void nhrp_vc_notify_del(struct nhrp_vc *vc, struct notifier_block *n) { notifier_del(n); nhrp_vc_check_delete(vc); } struct nhrp_vc_iterator_ctx { void (*cb)(struct nhrp_vc *, void *); void *ctx; }; static void nhrp_vc_iterator(struct hash_backet *b, void *ctx) { struct nhrp_vc_iterator_ctx *ic = ctx; ic->cb(b->data, ic->ctx); } void nhrp_vc_foreach(void (*cb)(struct nhrp_vc *, void *), void *ctx) { struct nhrp_vc_iterator_ctx ic = { .cb = cb, .ctx = ctx, }; hash_iterate(nhrp_vc_hash, nhrp_vc_iterator, &ic); } void nhrp_vc_init(void) { size_t i; nhrp_vc_hash = hash_create(nhrp_vc_key, nhrp_vc_cmp); for (i = 0; i < ZEBRA_NUM_OF(childlist_head); i++) list_init(&childlist_head[i]); } void nhrp_vc_reset(void) { struct child_sa *sa, *n; size_t i; for (i = 0; i < ZEBRA_NUM_OF(childlist_head); i++) { list_for_each_entry_safe(sa, n, &childlist_head[i], childlist_entry) nhrp_vc_ipsec_updown(sa->id, 0); } } void nhrp_vc_terminate(void) { nhrp_vc_reset(); hash_clean(nhrp_vc_hash, nhrp_vc_free); }