Merge tag 'upstream/1.2.4'
[quagga-debian.git] / nhrpd / nhrp_vc.c
1 /* NHRP virtual connection
2  * Copyright (c) 2014-2015 Timo Teräs
3  *
4  * This file is free software: you may copy, redistribute and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  */
9
10 #include "zebra.h"
11 #include "memory.h"
12 #include "stream.h"
13 #include "hash.h"
14 #include "thread.h"
15 #include "jhash.h"
16
17 #include "nhrpd.h"
18 #include "os.h"
19
20 struct child_sa {
21         uint32_t id;
22         struct nhrp_vc *vc;
23         struct list_head childlist_entry;
24 };
25
26 static struct hash *nhrp_vc_hash;
27 static struct list_head childlist_head[512];
28
29 static unsigned int nhrp_vc_key(void *peer_data)
30 {
31         struct nhrp_vc *vc = peer_data;
32         return jhash_2words(
33                 sockunion_hash(&vc->local.nbma),
34                 sockunion_hash(&vc->remote.nbma),
35                 0);
36 }
37
38 static int nhrp_vc_cmp(const void *cache_data, const void *key_data)
39 {
40         const struct nhrp_vc *a = cache_data;
41         const struct nhrp_vc *b = key_data;
42         return  sockunion_same(&a->local.nbma, &b->local.nbma) &&
43                 sockunion_same(&a->remote.nbma, &b->remote.nbma);
44 }
45
46 static void *nhrp_vc_alloc(void *data)
47 {
48         struct nhrp_vc *vc, *key = data;
49
50         vc = XMALLOC(MTYPE_NHRP_VC, sizeof(struct nhrp_vc));
51         if (vc) {
52                 *vc = (struct nhrp_vc) {
53                         .local.nbma = key->local.nbma,
54                         .remote.nbma = key->remote.nbma,
55                         .notifier_list = NOTIFIER_LIST_INITIALIZER(&vc->notifier_list),
56                 };
57         }
58
59         return vc;
60 }
61
62 static void nhrp_vc_free(void *data)
63 {
64         XFREE(MTYPE_NHRP_VC, data);
65 }
66
67 struct nhrp_vc *nhrp_vc_get(const union sockunion *src, const union sockunion *dst, int create)
68 {
69         struct nhrp_vc key;
70         key.local.nbma = *src;
71         key.remote.nbma = *dst;
72         return hash_get(nhrp_vc_hash, &key, create ? nhrp_vc_alloc : 0);
73 }
74
75 static void nhrp_vc_check_delete(struct nhrp_vc *vc)
76 {
77         if (vc->updating || vc->ipsec || notifier_active(&vc->notifier_list))
78                 return;
79         hash_release(nhrp_vc_hash, vc);
80         nhrp_vc_free(vc);
81 }
82
83 static void nhrp_vc_update(struct nhrp_vc *vc, long cmd)
84 {
85         vc->updating = 1;
86         notifier_call(&vc->notifier_list, cmd);
87         vc->updating = 0;
88         nhrp_vc_check_delete(vc);
89 }
90
91 static void nhrp_vc_ipsec_reset(struct nhrp_vc *vc)
92 {
93         vc->local.id[0] = 0;
94         vc->local.certlen = 0;
95         vc->remote.id[0] = 0;
96         vc->remote.certlen = 0;
97 }
98
99 int nhrp_vc_ipsec_updown(uint32_t child_id, struct nhrp_vc *vc)
100 {
101         char buf[2][SU_ADDRSTRLEN];
102         struct child_sa *sa = NULL, *lsa;
103         uint32_t child_hash = child_id % ZEBRA_NUM_OF(childlist_head);
104         int abort_migration = 0;
105
106         list_for_each_entry(lsa, &childlist_head[child_hash], childlist_entry) {
107                 if (lsa->id == child_id) {
108                         sa = lsa;
109                         break;
110                 }
111         }
112
113         if (!sa) {
114                 if (!vc) return 0;
115
116                 sa = XMALLOC(MTYPE_NHRP_VC, sizeof(struct child_sa));
117                 if (!sa) return 0;
118
119                 *sa = (struct child_sa) {
120                         .id = child_id,
121                         .childlist_entry = LIST_INITIALIZER(sa->childlist_entry),
122                         .vc = NULL,
123                 };
124                 list_add_tail(&sa->childlist_entry, &childlist_head[child_hash]);
125         }
126
127         if (sa->vc == vc)
128                 return 0;
129
130         if (vc) {
131                 /* Attach first to new VC */
132                 vc->ipsec++;
133                 nhrp_vc_update(vc, NOTIFY_VC_IPSEC_CHANGED);
134         }
135         if (sa->vc && vc) {
136                 /* Notify old VC of migration */
137                 sa->vc->abort_migration = 0;
138                 debugf(NHRP_DEBUG_COMMON, "IPsec NBMA change of %s to %s",
139                         sockunion2str(&sa->vc->remote.nbma, buf[0], sizeof buf[0]),
140                         sockunion2str(&vc->remote.nbma, buf[1], sizeof buf[1]));
141                 nhrp_vc_update(sa->vc, NOTIFY_VC_IPSEC_UPDATE_NBMA);
142                 abort_migration = sa->vc->abort_migration;
143         }
144         if (sa->vc) {
145                 /* Deattach old VC */
146                 sa->vc->ipsec--;
147                 if (!sa->vc->ipsec) nhrp_vc_ipsec_reset(sa->vc);
148                 nhrp_vc_update(sa->vc, NOTIFY_VC_IPSEC_CHANGED);
149         }
150
151         /* Update */
152         sa->vc = vc;
153         if (!vc) {
154                 list_del(&sa->childlist_entry);
155                 XFREE(MTYPE_NHRP_VC, sa);
156         }
157
158         return abort_migration;
159 }
160
161 void nhrp_vc_notify_add(struct nhrp_vc *vc, struct notifier_block *n, notifier_fn_t action)
162 {
163         notifier_add(n, &vc->notifier_list, action);
164 }
165
166 void nhrp_vc_notify_del(struct nhrp_vc *vc, struct notifier_block *n)
167 {
168         notifier_del(n);
169         nhrp_vc_check_delete(vc);
170 }
171
172
173 struct nhrp_vc_iterator_ctx {
174         void (*cb)(struct nhrp_vc *, void *);
175         void *ctx;
176 };
177
178 static void nhrp_vc_iterator(struct hash_backet *b, void *ctx)
179 {
180         struct nhrp_vc_iterator_ctx *ic = ctx;
181         ic->cb(b->data, ic->ctx);
182 }
183
184 void nhrp_vc_foreach(void (*cb)(struct nhrp_vc *, void *), void *ctx)
185 {
186         struct nhrp_vc_iterator_ctx ic = {
187                 .cb = cb,
188                 .ctx = ctx,
189         };
190         hash_iterate(nhrp_vc_hash, nhrp_vc_iterator, &ic);
191 }
192
193 void nhrp_vc_init(void)
194 {
195         size_t i;
196
197         nhrp_vc_hash = hash_create(nhrp_vc_key, nhrp_vc_cmp);
198         for (i = 0; i < ZEBRA_NUM_OF(childlist_head); i++)
199                 list_init(&childlist_head[i]);
200 }
201
202 void nhrp_vc_reset(void)
203 {
204         struct child_sa *sa, *n;
205         size_t i;
206
207         for (i = 0; i < ZEBRA_NUM_OF(childlist_head); i++) {
208                 list_for_each_entry_safe(sa, n, &childlist_head[i], childlist_entry)
209                         nhrp_vc_ipsec_updown(sa->id, 0);
210         }
211 }
212
213 void nhrp_vc_terminate(void)
214 {
215         nhrp_vc_reset();
216         hash_clean(nhrp_vc_hash, nhrp_vc_free);
217 }