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