]> git.proxmox.com Git - mirror_frr.git/blame - nhrpd/nhrp_nhs.c
Merge pull request #7550 from volta-networks/fix_bfd_isis
[mirror_frr.git] / nhrpd / nhrp_nhs.c
CommitLineData
2fb975da
TT
1/* NHRP NHC nexthop server functions (registration)
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 "zbuf.h"
12#include "memory.h"
13#include "thread.h"
14#include "nhrpd.h"
15#include "nhrp_protocol.h"
16
819dc8bb
DL
17DEFINE_MTYPE_STATIC(NHRPD, NHRP_NHS, "NHRP next hop server")
18DEFINE_MTYPE_STATIC(NHRPD, NHRP_REGISTRATION, "NHRP registration entries")
19
2fb975da 20static int nhrp_nhs_resolve(struct thread *t);
2fb975da
TT
21static int nhrp_reg_send_req(struct thread *t);
22
23static void nhrp_reg_reply(struct nhrp_reqid *reqid, void *arg)
24{
25 struct nhrp_packet_parser *p = arg;
996c9314
LB
26 struct nhrp_registration *r =
27 container_of(reqid, struct nhrp_registration, reqid);
2fb975da
TT
28 struct nhrp_nhs *nhs = r->nhs;
29 struct interface *ifp = nhs->ifp;
30 struct nhrp_interface *nifp = ifp->info;
31 struct nhrp_extension_header *ext;
32 struct nhrp_cie_header *cie;
33 struct nhrp_cache *c;
34 struct zbuf extpl;
35 union sockunion cie_nbma, cie_proto, *proto;
36 char buf[64];
37 int ok = 0, holdtime;
659fde26 38 unsigned short mtu = 0;
2fb975da
TT
39
40 nhrp_reqid_free(&nhrp_packet_reqid, &r->reqid);
41
42 if (p->hdr->type != NHRP_PACKET_REGISTRATION_REPLY) {
43 debugf(NHRP_DEBUG_COMMON, "NHS: Registration failed");
44 return;
45 }
46
47 debugf(NHRP_DEBUG_COMMON, "NHS: Reg.reply received");
48
49 ok = 1;
996c9314
LB
50 while ((cie = nhrp_cie_pull(&p->payload, p->hdr, &cie_nbma, &cie_proto))
51 != NULL) {
52 proto = sockunion_family(&cie_proto) != AF_UNSPEC
53 ? &cie_proto
54 : &p->src_proto;
2fb975da 55 debugf(NHRP_DEBUG_COMMON, "NHS: CIE registration: %s: %d",
996c9314
LB
56 sockunion2str(proto, buf, sizeof(buf)), cie->code);
57 if (!((cie->code == NHRP_CODE_SUCCESS)
58 || (cie->code == NHRP_CODE_ADMINISTRATIVELY_PROHIBITED
59 && nhs->hub)))
2fb975da 60 ok = 0;
659fde26
GG
61 mtu = ntohs(cie->mtu);
62 debugf(NHRP_DEBUG_COMMON, "NHS: CIE MTU: %d", mtu);
2fb975da
TT
63 }
64
65 if (!ok)
66 return;
67
68 /* Parse extensions */
69 sockunion_family(&nifp->nat_nbma) = AF_UNSPEC;
70 while ((ext = nhrp_ext_pull(&p->extensions, &extpl)) != NULL) {
71 switch (htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY) {
72 case NHRP_EXTENSION_NAT_ADDRESS:
73 /* NHS adds second CIE if NAT is detected */
996c9314
LB
74 if (nhrp_cie_pull(&extpl, p->hdr, &cie_nbma, &cie_proto)
75 && nhrp_cie_pull(&extpl, p->hdr, &cie_nbma,
76 &cie_proto)) {
2fb975da 77 nifp->nat_nbma = cie_nbma;
996c9314
LB
78 debugf(NHRP_DEBUG_IF,
79 "%s: NAT detected, real NBMA address: %s",
80 ifp->name,
81 sockunion2str(&nifp->nbma, buf,
82 sizeof(buf)));
2fb975da
TT
83 }
84 break;
85 }
86 }
87
88 /* Success - schedule next registration, and route NHS */
89 r->timeout = 2;
90 holdtime = nifp->afi[nhs->afi].holdtime;
91 THREAD_OFF(r->t_register);
92
93 /* RFC 2332 5.2.3 - Registration is recommend to be renewed
94 * every one third of holdtime */
ffa2c898
QY
95 thread_add_timer(master, nhrp_reg_send_req, r, holdtime / 3,
96 &r->t_register);
2fb975da
TT
97
98 r->proto_addr = p->dst_proto;
99 c = nhrp_cache_get(ifp, &p->dst_proto, 1);
996c9314
LB
100 if (c)
101 nhrp_cache_update_binding(c, NHRP_CACHE_NHS, holdtime,
659fde26 102 nhrp_peer_ref(r->peer), mtu, NULL);
2fb975da
TT
103}
104
105static int nhrp_reg_timeout(struct thread *t)
106{
107 struct nhrp_registration *r = THREAD_ARG(t);
108 struct nhrp_cache *c;
109
110 r->t_register = NULL;
111
112 if (r->timeout >= 16 && sockunion_family(&r->proto_addr) != AF_UNSPEC) {
113 nhrp_reqid_free(&nhrp_packet_reqid, &r->reqid);
114 c = nhrp_cache_get(r->nhs->ifp, &r->proto_addr, 0);
996c9314
LB
115 if (c)
116 nhrp_cache_update_binding(c, NHRP_CACHE_NHS, -1, NULL,
117 0, NULL);
2fb975da
TT
118 sockunion_family(&r->proto_addr) = AF_UNSPEC;
119 }
120
121 r->timeout <<= 1;
996c9314
LB
122 if (r->timeout > 64)
123 r->timeout = 2;
124 thread_add_timer_msec(master, nhrp_reg_send_req, r, 10, &r->t_register);
2fb975da
TT
125
126 return 0;
127}
128
129static void nhrp_reg_peer_notify(struct notifier_block *n, unsigned long cmd)
130{
996c9314
LB
131 struct nhrp_registration *r =
132 container_of(n, struct nhrp_registration, peer_notifier);
2fb975da
TT
133 char buf[SU_ADDRSTRLEN];
134
135 switch (cmd) {
136 case NOTIFY_PEER_UP:
137 case NOTIFY_PEER_DOWN:
138 case NOTIFY_PEER_IFCONFIG_CHANGED:
139 case NOTIFY_PEER_MTU_CHANGED:
140 debugf(NHRP_DEBUG_COMMON, "NHS: Flush timer for %s",
996c9314 141 sockunion2str(&r->peer->vc->remote.nbma, buf,
0d6f7fd6 142 sizeof(buf)));
50478845 143 THREAD_OFF(r->t_register);
ffa2c898
QY
144 thread_add_timer_msec(master, nhrp_reg_send_req, r, 10,
145 &r->t_register);
2fb975da
TT
146 break;
147 }
148}
149
150static int nhrp_reg_send_req(struct thread *t)
151{
152 struct nhrp_registration *r = THREAD_ARG(t);
153 struct nhrp_nhs *nhs = r->nhs;
154 char buf1[SU_ADDRSTRLEN], buf2[SU_ADDRSTRLEN];
155 struct interface *ifp = nhs->ifp;
156 struct nhrp_interface *nifp = ifp->info;
157 struct nhrp_afi_data *if_ad = &nifp->afi[nhs->afi];
158 union sockunion *dst_proto;
159 struct zbuf *zb;
160 struct nhrp_packet_header *hdr;
161 struct nhrp_extension_header *ext;
162 struct nhrp_cie_header *cie;
163
164 r->t_register = NULL;
165 if (!nhrp_peer_check(r->peer, 2)) {
166 debugf(NHRP_DEBUG_COMMON, "NHS: Waiting link for %s",
996c9314 167 sockunion2str(&r->peer->vc->remote.nbma, buf1,
0d6f7fd6 168 sizeof(buf1)));
ffa2c898
QY
169 thread_add_timer(master, nhrp_reg_send_req, r, 120,
170 &r->t_register);
2fb975da
TT
171 return 0;
172 }
173
ffa2c898
QY
174 thread_add_timer(master, nhrp_reg_timeout, r, r->timeout,
175 &r->t_register);
2fb975da
TT
176
177 /* RFC2332 5.2.3 NHC uses it's own address as dst if NHS is unknown */
178 dst_proto = &nhs->proto_addr;
179 if (sockunion_family(dst_proto) == AF_UNSPEC)
180 dst_proto = &if_ad->addr;
181
182 sockunion2str(&if_ad->addr, buf1, sizeof(buf1));
183 sockunion2str(dst_proto, buf2, sizeof(buf2));
996c9314
LB
184 debugf(NHRP_DEBUG_COMMON, "NHS: Register %s -> %s (timeout %d)", buf1,
185 buf2, r->timeout);
2fb975da
TT
186
187 /* No protocol address configured for tunnel interface */
188 if (sockunion_family(&if_ad->addr) == AF_UNSPEC)
189 return 0;
190
191 zb = zbuf_alloc(1400);
996c9314
LB
192 hdr = nhrp_packet_push(zb, NHRP_PACKET_REGISTRATION_REQUEST,
193 &nifp->nbma, &if_ad->addr, dst_proto);
ef9329ac 194 hdr->hop_count = 1;
2fb975da
TT
195 if (!(if_ad->flags & NHRP_IFF_REG_NO_UNIQUE))
196 hdr->flags |= htons(NHRP_FLAG_REGISTRATION_UNIQUE);
197
996c9314
LB
198 hdr->u.request_id = htonl(nhrp_reqid_alloc(&nhrp_packet_reqid,
199 &r->reqid, nhrp_reg_reply));
2fb975da
TT
200
201 /* FIXME: push CIE for each local protocol address */
202 cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, NULL, NULL);
f7f9a377
AL
203 /* RFC2332 5.2.1 if unique is set then prefix length must be 0xff */
204 cie->prefix_length = (if_ad->flags & NHRP_IFF_REG_NO_UNIQUE) ? 8 * sockunion_get_addrlen(dst_proto) : 0xff;
2fb975da
TT
205 cie->holding_time = htons(if_ad->holdtime);
206 cie->mtu = htons(if_ad->mtu);
207
208 nhrp_ext_request(zb, hdr, ifp);
209
210 /* Cisco NAT detection extension */
211 hdr->flags |= htons(NHRP_FLAG_REGISTRATION_NAT);
212 ext = nhrp_ext_push(zb, hdr, NHRP_EXTENSION_NAT_ADDRESS);
213 cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, &nifp->nbma, &if_ad->addr);
55fd6ee9 214 cie->prefix_length = 8 * sockunion_get_addrlen(&if_ad->addr);
2fb975da
TT
215 nhrp_ext_complete(zb, ext);
216
217 nhrp_packet_complete(zb, hdr);
218 nhrp_peer_send(r->peer, zb);
219 zbuf_free(zb);
220
221 return 0;
222}
223
224static void nhrp_reg_delete(struct nhrp_registration *r)
225{
226 nhrp_peer_notify_del(r->peer, &r->peer_notifier);
227 nhrp_peer_unref(r->peer);
228 list_del(&r->reglist_entry);
229 THREAD_OFF(r->t_register);
230 XFREE(MTYPE_NHRP_REGISTRATION, r);
231}
232
996c9314
LB
233static struct nhrp_registration *
234nhrp_reg_by_nbma(struct nhrp_nhs *nhs, const union sockunion *nbma_addr)
2fb975da
TT
235{
236 struct nhrp_registration *r;
237
996c9314
LB
238 list_for_each_entry(
239 r, &nhs->reglist_head,
240 reglist_entry) if (sockunion_same(&r->peer->vc->remote.nbma,
241 nbma_addr)) return r;
2fb975da
TT
242 return NULL;
243}
244
3286ca07
DL
245static void nhrp_nhs_resolve_cb(struct resolver_query *q, const char *errstr,
246 int n, union sockunion *addrs)
2fb975da
TT
247{
248 struct nhrp_nhs *nhs = container_of(q, struct nhrp_nhs, dns_resolve);
249 struct nhrp_interface *nifp = nhs->ifp->info;
250 struct nhrp_registration *reg, *regn;
251 int i;
252
253 nhs->t_resolve = NULL;
254 if (n < 0) {
255 /* Failed, retry in a moment */
ffa2c898
QY
256 thread_add_timer(master, nhrp_nhs_resolve, nhs, 5,
257 &nhs->t_resolve);
2fb975da
TT
258 return;
259 }
260
ffa2c898
QY
261 thread_add_timer(master, nhrp_nhs_resolve, nhs, 2 * 60 * 60,
262 &nhs->t_resolve);
2fb975da 263
996c9314
LB
264 list_for_each_entry(reg, &nhs->reglist_head, reglist_entry) reg->mark =
265 1;
2fb975da
TT
266
267 nhs->hub = 0;
268 for (i = 0; i < n; i++) {
269 if (sockunion_same(&addrs[i], &nifp->nbma)) {
270 nhs->hub = 1;
271 continue;
272 }
273
274 reg = nhrp_reg_by_nbma(nhs, &addrs[i]);
275 if (reg) {
276 reg->mark = 0;
277 continue;
278 }
279
280 reg = XCALLOC(MTYPE_NHRP_REGISTRATION, sizeof(*reg));
281 reg->peer = nhrp_peer_get(nhs->ifp, &addrs[i]);
282 reg->nhs = nhs;
283 reg->timeout = 1;
284 list_init(&reg->reglist_entry);
285 list_add_tail(&reg->reglist_entry, &nhs->reglist_head);
996c9314
LB
286 nhrp_peer_notify_add(reg->peer, &reg->peer_notifier,
287 nhrp_reg_peer_notify);
ffa2c898
QY
288 thread_add_timer_msec(master, nhrp_reg_send_req, reg, 50,
289 &reg->t_register);
2fb975da
TT
290 }
291
996c9314
LB
292 list_for_each_entry_safe(reg, regn, &nhs->reglist_head, reglist_entry)
293 {
2fb975da
TT
294 if (reg->mark)
295 nhrp_reg_delete(reg);
296 }
297}
298
299static int nhrp_nhs_resolve(struct thread *t)
300{
301 struct nhrp_nhs *nhs = THREAD_ARG(t);
302
996c9314
LB
303 resolver_resolve(&nhs->dns_resolve, AF_INET, nhs->nbma_fqdn,
304 nhrp_nhs_resolve_cb);
2fb975da
TT
305
306 return 0;
307}
308
996c9314
LB
309int nhrp_nhs_add(struct interface *ifp, afi_t afi, union sockunion *proto_addr,
310 const char *nbma_fqdn)
2fb975da
TT
311{
312 struct nhrp_interface *nifp = ifp->info;
313 struct nhrp_nhs *nhs;
314
996c9314
LB
315 if (sockunion_family(proto_addr) != AF_UNSPEC
316 && sockunion_family(proto_addr) != afi2family(afi))
2fb975da
TT
317 return NHRP_ERR_PROTOCOL_ADDRESS_MISMATCH;
318
996c9314
LB
319 list_for_each_entry(nhs, &nifp->afi[afi].nhslist_head, nhslist_entry)
320 {
321 if (sockunion_family(&nhs->proto_addr) != AF_UNSPEC
322 && sockunion_family(proto_addr) != AF_UNSPEC
323 && sockunion_same(&nhs->proto_addr, proto_addr))
2fb975da
TT
324 return NHRP_ERR_ENTRY_EXISTS;
325
326 if (strcmp(nhs->nbma_fqdn, nbma_fqdn) == 0)
327 return NHRP_ERR_ENTRY_EXISTS;
328 }
329
330 nhs = XMALLOC(MTYPE_NHRP_NHS, sizeof(struct nhrp_nhs));
2fb975da 331
996c9314 332 *nhs = (struct nhrp_nhs){
2fb975da
TT
333 .afi = afi,
334 .ifp = ifp,
335 .proto_addr = *proto_addr,
336 .nbma_fqdn = strdup(nbma_fqdn),
337 .reglist_head = LIST_INITIALIZER(nhs->reglist_head),
338 };
339 list_add_tail(&nhs->nhslist_entry, &nifp->afi[afi].nhslist_head);
ffa2c898
QY
340 thread_add_timer_msec(master, nhrp_nhs_resolve, nhs, 1000,
341 &nhs->t_resolve);
2fb975da
TT
342
343 return NHRP_OK;
344}
345
996c9314
LB
346int nhrp_nhs_del(struct interface *ifp, afi_t afi, union sockunion *proto_addr,
347 const char *nbma_fqdn)
2fb975da
TT
348{
349 struct nhrp_interface *nifp = ifp->info;
350 struct nhrp_nhs *nhs, *nnhs;
351 int ret = NHRP_ERR_ENTRY_NOT_FOUND;
352
996c9314
LB
353 if (sockunion_family(proto_addr) != AF_UNSPEC
354 && sockunion_family(proto_addr) != afi2family(afi))
2fb975da
TT
355 return NHRP_ERR_PROTOCOL_ADDRESS_MISMATCH;
356
996c9314
LB
357 list_for_each_entry_safe(nhs, nnhs, &nifp->afi[afi].nhslist_head,
358 nhslist_entry)
359 {
2fb975da
TT
360 if (!sockunion_same(&nhs->proto_addr, proto_addr))
361 continue;
362 if (strcmp(nhs->nbma_fqdn, nbma_fqdn) != 0)
363 continue;
364
365 nhrp_nhs_free(nhs);
366 ret = NHRP_OK;
367 }
368
369 return ret;
370}
371
372int nhrp_nhs_free(struct nhrp_nhs *nhs)
373{
374 struct nhrp_registration *r, *rn;
375
376 list_for_each_entry_safe(r, rn, &nhs->reglist_head, reglist_entry)
377 nhrp_reg_delete(r);
378 THREAD_OFF(nhs->t_resolve);
379 list_del(&nhs->nhslist_entry);
996c9314 380 free((void *)nhs->nbma_fqdn);
2fb975da
TT
381 XFREE(MTYPE_NHRP_NHS, nhs);
382 return 0;
383}
384
ee72f0a0
RD
385void nhrp_nhs_interface_del(struct interface *ifp)
386{
387 struct nhrp_interface *nifp = ifp->info;
388 struct nhrp_nhs *nhs, *tmp;
389 afi_t afi;
390
391 for (afi = 0; afi < AFI_MAX; afi++) {
392 debugf(NHRP_DEBUG_COMMON, "Cleaning up nhs entries (%d)",
393 !list_empty(&nifp->afi[afi].nhslist_head));
394
395 list_for_each_entry_safe(nhs, tmp, &nifp->afi[afi].nhslist_head,
396 nhslist_entry)
397 {
398 nhrp_nhs_free(nhs);
399 }
400 }
401}
402
2fb975da
TT
403void nhrp_nhs_terminate(void)
404{
f4e14fdb 405 struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
2fb975da
TT
406 struct interface *ifp;
407 struct nhrp_interface *nifp;
408 struct nhrp_nhs *nhs, *tmp;
2fb975da
TT
409 afi_t afi;
410
451fda4f 411 FOR_ALL_INTERFACES (vrf, ifp) {
2fb975da
TT
412 nifp = ifp->info;
413 for (afi = 0; afi < AFI_MAX; afi++) {
996c9314
LB
414 list_for_each_entry_safe(
415 nhs, tmp, &nifp->afi[afi].nhslist_head,
416 nhslist_entry) nhrp_nhs_free(nhs);
2fb975da
TT
417 }
418 }
419}
0ca036b4 420
996c9314
LB
421void nhrp_nhs_foreach(struct interface *ifp, afi_t afi,
422 void (*cb)(struct nhrp_nhs *, struct nhrp_registration *,
423 void *),
424 void *ctx)
0ca036b4
TT
425{
426 struct nhrp_interface *nifp = ifp->info;
427 struct nhrp_nhs *nhs;
428 struct nhrp_registration *reg;
429
996c9314
LB
430 list_for_each_entry(nhs, &nifp->afi[afi].nhslist_head, nhslist_entry)
431 {
0ca036b4 432 if (!list_empty(&nhs->reglist_head)) {
996c9314
LB
433 list_for_each_entry(reg, &nhs->reglist_head,
434 reglist_entry) cb(nhs, reg, ctx);
0ca036b4
TT
435 } else
436 cb(nhs, 0, ctx);
437 }
438}