]> git.proxmox.com Git - mirror_frr.git/blame - nhrpd/nhrp_peer.c
*: require semicolon after DEFINE_MTYPE & co
[mirror_frr.git] / nhrpd / nhrp_peer.c
CommitLineData
2fb975da
TT
1/* NHRP peer functions
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
b45ac5f5
DL
10#ifdef HAVE_CONFIG_H
11#include "config.h"
12#endif
13
2fb975da
TT
14#include <netinet/if_ether.h>
15
16#include "zebra.h"
17#include "memory.h"
18#include "thread.h"
19#include "hash.h"
20
21#include "nhrpd.h"
22#include "nhrp_protocol.h"
23#include "os.h"
24
bf8d3d6a 25DEFINE_MTYPE_STATIC(NHRPD, NHRP_PEER, "NHRP peer entry");
819dc8bb 26
2fb975da
TT
27struct ipv6hdr {
28 uint8_t priority_version;
29 uint8_t flow_lbl[3];
30 uint16_t payload_len;
31 uint8_t nexthdr;
32 uint8_t hop_limit;
33 struct in6_addr saddr;
34 struct in6_addr daddr;
35};
36
37static void nhrp_packet_debug(struct zbuf *zb, const char *dir);
38
39static void nhrp_peer_check_delete(struct nhrp_peer *p)
40{
41 struct nhrp_interface *nifp = p->ifp->info;
42
43 if (p->ref || notifier_active(&p->notifier_list))
44 return;
45
b6c48481
DS
46 debugf(NHRP_DEBUG_COMMON, "Deleting peer ref:%d remote:%pSU local:%pSU",
47 p->ref, &p->vc->remote.nbma, &p->vc->local.nbma);
ee72f0a0 48
2fb975da
TT
49 THREAD_OFF(p->t_fallback);
50 hash_release(nifp->peer_hash, p);
51 nhrp_interface_notify_del(p->ifp, &p->ifp_notifier);
52 nhrp_vc_notify_del(p->vc, &p->vc_notifier);
53 XFREE(MTYPE_NHRP_PEER, p);
54}
55
56static int nhrp_peer_notify_up(struct thread *t)
57{
58 struct nhrp_peer *p = THREAD_ARG(t);
59 struct nhrp_vc *vc = p->vc;
60 struct interface *ifp = p->ifp;
61 struct nhrp_interface *nifp = ifp->info;
62
63 p->t_fallback = NULL;
64 if (nifp->enabled && (!nifp->ipsec_profile || vc->ipsec)) {
65 p->online = 1;
66 nhrp_peer_ref(p);
67 notifier_call(&p->notifier_list, NOTIFY_PEER_UP);
68 nhrp_peer_unref(p);
69 }
70
71 return 0;
72}
73
74static void __nhrp_peer_check(struct nhrp_peer *p)
75{
76 struct nhrp_vc *vc = p->vc;
77 struct interface *ifp = p->ifp;
78 struct nhrp_interface *nifp = ifp->info;
79 unsigned online;
80
81 online = nifp->enabled && (!nifp->ipsec_profile || vc->ipsec);
82 if (p->online != online) {
83 THREAD_OFF(p->t_fallback);
84 if (online && notifier_active(&p->notifier_list)) {
85 /* If we requested the IPsec connection, delay
86 * the up notification a bit to allow things
87 * settle down. This allows IKE to install
88 * SPDs and SAs. */
ffa2c898
QY
89 thread_add_timer_msec(master, nhrp_peer_notify_up, p,
90 50, &p->t_fallback);
2fb975da
TT
91 } else {
92 nhrp_peer_ref(p);
93 p->online = online;
94 if (online) {
996c9314
LB
95 notifier_call(&p->notifier_list,
96 NOTIFY_PEER_UP);
2fb975da
TT
97 } else {
98 p->requested = p->fallback_requested = 0;
996c9314
LB
99 notifier_call(&p->notifier_list,
100 NOTIFY_PEER_DOWN);
2fb975da
TT
101 }
102 nhrp_peer_unref(p);
103 }
104 }
105}
106
107static void nhrp_peer_vc_notify(struct notifier_block *n, unsigned long cmd)
108{
109 struct nhrp_peer *p = container_of(n, struct nhrp_peer, vc_notifier);
110
111 switch (cmd) {
112 case NOTIFY_VC_IPSEC_CHANGED:
113 __nhrp_peer_check(p);
114 break;
115 case NOTIFY_VC_IPSEC_UPDATE_NBMA:
116 nhrp_peer_ref(p);
117 notifier_call(&p->notifier_list, NOTIFY_PEER_NBMA_CHANGING);
118 nhrp_peer_unref(p);
119 break;
120 }
121}
122
123static void nhrp_peer_ifp_notify(struct notifier_block *n, unsigned long cmd)
124{
125 struct nhrp_peer *p = container_of(n, struct nhrp_peer, ifp_notifier);
126 struct nhrp_interface *nifp;
127 struct nhrp_vc *vc;
128
129 nhrp_peer_ref(p);
130 switch (cmd) {
131 case NOTIFY_INTERFACE_UP:
132 case NOTIFY_INTERFACE_DOWN:
133 __nhrp_peer_check(p);
134 break;
135 case NOTIFY_INTERFACE_NBMA_CHANGED:
136 /* Source NBMA changed, rebind to new VC */
137 nifp = p->ifp->info;
138 vc = nhrp_vc_get(&nifp->nbma, &p->vc->remote.nbma, 1);
139 if (vc && p->vc != vc) {
140 nhrp_vc_notify_del(p->vc, &p->vc_notifier);
141 p->vc = vc;
996c9314
LB
142 nhrp_vc_notify_add(p->vc, &p->vc_notifier,
143 nhrp_peer_vc_notify);
2fb975da
TT
144 __nhrp_peer_check(p);
145 }
acd738fc 146 /* fallthru */ /* to post config update */
2fb975da
TT
147 case NOTIFY_INTERFACE_ADDRESS_CHANGED:
148 notifier_call(&p->notifier_list, NOTIFY_PEER_IFCONFIG_CHANGED);
149 break;
150 case NOTIFY_INTERFACE_MTU_CHANGED:
151 notifier_call(&p->notifier_list, NOTIFY_PEER_MTU_CHANGED);
152 break;
153 }
154 nhrp_peer_unref(p);
155}
156
d8b87afe 157static unsigned int nhrp_peer_key(const void *peer_data)
2fb975da 158{
d8b87afe 159 const struct nhrp_peer *p = peer_data;
2fb975da
TT
160 return sockunion_hash(&p->vc->remote.nbma);
161}
162
74df8d6d 163static bool nhrp_peer_cmp(const void *cache_data, const void *key_data)
2fb975da
TT
164{
165 const struct nhrp_peer *a = cache_data;
166 const struct nhrp_peer *b = key_data;
74df8d6d 167
2fb975da
TT
168 return a->ifp == b->ifp && a->vc == b->vc;
169}
170
171static void *nhrp_peer_create(void *data)
172{
173 struct nhrp_peer *p, *key = data;
174
175 p = XMALLOC(MTYPE_NHRP_PEER, sizeof(*p));
0ce1ca80
DS
176
177 *p = (struct nhrp_peer){
178 .ref = 0,
179 .ifp = key->ifp,
180 .vc = key->vc,
181 .notifier_list =
182 NOTIFIER_LIST_INITIALIZER(&p->notifier_list),
183 };
184 nhrp_vc_notify_add(p->vc, &p->vc_notifier, nhrp_peer_vc_notify);
185 nhrp_interface_notify_add(p->ifp, &p->ifp_notifier,
186 nhrp_peer_ifp_notify);
187
2fb975da
TT
188 return p;
189}
190
ee72f0a0
RD
191static void do_peer_hash_free(struct hash_bucket *hb,
192 void *arg __attribute__((__unused__)))
193{
194 struct nhrp_peer *p = hb->data;
195 nhrp_peer_check_delete(p);
196}
197
198void nhrp_peer_interface_del(struct interface *ifp)
199{
200 struct nhrp_interface *nifp = ifp->info;
201
202 debugf(NHRP_DEBUG_COMMON, "Cleaning up undeleted peer entries (%lu)",
203 nifp->peer_hash ? nifp->peer_hash->count : 0);
204
205 if (nifp->peer_hash) {
206 hash_iterate(nifp->peer_hash, do_peer_hash_free, NULL);
207 assert(nifp->peer_hash->count == 0);
208 hash_free(nifp->peer_hash);
209 }
210}
211
996c9314
LB
212struct nhrp_peer *nhrp_peer_get(struct interface *ifp,
213 const union sockunion *remote_nbma)
2fb975da
TT
214{
215 struct nhrp_interface *nifp = ifp->info;
216 struct nhrp_peer key, *p;
217 struct nhrp_vc *vc;
218
219 if (!nifp->peer_hash) {
996c9314 220 nifp->peer_hash = hash_create(nhrp_peer_key, nhrp_peer_cmp,
8462c0ff 221 "NHRP Peer Hash");
996c9314
LB
222 if (!nifp->peer_hash)
223 return NULL;
2fb975da
TT
224 }
225
226 vc = nhrp_vc_get(&nifp->nbma, remote_nbma, 1);
996c9314
LB
227 if (!vc)
228 return NULL;
2fb975da
TT
229
230 key.ifp = ifp;
231 key.vc = vc;
232
233 p = hash_get(nifp->peer_hash, &key, nhrp_peer_create);
234 nhrp_peer_ref(p);
996c9314
LB
235 if (p->ref == 1)
236 __nhrp_peer_check(p);
2fb975da
TT
237
238 return p;
239}
240
241struct nhrp_peer *nhrp_peer_ref(struct nhrp_peer *p)
242{
996c9314
LB
243 if (p)
244 p->ref++;
2fb975da
TT
245 return p;
246}
247
248void nhrp_peer_unref(struct nhrp_peer *p)
249{
250 if (p) {
251 p->ref--;
252 nhrp_peer_check_delete(p);
253 }
254}
255
256static int nhrp_peer_request_timeout(struct thread *t)
257{
258 struct nhrp_peer *p = THREAD_ARG(t);
259 struct nhrp_vc *vc = p->vc;
260 struct interface *ifp = p->ifp;
261 struct nhrp_interface *nifp = ifp->info;
262
263 p->t_fallback = NULL;
264
265 if (p->online)
266 return 0;
267
996c9314
LB
268 if (nifp->ipsec_fallback_profile && !p->prio
269 && !p->fallback_requested) {
2fb975da 270 p->fallback_requested = 1;
996c9314
LB
271 vici_request_vc(nifp->ipsec_fallback_profile, &vc->local.nbma,
272 &vc->remote.nbma, p->prio);
ffa2c898
QY
273 thread_add_timer(master, nhrp_peer_request_timeout, p, 30,
274 &p->t_fallback);
2fb975da
TT
275 } else {
276 p->requested = p->fallback_requested = 0;
277 }
278
279 return 0;
280}
281
282int nhrp_peer_check(struct nhrp_peer *p, int establish)
283{
284 struct nhrp_vc *vc = p->vc;
285 struct interface *ifp = p->ifp;
286 struct nhrp_interface *nifp = ifp->info;
287
288 if (p->online)
289 return 1;
290 if (!establish)
291 return 0;
292 if (p->requested)
293 return 0;
8ec0c3c1
TT
294 if (!nifp->ipsec_profile)
295 return 0;
2fb975da
TT
296 if (sockunion_family(&vc->local.nbma) == AF_UNSPEC)
297 return 0;
6c9ca587
GG
298 if (vc->ipsec)
299 return 1;
2fb975da
TT
300
301 p->prio = establish > 1;
302 p->requested = 1;
996c9314
LB
303 vici_request_vc(nifp->ipsec_profile, &vc->local.nbma, &vc->remote.nbma,
304 p->prio);
ffa2c898
QY
305 thread_add_timer(master, nhrp_peer_request_timeout, p,
306 (nifp->ipsec_fallback_profile && !p->prio) ? 15 : 30,
307 &p->t_fallback);
2fb975da
TT
308
309 return 0;
310}
311
996c9314
LB
312void nhrp_peer_notify_add(struct nhrp_peer *p, struct notifier_block *n,
313 notifier_fn_t fn)
2fb975da
TT
314{
315 notifier_add(n, &p->notifier_list, fn);
316}
317
318void nhrp_peer_notify_del(struct nhrp_peer *p, struct notifier_block *n)
319{
320 notifier_del(n);
321 nhrp_peer_check_delete(p);
322}
323
324void nhrp_peer_send(struct nhrp_peer *p, struct zbuf *zb)
325{
2fb975da
TT
326 nhrp_packet_debug(zb, "Send");
327
328 if (!p->online)
329 return;
330
b6c48481
DS
331 debugf(NHRP_DEBUG_KERNEL, "PACKET: Send %pSU -> %pSU",
332 &p->vc->local.nbma, &p->vc->remote.nbma);
2fb975da 333
996c9314
LB
334 os_sendmsg(zb->head, zbuf_used(zb), p->ifp->ifindex,
335 sockunion_get_addr(&p->vc->remote.nbma),
336 sockunion_get_addrlen(&p->vc->remote.nbma));
2fb975da
TT
337 zbuf_reset(zb);
338}
339
47d40757 340static void nhrp_handle_resolution_req(struct nhrp_packet_parser *pp)
2fb975da 341{
47d40757 342 struct interface *ifp = pp->ifp;
2fb975da
TT
343 struct zbuf *zb, payload;
344 struct nhrp_packet_header *hdr;
345 struct nhrp_cie_header *cie;
346 struct nhrp_extension_header *ext;
47d40757
GN
347 struct nhrp_cache *c;
348 union sockunion cie_nbma, cie_proto, *proto_addr, *nbma_addr;
349 int holdtime, prefix_len, hostprefix_len;
350 struct nhrp_interface *nifp = ifp->info;
2fb975da 351 struct nhrp_peer *peer;
47d40757
GN
352 size_t paylen;
353 char buf[SU_ADDRSTRLEN];
2fb975da 354
47d40757 355 if (!(pp->if_ad->flags & NHRP_IFF_SHORTCUT)) {
2fb975da
TT
356 debugf(NHRP_DEBUG_COMMON, "Shortcuts disabled");
357 /* FIXME: Send error indication? */
358 return;
359 }
360
47d40757
GN
361 if (pp->if_ad->network_id && pp->route_type == NHRP_ROUTE_OFF_NBMA
362 && pp->route_prefix.prefixlen < 8) {
996c9314
LB
363 debugf(NHRP_DEBUG_COMMON,
364 "Shortcut to more generic than /8 dropped");
2fb975da
TT
365 return;
366 }
367
368 debugf(NHRP_DEBUG_COMMON, "Parsing and replying to Resolution Req");
369
47d40757 370 if (nhrp_route_address(ifp, &pp->src_proto, NULL, &peer)
996c9314 371 != NHRP_ROUTE_NBMA_NEXTHOP)
2fb975da
TT
372 return;
373
47d40757
GN
374 /* Copy payload CIE */
375 hostprefix_len = 8 * sockunion_get_addrlen(&pp->if_ad->addr);
376 paylen = zbuf_used(&pp->payload);
377 debugf(NHRP_DEBUG_COMMON, "shortcut res_rep: paylen %zu", paylen);
378
379 while ((cie = nhrp_cie_pull(&pp->payload, pp->hdr, &cie_nbma,
380 &cie_proto))
381 != NULL) {
382 prefix_len = cie->prefix_length;
383 debugf(NHRP_DEBUG_COMMON,
5f36c26c 384 "shortcut res_rep: parsing CIE with prefixlen=%u",
47d40757
GN
385 prefix_len);
386 if (prefix_len == 0 || prefix_len >= hostprefix_len)
387 prefix_len = hostprefix_len;
388
389 if (prefix_len != hostprefix_len
390 && !(pp->hdr->flags
391 & htons(NHRP_FLAG_REGISTRATION_UNIQUE))) {
392 cie->code = NHRP_CODE_BINDING_NON_UNIQUE;
393 continue;
394 }
395
396 /* We currently support only unique prefix registrations */
397 if (prefix_len != hostprefix_len) {
398 cie->code = NHRP_CODE_ADMINISTRATIVELY_PROHIBITED;
399 continue;
400 }
401
402 proto_addr = (sockunion_family(&cie_proto) == AF_UNSPEC)
403 ? &pp->src_proto
404 : &cie_proto;
405 nbma_addr = (sockunion_family(&cie_nbma) == AF_UNSPEC)
406 ? &pp->src_nbma
407 : &cie_nbma;
408
409 holdtime = htons(cie->holding_time);
410 debugf(NHRP_DEBUG_COMMON,
5f36c26c 411 "shortcut res_rep: holdtime is %u (if 0, using %u)",
47d40757
GN
412 holdtime, pp->if_ad->holdtime);
413 if (!holdtime)
414 holdtime = pp->if_ad->holdtime;
415
416 c = nhrp_cache_get(ifp, proto_addr, 1);
417 if (!c) {
418 debugf(NHRP_DEBUG_COMMON,
5f36c26c 419 "shortcut res_rep: no cache found");
47d40757
GN
420 cie->code = NHRP_CODE_INSUFFICIENT_RESOURCES;
421 continue;
422 }
423 if (nbma_addr)
5f36c26c 424 sockunion2str(nbma_addr, buf, sizeof(buf));
47d40757
GN
425
426 debugf(NHRP_DEBUG_COMMON,
5f36c26c 427 "shortcut res_rep: updating binding for nmba addr %s",
47d40757
GN
428 nbma_addr ? buf : "(NULL)");
429 if (!nhrp_cache_update_binding(c, NHRP_CACHE_DYNAMIC, holdtime,
430 nhrp_peer_ref(pp->peer),
431 htons(cie->mtu), nbma_addr)) {
432 cie->code = NHRP_CODE_ADMINISTRATIVELY_PROHIBITED;
433 continue;
434 }
2fb975da 435
47d40757
GN
436 cie->code = NHRP_CODE_SUCCESS;
437 }
2fb975da
TT
438
439 /* Create reply */
440 zb = zbuf_alloc(1500);
47d40757
GN
441 hdr = nhrp_packet_push(zb, NHRP_PACKET_RESOLUTION_REPLY, &pp->src_nbma,
442 &pp->src_proto, &pp->dst_proto);
2fb975da
TT
443
444 /* Copied information from request */
47d40757
GN
445 hdr->flags = pp->hdr->flags
446 & htons(NHRP_FLAG_RESOLUTION_SOURCE_IS_ROUTER
447 | NHRP_FLAG_RESOLUTION_SOURCE_STABLE);
996c9314
LB
448 hdr->flags |= htons(NHRP_FLAG_RESOLUTION_DESTINATION_STABLE
449 | NHRP_FLAG_RESOLUTION_AUTHORATIVE);
47d40757 450 hdr->u.request_id = pp->hdr->u.request_id;
2fb975da 451
47d40757 452 /* CIE payload for the reply packet */
996c9314 453 cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, &nifp->nbma,
47d40757
GN
454 &pp->if_ad->addr);
455 cie->holding_time = htons(pp->if_ad->holdtime);
456 cie->mtu = htons(pp->if_ad->mtu);
457 if (pp->if_ad->network_id && pp->route_type == NHRP_ROUTE_OFF_NBMA)
458 cie->prefix_length = pp->route_prefix.prefixlen;
2fb975da 459 else
47d40757
GN
460 cie->prefix_length =
461 8 * sockunion_get_addrlen(&pp->if_ad->addr);
2fb975da
TT
462
463 /* Handle extensions */
47d40757 464 while ((ext = nhrp_ext_pull(&pp->extensions, &payload)) != NULL) {
2fb975da
TT
465 switch (htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY) {
466 case NHRP_EXTENSION_NAT_ADDRESS:
467 if (sockunion_family(&nifp->nat_nbma) == AF_UNSPEC)
468 break;
996c9314
LB
469 ext = nhrp_ext_push(zb, hdr,
470 NHRP_EXTENSION_NAT_ADDRESS);
471 if (!ext)
472 goto err;
473 cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS,
47d40757 474 &nifp->nat_nbma, &pp->if_ad->addr);
996c9314
LB
475 if (!cie)
476 goto err;
2fb975da
TT
477 nhrp_ext_complete(zb, ext);
478 break;
479 default:
47d40757 480 if (nhrp_ext_reply(zb, hdr, ifp, ext, &payload) < 0)
2fb975da
TT
481 goto err;
482 break;
483 }
484 }
485
486 nhrp_packet_complete(zb, hdr);
487 nhrp_peer_send(peer, zb);
488err:
489 nhrp_peer_unref(peer);
490 zbuf_free(zb);
491}
492
493static void nhrp_handle_registration_request(struct nhrp_packet_parser *p)
494{
495 struct interface *ifp = p->ifp;
496 struct zbuf *zb, payload;
497 struct nhrp_packet_header *hdr;
498 struct nhrp_cie_header *cie;
499 struct nhrp_extension_header *ext;
500 struct nhrp_cache *c;
996c9314
LB
501 union sockunion cie_nbma, cie_proto, *proto_addr, *nbma_addr,
502 *nbma_natoa;
55fd6ee9 503 int holdtime, prefix_len, hostprefix_len, natted = 0;
2fb975da
TT
504 size_t paylen;
505 void *pay;
506
507 debugf(NHRP_DEBUG_COMMON, "Parsing and replying to Registration Req");
55fd6ee9 508 hostprefix_len = 8 * sockunion_get_addrlen(&p->if_ad->addr);
2fb975da
TT
509
510 if (!sockunion_same(&p->src_nbma, &p->peer->vc->remote.nbma))
511 natted = 1;
512
513 /* Create reply */
514 zb = zbuf_alloc(1500);
996c9314
LB
515 hdr = nhrp_packet_push(zb, NHRP_PACKET_REGISTRATION_REPLY, &p->src_nbma,
516 &p->src_proto, &p->if_ad->addr);
2fb975da
TT
517
518 /* Copied information from request */
996c9314
LB
519 hdr->flags = p->hdr->flags & htons(NHRP_FLAG_REGISTRATION_UNIQUE
520 | NHRP_FLAG_REGISTRATION_NAT);
2fb975da
TT
521 hdr->u.request_id = p->hdr->u.request_id;
522
523 /* Copy payload CIEs */
524 paylen = zbuf_used(&p->payload);
525 pay = zbuf_pushn(zb, paylen);
996c9314
LB
526 if (!pay)
527 goto err;
2fb975da
TT
528 memcpy(pay, zbuf_pulln(&p->payload, paylen), paylen);
529 zbuf_init(&payload, pay, paylen, paylen);
530
996c9314
LB
531 while ((cie = nhrp_cie_pull(&payload, hdr, &cie_nbma, &cie_proto))
532 != NULL) {
55fd6ee9
TT
533 prefix_len = cie->prefix_length;
534 if (prefix_len == 0 || prefix_len >= hostprefix_len)
535 prefix_len = hostprefix_len;
536
996c9314
LB
537 if (prefix_len != hostprefix_len
538 && !(p->hdr->flags
539 & htons(NHRP_FLAG_REGISTRATION_UNIQUE))) {
2fb975da
TT
540 cie->code = NHRP_CODE_BINDING_NON_UNIQUE;
541 continue;
542 }
543
544 /* We currently support only unique prefix registrations */
55fd6ee9 545 if (prefix_len != hostprefix_len) {
2fb975da
TT
546 cie->code = NHRP_CODE_ADMINISTRATIVELY_PROHIBITED;
547 continue;
548 }
549
996c9314
LB
550 proto_addr = (sockunion_family(&cie_proto) == AF_UNSPEC)
551 ? &p->src_proto
552 : &cie_proto;
553 nbma_addr = (sockunion_family(&cie_nbma) == AF_UNSPEC)
554 ? &p->src_nbma
555 : &cie_nbma;
2fb975da
TT
556 nbma_natoa = NULL;
557 if (natted) {
558 nbma_natoa = nbma_addr;
2fb975da
TT
559 }
560
561 holdtime = htons(cie->holding_time);
996c9314
LB
562 if (!holdtime)
563 holdtime = p->if_ad->holdtime;
2fb975da
TT
564
565 c = nhrp_cache_get(ifp, proto_addr, 1);
566 if (!c) {
567 cie->code = NHRP_CODE_INSUFFICIENT_RESOURCES;
568 continue;
569 }
570
996c9314
LB
571 if (!nhrp_cache_update_binding(c, NHRP_CACHE_DYNAMIC, holdtime,
572 nhrp_peer_ref(p->peer),
573 htons(cie->mtu), nbma_natoa)) {
2fb975da
TT
574 cie->code = NHRP_CODE_ADMINISTRATIVELY_PROHIBITED;
575 continue;
576 }
577
578 cie->code = NHRP_CODE_SUCCESS;
579 }
580
581 /* Handle extensions */
582 while ((ext = nhrp_ext_pull(&p->extensions, &payload)) != NULL) {
583 switch (htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY) {
584 case NHRP_EXTENSION_NAT_ADDRESS:
996c9314
LB
585 ext = nhrp_ext_push(zb, hdr,
586 NHRP_EXTENSION_NAT_ADDRESS);
587 if (!ext)
588 goto err;
2fb975da
TT
589 zbuf_copy(zb, &payload, zbuf_used(&payload));
590 if (natted) {
591 nhrp_cie_push(zb, NHRP_CODE_SUCCESS,
996c9314
LB
592 &p->peer->vc->remote.nbma,
593 &p->src_proto);
2fb975da
TT
594 }
595 nhrp_ext_complete(zb, ext);
596 break;
597 default:
598 if (nhrp_ext_reply(zb, hdr, ifp, ext, &payload) < 0)
599 goto err;
600 break;
601 }
602 }
603
604 nhrp_packet_complete(zb, hdr);
605 nhrp_peer_send(p->peer, zb);
606err:
607 zbuf_free(zb);
608}
609
996c9314
LB
610static int parse_ether_packet(struct zbuf *zb, uint16_t protocol_type,
611 union sockunion *src, union sockunion *dst)
2fb975da
TT
612{
613 switch (protocol_type) {
614 case ETH_P_IP: {
996c9314
LB
615 struct iphdr *iph = zbuf_pull(zb, struct iphdr);
616 if (iph) {
617 if (src)
618 sockunion_set(src, AF_INET,
619 (uint8_t *)&iph->saddr,
620 sizeof(iph->saddr));
621 if (dst)
622 sockunion_set(dst, AF_INET,
623 (uint8_t *)&iph->daddr,
624 sizeof(iph->daddr));
2fb975da 625 }
996c9314 626 } break;
2fb975da 627 case ETH_P_IPV6: {
996c9314
LB
628 struct ipv6hdr *iph = zbuf_pull(zb, struct ipv6hdr);
629 if (iph) {
630 if (src)
631 sockunion_set(src, AF_INET6,
632 (uint8_t *)&iph->saddr,
633 sizeof(iph->saddr));
634 if (dst)
635 sockunion_set(dst, AF_INET6,
636 (uint8_t *)&iph->daddr,
637 sizeof(iph->daddr));
2fb975da 638 }
996c9314 639 } break;
2fb975da
TT
640 default:
641 return 0;
642 }
643 return 1;
644}
645
996c9314
LB
646void nhrp_peer_send_indication(struct interface *ifp, uint16_t protocol_type,
647 struct zbuf *pkt)
2fb975da
TT
648{
649 union sockunion dst;
650 struct zbuf *zb, payload;
651 struct nhrp_interface *nifp = ifp->info;
652 struct nhrp_afi_data *if_ad;
653 struct nhrp_packet_header *hdr;
654 struct nhrp_peer *p;
2fb975da 655
996c9314
LB
656 if (!nifp->enabled)
657 return;
2fb975da
TT
658
659 payload = *pkt;
660 if (!parse_ether_packet(&payload, protocol_type, &dst, NULL))
661 return;
662
663 if (nhrp_route_address(ifp, &dst, NULL, &p) != NHRP_ROUTE_NBMA_NEXTHOP)
664 return;
665
666 if_ad = &nifp->afi[family2afi(sockunion_family(&dst))];
667 if (!(if_ad->flags & NHRP_IFF_REDIRECT)) {
996c9314 668 debugf(NHRP_DEBUG_COMMON,
b6c48481
DS
669 "Send Traffic Indication to %pSU about packet to %pSU ignored",
670 &p->vc->remote.nbma, &dst);
2fb975da
TT
671 return;
672 }
673
996c9314 674 debugf(NHRP_DEBUG_COMMON,
b6c48481
DS
675 "Send Traffic Indication to %pSU (online=%d) about packet to %pSU",
676 &p->vc->remote.nbma, p->online, &dst);
2fb975da
TT
677
678 /* Create reply */
679 zb = zbuf_alloc(1500);
996c9314
LB
680 hdr = nhrp_packet_push(zb, NHRP_PACKET_TRAFFIC_INDICATION, &nifp->nbma,
681 &if_ad->addr, &dst);
6f8817b4 682 hdr->hop_count = 1;
2fb975da
TT
683
684 /* Payload is the packet causing indication */
685 zbuf_copy(zb, pkt, zbuf_used(pkt));
686 nhrp_packet_complete(zb, hdr);
687 nhrp_peer_send(p, zb);
688 nhrp_peer_unref(p);
689 zbuf_free(zb);
690}
691
692static void nhrp_handle_error_ind(struct nhrp_packet_parser *pp)
693{
694 struct zbuf origmsg = pp->payload;
695 struct nhrp_packet_header *hdr;
696 struct nhrp_reqid *reqid;
697 union sockunion src_nbma, src_proto, dst_proto;
2fb975da
TT
698
699 hdr = nhrp_packet_pull(&origmsg, &src_nbma, &src_proto, &dst_proto);
996c9314
LB
700 if (!hdr)
701 return;
2fb975da 702
996c9314 703 debugf(NHRP_DEBUG_COMMON,
b6c48481
DS
704 "Error Indication from %pSU about packet to %pSU ignored",
705 &pp->src_proto, &dst_proto);
2fb975da
TT
706
707 reqid = nhrp_reqid_lookup(&nhrp_packet_reqid, htonl(hdr->u.request_id));
708 if (reqid)
709 reqid->cb(reqid, pp);
710}
711
712static void nhrp_handle_traffic_ind(struct nhrp_packet_parser *p)
713{
714 union sockunion dst;
2fb975da 715
996c9314
LB
716 if (!parse_ether_packet(&p->payload, htons(p->hdr->protocol_type), NULL,
717 &dst))
2fb975da
TT
718 return;
719
996c9314 720 debugf(NHRP_DEBUG_COMMON,
b6c48481
DS
721 "Traffic Indication from %pSU about packet to %pSU: %s",
722 &p->src_proto, &dst,
996c9314
LB
723 (p->if_ad->flags & NHRP_IFF_SHORTCUT) ? "trying shortcut"
724 : "ignored");
2fb975da
TT
725
726 if (p->if_ad->flags & NHRP_IFF_SHORTCUT)
727 nhrp_shortcut_initiate(&dst);
728}
729
730enum packet_type_t {
731 PACKET_UNKNOWN = 0,
732 PACKET_REQUEST,
733 PACKET_REPLY,
734 PACKET_INDICATION,
735};
736
47d40757 737static struct {
2fb975da
TT
738 enum packet_type_t type;
739 const char *name;
740 void (*handler)(struct nhrp_packet_parser *);
996c9314
LB
741} packet_types[] = {[0] =
742 {
743 .type = PACKET_UNKNOWN,
744 .name = "UNKNOWN",
745 },
746 [NHRP_PACKET_RESOLUTION_REQUEST] =
747 {
748 .type = PACKET_REQUEST,
749 .name = "Resolution-Request",
750 .handler = nhrp_handle_resolution_req,
751 },
752 [NHRP_PACKET_RESOLUTION_REPLY] =
753 {
754 .type = PACKET_REPLY,
755 .name = "Resolution-Reply",
756 },
757 [NHRP_PACKET_REGISTRATION_REQUEST] =
758 {
759 .type = PACKET_REQUEST,
760 .name = "Registration-Request",
761 .handler = nhrp_handle_registration_request,
762 },
763 [NHRP_PACKET_REGISTRATION_REPLY] =
764 {
765 .type = PACKET_REPLY,
766 .name = "Registration-Reply",
767 },
768 [NHRP_PACKET_PURGE_REQUEST] =
769 {
770 .type = PACKET_REQUEST,
771 .name = "Purge-Request",
772 },
773 [NHRP_PACKET_PURGE_REPLY] =
774 {
775 .type = PACKET_REPLY,
776 .name = "Purge-Reply",
777 },
778 [NHRP_PACKET_ERROR_INDICATION] =
779 {
780 .type = PACKET_INDICATION,
781 .name = "Error-Indication",
782 .handler = nhrp_handle_error_ind,
783 },
784 [NHRP_PACKET_TRAFFIC_INDICATION] = {
785 .type = PACKET_INDICATION,
786 .name = "Traffic-Indication",
787 .handler = nhrp_handle_traffic_ind,
788 }};
789
790static void nhrp_peer_forward(struct nhrp_peer *p,
791 struct nhrp_packet_parser *pp)
2fb975da
TT
792{
793 struct zbuf *zb, extpl;
794 struct nhrp_packet_header *hdr;
795 struct nhrp_extension_header *ext, *dst;
796 struct nhrp_cie_header *cie;
797 struct nhrp_interface *nifp = pp->ifp->info;
798 struct nhrp_afi_data *if_ad = pp->if_ad;
799 union sockunion cie_nbma, cie_protocol;
800 uint16_t type, len;
801
802 if (pp->hdr->hop_count == 0)
803 return;
804
805 /* Create forward packet - copy header */
806 zb = zbuf_alloc(1500);
996c9314
LB
807 hdr = nhrp_packet_push(zb, pp->hdr->type, &pp->src_nbma, &pp->src_proto,
808 &pp->dst_proto);
2fb975da
TT
809 hdr->flags = pp->hdr->flags;
810 hdr->hop_count = pp->hdr->hop_count - 1;
811 hdr->u.request_id = pp->hdr->u.request_id;
812
813 /* Copy payload */
814 zbuf_copy(zb, &pp->payload, zbuf_used(&pp->payload));
815
816 /* Copy extensions */
817 while ((ext = nhrp_ext_pull(&pp->extensions, &extpl)) != NULL) {
818 type = htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY;
819 len = htons(ext->length);
820
821 if (type == NHRP_EXTENSION_END)
822 break;
823
824 dst = nhrp_ext_push(zb, hdr, htons(ext->type));
996c9314
LB
825 if (!dst)
826 goto err;
2fb975da
TT
827
828 switch (type) {
829 case NHRP_EXTENSION_FORWARD_TRANSIT_NHS:
830 case NHRP_EXTENSION_REVERSE_TRANSIT_NHS:
831 zbuf_put(zb, extpl.head, len);
996c9314
LB
832 if ((type == NHRP_EXTENSION_REVERSE_TRANSIT_NHS)
833 == (packet_types[hdr->type].type == PACKET_REPLY)) {
2fb975da 834 /* Check NHS list for forwarding loop */
8ba9026b
DS
835 while (nhrp_cie_pull(&extpl, pp->hdr,
836 &cie_nbma,
837 &cie_protocol) != NULL) {
996c9314
LB
838 if (sockunion_same(&p->vc->remote.nbma,
839 &cie_nbma))
2fb975da
TT
840 goto err;
841 }
842 /* Append our selves to the list */
996c9314
LB
843 cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS,
844 &nifp->nbma, &if_ad->addr);
845 if (!cie)
846 goto err;
2fb975da
TT
847 cie->holding_time = htons(if_ad->holdtime);
848 }
849 break;
850 default:
851 if (htons(ext->type) & NHRP_EXTENSION_FLAG_COMPULSORY)
852 /* FIXME: RFC says to just copy, but not
853 * append our selves to the transit NHS list */
854 goto err;
996c9314 855 /* fallthru */
2fb975da
TT
856 case NHRP_EXTENSION_RESPONDER_ADDRESS:
857 /* Supported compulsory extensions, and any
858 * non-compulsory that is not explicitly handled,
859 * should be just copied. */
860 zbuf_copy(zb, &extpl, len);
861 break;
862 }
863 nhrp_ext_complete(zb, dst);
864 }
865
866 nhrp_packet_complete(zb, hdr);
867 nhrp_peer_send(p, zb);
868 zbuf_free(zb);
869 return;
870err:
871 nhrp_packet_debug(pp->pkt, "FWD-FAIL");
872 zbuf_free(zb);
873}
874
875static void nhrp_packet_debug(struct zbuf *zb, const char *dir)
876{
877 char buf[2][SU_ADDRSTRLEN];
878 union sockunion src_nbma, src_proto, dst_proto;
879 struct nhrp_packet_header *hdr;
880 struct zbuf zhdr;
881 int reply;
882
883 if (likely(!(debug_flags & NHRP_DEBUG_COMMON)))
884 return;
885
996c9314 886 zbuf_init(&zhdr, zb->buf, zb->tail - zb->buf, zb->tail - zb->buf);
2fb975da
TT
887 hdr = nhrp_packet_pull(&zhdr, &src_nbma, &src_proto, &dst_proto);
888
0d6f7fd6
DA
889 sockunion2str(&src_proto, buf[0], sizeof(buf[0]));
890 sockunion2str(&dst_proto, buf[1], sizeof(buf[1]));
2fb975da
TT
891
892 reply = packet_types[hdr->type].type == PACKET_REPLY;
996c9314 893 debugf(NHRP_DEBUG_COMMON, "%s %s(%d) %s -> %s", dir,
57b0ac50
A
894 (packet_types[hdr->type].name ? packet_types[hdr->type].name
895 : "Unknown"),
896 hdr->type, reply ? buf[1] : buf[0], reply ? buf[0] : buf[1]);
2fb975da
TT
897}
898
8c01a3bd
TT
899static int proto2afi(uint16_t proto)
900{
901 switch (proto) {
996c9314
LB
902 case ETH_P_IP:
903 return AFI_IP;
904 case ETH_P_IPV6:
905 return AFI_IP6;
8c01a3bd
TT
906 }
907 return AF_UNSPEC;
908}
909
2fb975da
TT
910struct nhrp_route_info {
911 int local;
912 struct interface *ifp;
913 struct nhrp_vc *vc;
914};
915
916void nhrp_peer_recv(struct nhrp_peer *p, struct zbuf *zb)
917{
2fb975da
TT
918 struct nhrp_packet_header *hdr;
919 struct nhrp_vc *vc = p->vc;
920 struct interface *ifp = p->ifp;
921 struct nhrp_interface *nifp = ifp->info;
922 struct nhrp_packet_parser pp;
923 struct nhrp_peer *peer = NULL;
924 struct nhrp_reqid *reqid;
925 const char *info = NULL;
926 union sockunion *target_addr;
927 unsigned paylen, extoff, extlen, realsize;
8c01a3bd 928 afi_t nbma_afi, proto_afi;
2fb975da 929
b6c48481
DS
930 debugf(NHRP_DEBUG_KERNEL, "PACKET: Recv %pSU -> %pSU", &vc->remote.nbma,
931 &vc->local.nbma);
2fb975da
TT
932
933 if (!p->online) {
934 info = "peer not online";
935 goto drop;
936 }
937
938 if (nhrp_packet_calculate_checksum(zb->head, zbuf_used(zb)) != 0) {
939 info = "bad checksum";
940 goto drop;
941 }
942
943 realsize = zbuf_used(zb);
944 hdr = nhrp_packet_pull(zb, &pp.src_nbma, &pp.src_proto, &pp.dst_proto);
945 if (!hdr) {
946 info = "corrupt header";
947 goto drop;
948 }
949
950 pp.ifp = ifp;
951 pp.pkt = zb;
952 pp.hdr = hdr;
953 pp.peer = p;
954
8c01a3bd
TT
955 nbma_afi = htons(hdr->afnum);
956 proto_afi = proto2afi(htons(hdr->protocol_type));
996c9314
LB
957 if (hdr->type > NHRP_PACKET_MAX || hdr->version != NHRP_VERSION_RFC2332
958 || nbma_afi >= AFI_MAX || proto_afi == AF_UNSPEC
959 || packet_types[hdr->type].type == PACKET_UNKNOWN
960 || htons(hdr->packet_size) > realsize) {
961 zlog_info(
b6c48481
DS
962 "From %pSU: error: packet type %d, version %d, AFI %d, proto %x, size %d (real size %d)",
963 &vc->remote.nbma, (int)hdr->type, (int)hdr->version,
964 (int)nbma_afi, (int)htons(hdr->protocol_type),
996c9314 965 (int)htons(hdr->packet_size), (int)realsize);
2fb975da
TT
966 goto drop;
967 }
8c01a3bd 968 pp.if_ad = &((struct nhrp_interface *)ifp->info)->afi[proto_afi];
2fb975da
TT
969
970 extoff = htons(hdr->extension_offset);
971 if (extoff) {
a7051a18
QY
972 assert(zb->head > zb->buf);
973 uint32_t header_offset = zb->head - zb->buf;
47d40757
GN
974 if (extoff >= realsize) {
975 info = "extoff larger than packet";
976 goto drop;
977 }
978 if (extoff < header_offset) {
979 info = "extoff smaller than header offset";
2fb975da
TT
980 goto drop;
981 }
47d40757 982 paylen = extoff - header_offset;
2fb975da
TT
983 } else {
984 paylen = zbuf_used(zb);
985 }
986 zbuf_init(&pp.payload, zbuf_pulln(zb, paylen), paylen, paylen);
987 extlen = zbuf_used(zb);
988 zbuf_init(&pp.extensions, zbuf_pulln(zb, extlen), extlen, extlen);
989
8c01a3bd 990 if (!nifp->afi[proto_afi].network_id) {
2fb975da
TT
991 info = "nhrp not enabled";
992 goto drop;
993 }
994
995 nhrp_packet_debug(zb, "Recv");
996
997 /* FIXME: Check authentication here. This extension needs to be
998 * pre-handled. */
999
1000 /* Figure out if this is local */
996c9314
LB
1001 target_addr = (packet_types[hdr->type].type == PACKET_REPLY)
1002 ? &pp.src_proto
1003 : &pp.dst_proto;
2fb975da
TT
1004
1005 if (sockunion_same(&pp.src_proto, &pp.dst_proto))
1006 pp.route_type = NHRP_ROUTE_LOCAL;
1007 else
996c9314
LB
1008 pp.route_type = nhrp_route_address(pp.ifp, target_addr,
1009 &pp.route_prefix, &peer);
2fb975da
TT
1010
1011 switch (pp.route_type) {
1012 case NHRP_ROUTE_LOCAL:
1013 nhrp_packet_debug(zb, "!LOCAL");
1014 if (packet_types[hdr->type].type == PACKET_REPLY) {
996c9314
LB
1015 reqid = nhrp_reqid_lookup(&nhrp_packet_reqid,
1016 htonl(hdr->u.request_id));
2fb975da
TT
1017 if (reqid) {
1018 reqid->cb(reqid, &pp);
1019 break;
1020 } else {
1021 nhrp_packet_debug(zb, "!UNKNOWN-REQID");
1022 /* FIXME: send error-indication */
1023 }
1024 }
acd738fc 1025 /* fallthru */ /* FIXME: double check, is this correct? */
2fb975da
TT
1026 case NHRP_ROUTE_OFF_NBMA:
1027 if (packet_types[hdr->type].handler) {
1028 packet_types[hdr->type].handler(&pp);
1029 break;
1030 }
1031 break;
1032 case NHRP_ROUTE_NBMA_NEXTHOP:
1033 nhrp_peer_forward(peer, &pp);
1034 break;
1035 case NHRP_ROUTE_BLACKHOLE:
1036 break;
1037 }
1038
1039drop:
1040 if (info) {
b6c48481 1041 zlog_info("From %pSU: error: %s", &vc->remote.nbma, info);
2fb975da 1042 }
996c9314
LB
1043 if (peer)
1044 nhrp_peer_unref(peer);
2fb975da
TT
1045 zbuf_free(zb);
1046}