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