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