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