]> git.proxmox.com Git - mirror_frr.git/blame - nhrpd/nhrp_peer.c
doc: Add `show ipv6 rpf X:X::X:X` command to docs
[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"
7fc3f834 20#include "network.h"
2fb975da
TT
21
22#include "nhrpd.h"
23#include "nhrp_protocol.h"
24#include "os.h"
25
bf8d3d6a 26DEFINE_MTYPE_STATIC(NHRPD, NHRP_PEER, "NHRP peer entry");
819dc8bb 27
2fb975da
TT
28struct ipv6hdr {
29 uint8_t priority_version;
30 uint8_t flow_lbl[3];
31 uint16_t payload_len;
32 uint8_t nexthdr;
33 uint8_t hop_limit;
34 struct in6_addr saddr;
35 struct in6_addr daddr;
36};
37
38static void nhrp_packet_debug(struct zbuf *zb, const char *dir);
39
40static void nhrp_peer_check_delete(struct nhrp_peer *p)
41{
42 struct nhrp_interface *nifp = p->ifp->info;
43
44 if (p->ref || notifier_active(&p->notifier_list))
45 return;
46
b6c48481
DS
47 debugf(NHRP_DEBUG_COMMON, "Deleting peer ref:%d remote:%pSU local:%pSU",
48 p->ref, &p->vc->remote.nbma, &p->vc->local.nbma);
ee72f0a0 49
2fb975da 50 THREAD_OFF(p->t_fallback);
bb58f442 51 THREAD_OFF(p->t_timer);
2fb975da
TT
52 hash_release(nifp->peer_hash, p);
53 nhrp_interface_notify_del(p->ifp, &p->ifp_notifier);
54 nhrp_vc_notify_del(p->vc, &p->vc_notifier);
55 XFREE(MTYPE_NHRP_PEER, p);
56}
57
cc9f21da 58static void nhrp_peer_notify_up(struct thread *t)
2fb975da
TT
59{
60 struct nhrp_peer *p = THREAD_ARG(t);
61 struct nhrp_vc *vc = p->vc;
62 struct interface *ifp = p->ifp;
63 struct nhrp_interface *nifp = ifp->info;
64
65 p->t_fallback = NULL;
66 if (nifp->enabled && (!nifp->ipsec_profile || vc->ipsec)) {
67 p->online = 1;
68 nhrp_peer_ref(p);
69 notifier_call(&p->notifier_list, NOTIFY_PEER_UP);
70 nhrp_peer_unref(p);
71 }
2fb975da
TT
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;
58ef1668
GG
150 case NOTIFY_INTERFACE_IPSEC_CHANGED:
151 __nhrp_peer_check(p);
152 notifier_call(&p->notifier_list, NOTIFY_PEER_IFCONFIG_CHANGED);
153 break;
2fb975da
TT
154 case NOTIFY_INTERFACE_MTU_CHANGED:
155 notifier_call(&p->notifier_list, NOTIFY_PEER_MTU_CHANGED);
156 break;
157 }
158 nhrp_peer_unref(p);
159}
160
d8b87afe 161static unsigned int nhrp_peer_key(const void *peer_data)
2fb975da 162{
d8b87afe 163 const struct nhrp_peer *p = peer_data;
2fb975da
TT
164 return sockunion_hash(&p->vc->remote.nbma);
165}
166
74df8d6d 167static bool nhrp_peer_cmp(const void *cache_data, const void *key_data)
2fb975da
TT
168{
169 const struct nhrp_peer *a = cache_data;
170 const struct nhrp_peer *b = key_data;
74df8d6d 171
2fb975da
TT
172 return a->ifp == b->ifp && a->vc == b->vc;
173}
174
175static void *nhrp_peer_create(void *data)
176{
177 struct nhrp_peer *p, *key = data;
178
179 p = XMALLOC(MTYPE_NHRP_PEER, sizeof(*p));
0ce1ca80
DS
180
181 *p = (struct nhrp_peer){
182 .ref = 0,
183 .ifp = key->ifp,
184 .vc = key->vc,
611915ae 185 .notifier_list = NOTIFIER_LIST_INITIALIZER(&p->notifier_list),
0ce1ca80
DS
186 };
187 nhrp_vc_notify_add(p->vc, &p->vc_notifier, nhrp_peer_vc_notify);
188 nhrp_interface_notify_add(p->ifp, &p->ifp_notifier,
189 nhrp_peer_ifp_notify);
190
2fb975da
TT
191 return p;
192}
193
850b2b70 194static void do_peer_hash_free(void *hb_data)
ee72f0a0 195{
850b2b70
PG
196 struct nhrp_peer *p = (struct nhrp_peer *)hb_data;
197
ee72f0a0
RD
198 nhrp_peer_check_delete(p);
199}
200
201void nhrp_peer_interface_del(struct interface *ifp)
202{
203 struct nhrp_interface *nifp = ifp->info;
204
205 debugf(NHRP_DEBUG_COMMON, "Cleaning up undeleted peer entries (%lu)",
206 nifp->peer_hash ? nifp->peer_hash->count : 0);
207
208 if (nifp->peer_hash) {
850b2b70 209 hash_clean(nifp->peer_hash, do_peer_hash_free);
ee72f0a0
RD
210 assert(nifp->peer_hash->count == 0);
211 hash_free(nifp->peer_hash);
850b2b70 212 nifp->peer_hash = NULL;
ee72f0a0
RD
213 }
214}
215
996c9314
LB
216struct nhrp_peer *nhrp_peer_get(struct interface *ifp,
217 const union sockunion *remote_nbma)
2fb975da
TT
218{
219 struct nhrp_interface *nifp = ifp->info;
220 struct nhrp_peer key, *p;
221 struct nhrp_vc *vc;
222
223 if (!nifp->peer_hash) {
996c9314 224 nifp->peer_hash = hash_create(nhrp_peer_key, nhrp_peer_cmp,
8462c0ff 225 "NHRP Peer Hash");
996c9314
LB
226 if (!nifp->peer_hash)
227 return NULL;
2fb975da
TT
228 }
229
230 vc = nhrp_vc_get(&nifp->nbma, remote_nbma, 1);
996c9314
LB
231 if (!vc)
232 return NULL;
2fb975da
TT
233
234 key.ifp = ifp;
235 key.vc = vc;
236
237 p = hash_get(nifp->peer_hash, &key, nhrp_peer_create);
238 nhrp_peer_ref(p);
996c9314
LB
239 if (p->ref == 1)
240 __nhrp_peer_check(p);
2fb975da
TT
241
242 return p;
243}
244
245struct nhrp_peer *nhrp_peer_ref(struct nhrp_peer *p)
246{
996c9314
LB
247 if (p)
248 p->ref++;
2fb975da
TT
249 return p;
250}
251
252void nhrp_peer_unref(struct nhrp_peer *p)
253{
254 if (p) {
255 p->ref--;
256 nhrp_peer_check_delete(p);
257 }
258}
259
cc9f21da 260static void nhrp_peer_request_timeout(struct thread *t)
2fb975da
TT
261{
262 struct nhrp_peer *p = THREAD_ARG(t);
263 struct nhrp_vc *vc = p->vc;
264 struct interface *ifp = p->ifp;
265 struct nhrp_interface *nifp = ifp->info;
266
2fb975da
TT
267
268 if (p->online)
cc9f21da 269 return;
2fb975da 270
996c9314
LB
271 if (nifp->ipsec_fallback_profile && !p->prio
272 && !p->fallback_requested) {
2fb975da 273 p->fallback_requested = 1;
996c9314
LB
274 vici_request_vc(nifp->ipsec_fallback_profile, &vc->local.nbma,
275 &vc->remote.nbma, p->prio);
ffa2c898
QY
276 thread_add_timer(master, nhrp_peer_request_timeout, p, 30,
277 &p->t_fallback);
2fb975da
TT
278 } else {
279 p->requested = p->fallback_requested = 0;
280 }
2fb975da
TT
281}
282
cc9f21da 283static void nhrp_peer_defer_vici_request(struct thread *t)
bb58f442
GG
284{
285 struct nhrp_peer *p = THREAD_ARG(t);
286 struct nhrp_vc *vc = p->vc;
287 struct interface *ifp = p->ifp;
288 struct nhrp_interface *nifp = ifp->info;
00683a14 289
bb58f442
GG
290 THREAD_OFF(p->t_timer);
291
611915ae
AL
292 if (p->online) {
293 debugf(NHRP_DEBUG_COMMON,
6baf035c 294 "IPsec connection to %pSU already established",
4ddc702a 295 &vc->remote.nbma);
611915ae
AL
296 } else {
297 vici_request_vc(nifp->ipsec_profile, &vc->local.nbma,
298 &vc->remote.nbma, p->prio);
299 thread_add_timer(
300 master, nhrp_peer_request_timeout, p,
301 (nifp->ipsec_fallback_profile && !p->prio) ? 15 : 30,
302 &p->t_fallback);
bb58f442 303 }
bb58f442
GG
304}
305
2fb975da
TT
306int nhrp_peer_check(struct nhrp_peer *p, int establish)
307{
308 struct nhrp_vc *vc = p->vc;
309 struct interface *ifp = p->ifp;
310 struct nhrp_interface *nifp = ifp->info;
311
312 if (p->online)
313 return 1;
314 if (!establish)
315 return 0;
316 if (p->requested)
317 return 0;
8ec0c3c1
TT
318 if (!nifp->ipsec_profile)
319 return 0;
2fb975da
TT
320 if (sockunion_family(&vc->local.nbma) == AF_UNSPEC)
321 return 0;
6c9ca587
GG
322 if (vc->ipsec)
323 return 1;
2fb975da
TT
324
325 p->prio = establish > 1;
326 p->requested = 1;
bb58f442 327
611915ae
AL
328 /* All NHRP registration requests are prioritized */
329 if (p->prio) {
330 vici_request_vc(nifp->ipsec_profile, &vc->local.nbma,
331 &vc->remote.nbma, p->prio);
332 thread_add_timer(
333 master, nhrp_peer_request_timeout, p,
334 (nifp->ipsec_fallback_profile && !p->prio) ? 15 : 30,
335 &p->t_fallback);
336 } else {
00683a14 337 /* Maximum timeout is 1 second */
7fc3f834 338 int r_time_ms = frr_weak_random() % 1000;
00683a14 339
611915ae 340 debugf(NHRP_DEBUG_COMMON,
6baf035c 341 "Initiating IPsec connection request to %pSU after %d ms:",
4ddc702a 342 &vc->remote.nbma, r_time_ms);
611915ae
AL
343 thread_add_timer_msec(master, nhrp_peer_defer_vici_request,
344 p, r_time_ms, &p->t_timer);
bb58f442 345 }
2fb975da
TT
346
347 return 0;
348}
349
996c9314
LB
350void nhrp_peer_notify_add(struct nhrp_peer *p, struct notifier_block *n,
351 notifier_fn_t fn)
2fb975da
TT
352{
353 notifier_add(n, &p->notifier_list, fn);
354}
355
356void nhrp_peer_notify_del(struct nhrp_peer *p, struct notifier_block *n)
357{
865bf787 358 notifier_del(n, &p->notifier_list);
2fb975da
TT
359 nhrp_peer_check_delete(p);
360}
361
362void nhrp_peer_send(struct nhrp_peer *p, struct zbuf *zb)
363{
2fb975da
TT
364 nhrp_packet_debug(zb, "Send");
365
366 if (!p->online)
367 return;
368
b6c48481
DS
369 debugf(NHRP_DEBUG_KERNEL, "PACKET: Send %pSU -> %pSU",
370 &p->vc->local.nbma, &p->vc->remote.nbma);
2fb975da 371
996c9314
LB
372 os_sendmsg(zb->head, zbuf_used(zb), p->ifp->ifindex,
373 sockunion_get_addr(&p->vc->remote.nbma),
0f8595a9 374 sockunion_get_addrlen(&p->vc->remote.nbma), ETH_P_NHRP);
2fb975da
TT
375 zbuf_reset(zb);
376}
377
611915ae
AL
378static void nhrp_process_nat_extension(struct nhrp_packet_parser *pp,
379 union sockunion *proto,
380 union sockunion *cie_nbma)
bb58f442 381{
bb58f442
GG
382 union sockunion cie_proto;
383 struct zbuf payload;
384 struct nhrp_extension_header *ext;
385 struct zbuf *extensions;
386
9025515c 387 if (!cie_nbma)
bb58f442
GG
388 return;
389
390 sockunion_family(cie_nbma) = AF_UNSPEC;
391
9025515c
RD
392 if (!proto || sockunion_family(proto) == AF_UNSPEC)
393 return;
394
bb58f442
GG
395 /* Handle extensions */
396 extensions = zbuf_alloc(zbuf_used(&pp->extensions));
611915ae
AL
397 if (extensions) {
398 zbuf_copy_peek(extensions, &pp->extensions,
399 zbuf_used(&pp->extensions));
bb58f442 400 while ((ext = nhrp_ext_pull(extensions, &payload)) != NULL) {
611915ae
AL
401 switch (htons(ext->type)
402 & ~NHRP_EXTENSION_FLAG_COMPULSORY) {
bb58f442 403 case NHRP_EXTENSION_NAT_ADDRESS:
611915ae
AL
404 /* Process the NBMA and proto address in NAT
405 * extension and update the cache without which
406 * the neighbor table in the kernel contains the
407 * source NBMA address which is not reachable
00683a14
RD
408 * since it is behind a NAT device
409 */
611915ae 410 debugf(NHRP_DEBUG_COMMON,
0551aead 411 "shortcut res_resp: Processing NAT Extension for %pSU",
4ddc702a 412 proto);
611915ae
AL
413 while (nhrp_cie_pull(&payload, pp->hdr,
414 cie_nbma, &cie_proto)) {
415 if (sockunion_family(&cie_proto)
416 == AF_UNSPEC)
bb58f442
GG
417 continue;
418
611915ae
AL
419 if (!sockunion_cmp(proto, &cie_proto)) {
420 debugf(NHRP_DEBUG_COMMON,
2a1fa50e 421 "cie_nbma for proto %pSU is %pSU",
4ddc702a 422 proto, cie_nbma);
bb58f442
GG
423 break;
424 }
425 }
426 }
427 }
428 zbuf_free(extensions);
429 }
430}
431
47d40757 432static void nhrp_handle_resolution_req(struct nhrp_packet_parser *pp)
2fb975da 433{
47d40757 434 struct interface *ifp = pp->ifp;
2fb975da
TT
435 struct zbuf *zb, payload;
436 struct nhrp_packet_header *hdr;
437 struct nhrp_cie_header *cie;
438 struct nhrp_extension_header *ext;
47d40757 439 struct nhrp_cache *c;
611915ae
AL
440 union sockunion cie_nbma, cie_nbma_nat, cie_proto, *proto_addr,
441 *nbma_addr, *claimed_nbma_addr;
47d40757
GN
442 int holdtime, prefix_len, hostprefix_len;
443 struct nhrp_interface *nifp = ifp->info;
2fb975da 444 struct nhrp_peer *peer;
47d40757 445 size_t paylen;
2fb975da 446
47d40757 447 if (!(pp->if_ad->flags & NHRP_IFF_SHORTCUT)) {
2fb975da
TT
448 debugf(NHRP_DEBUG_COMMON, "Shortcuts disabled");
449 /* FIXME: Send error indication? */
450 return;
451 }
452
47d40757
GN
453 if (pp->if_ad->network_id && pp->route_type == NHRP_ROUTE_OFF_NBMA
454 && pp->route_prefix.prefixlen < 8) {
996c9314
LB
455 debugf(NHRP_DEBUG_COMMON,
456 "Shortcut to more generic than /8 dropped");
2fb975da
TT
457 return;
458 }
459
460 debugf(NHRP_DEBUG_COMMON, "Parsing and replying to Resolution Req");
461
47d40757 462 if (nhrp_route_address(ifp, &pp->src_proto, NULL, &peer)
996c9314 463 != NHRP_ROUTE_NBMA_NEXTHOP)
2fb975da
TT
464 return;
465
47d40757
GN
466 /* Copy payload CIE */
467 hostprefix_len = 8 * sockunion_get_addrlen(&pp->if_ad->addr);
468 paylen = zbuf_used(&pp->payload);
469 debugf(NHRP_DEBUG_COMMON, "shortcut res_rep: paylen %zu", paylen);
470
471 while ((cie = nhrp_cie_pull(&pp->payload, pp->hdr, &cie_nbma,
472 &cie_proto))
473 != NULL) {
474 prefix_len = cie->prefix_length;
475 debugf(NHRP_DEBUG_COMMON,
5f36c26c 476 "shortcut res_rep: parsing CIE with prefixlen=%u",
47d40757
GN
477 prefix_len);
478 if (prefix_len == 0 || prefix_len >= hostprefix_len)
479 prefix_len = hostprefix_len;
480
481 if (prefix_len != hostprefix_len
482 && !(pp->hdr->flags
483 & htons(NHRP_FLAG_REGISTRATION_UNIQUE))) {
484 cie->code = NHRP_CODE_BINDING_NON_UNIQUE;
485 continue;
486 }
487
488 /* We currently support only unique prefix registrations */
489 if (prefix_len != hostprefix_len) {
490 cie->code = NHRP_CODE_ADMINISTRATIVELY_PROHIBITED;
491 continue;
492 }
493
494 proto_addr = (sockunion_family(&cie_proto) == AF_UNSPEC)
495 ? &pp->src_proto
496 : &cie_proto;
bb58f442 497
00683a14 498 /* Check for this proto_addr in NHRP_NAT_EXTENSION */
bb58f442
GG
499 nhrp_process_nat_extension(pp, proto_addr, &cie_nbma_nat);
500
611915ae
AL
501 if (sockunion_family(&cie_nbma_nat) == AF_UNSPEC) {
502 /* It may be possible that this resolution reply is
503 * coming directly from NATTED Spoke and there is not
00683a14
RD
504 * NAT Extension present
505 */
d0c9d09a
RD
506 debugf(NHRP_DEBUG_COMMON,
507 "shortcut res_rep: No NAT Extension for %pSU",
4ddc702a 508 proto_addr);
611915ae
AL
509
510 if (!sockunion_same(&pp->src_nbma,
511 &pp->peer->vc->remote.nbma)
512 && !nhrp_nhs_match_ip(&pp->peer->vc->remote.nbma,
513 nifp)) {
bb58f442 514 cie_nbma_nat = pp->peer->vc->remote.nbma;
611915ae 515 debugf(NHRP_DEBUG_COMMON,
0551aead 516 "shortcut res_rep: NAT detected using %pSU as cie_nbma",
4ddc702a 517 &cie_nbma_nat);
bb58f442
GG
518 }
519 }
520
611915ae 521 if (sockunion_family(&cie_nbma_nat) != AF_UNSPEC)
bb58f442 522 nbma_addr = &cie_nbma_nat;
611915ae 523 else if (sockunion_family(&cie_nbma) != AF_UNSPEC)
bb58f442
GG
524 nbma_addr = &cie_nbma;
525 else
526 nbma_addr = &pp->src_nbma;
47d40757 527
611915ae 528 if (sockunion_family(&cie_nbma) != AF_UNSPEC)
85365e51
AL
529 claimed_nbma_addr = &cie_nbma;
530 else
531 claimed_nbma_addr = &pp->src_nbma;
47d40757
GN
532
533 holdtime = htons(cie->holding_time);
534 debugf(NHRP_DEBUG_COMMON,
5f36c26c 535 "shortcut res_rep: holdtime is %u (if 0, using %u)",
47d40757
GN
536 holdtime, pp->if_ad->holdtime);
537 if (!holdtime)
538 holdtime = pp->if_ad->holdtime;
539
540 c = nhrp_cache_get(ifp, proto_addr, 1);
541 if (!c) {
542 debugf(NHRP_DEBUG_COMMON,
5f36c26c 543 "shortcut res_rep: no cache found");
47d40757
GN
544 cie->code = NHRP_CODE_INSUFFICIENT_RESOURCES;
545 continue;
546 }
47d40757
GN
547
548 debugf(NHRP_DEBUG_COMMON,
4ddc702a
RD
549 "shortcut res_rep: updating binding for nmba addr %pSU",
550 nbma_addr);
611915ae
AL
551 if (!nhrp_cache_update_binding(
552 c, NHRP_CACHE_DYNAMIC, holdtime,
553 nhrp_peer_get(pp->ifp, nbma_addr), htons(cie->mtu),
554 nbma_addr, claimed_nbma_addr)) {
47d40757
GN
555 cie->code = NHRP_CODE_ADMINISTRATIVELY_PROHIBITED;
556 continue;
557 }
2fb975da 558
47d40757
GN
559 cie->code = NHRP_CODE_SUCCESS;
560 }
2fb975da
TT
561
562 /* Create reply */
563 zb = zbuf_alloc(1500);
47d40757
GN
564 hdr = nhrp_packet_push(zb, NHRP_PACKET_RESOLUTION_REPLY, &pp->src_nbma,
565 &pp->src_proto, &pp->dst_proto);
2fb975da
TT
566
567 /* Copied information from request */
47d40757
GN
568 hdr->flags = pp->hdr->flags
569 & htons(NHRP_FLAG_RESOLUTION_SOURCE_IS_ROUTER
570 | NHRP_FLAG_RESOLUTION_SOURCE_STABLE);
996c9314
LB
571 hdr->flags |= htons(NHRP_FLAG_RESOLUTION_DESTINATION_STABLE
572 | NHRP_FLAG_RESOLUTION_AUTHORATIVE);
47d40757 573 hdr->u.request_id = pp->hdr->u.request_id;
2fb975da 574
47d40757 575 /* CIE payload for the reply packet */
996c9314 576 cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, &nifp->nbma,
47d40757
GN
577 &pp->if_ad->addr);
578 cie->holding_time = htons(pp->if_ad->holdtime);
579 cie->mtu = htons(pp->if_ad->mtu);
580 if (pp->if_ad->network_id && pp->route_type == NHRP_ROUTE_OFF_NBMA)
581 cie->prefix_length = pp->route_prefix.prefixlen;
2fb975da 582 else
47d40757
GN
583 cie->prefix_length =
584 8 * sockunion_get_addrlen(&pp->if_ad->addr);
2fb975da
TT
585
586 /* Handle extensions */
47d40757 587 while ((ext = nhrp_ext_pull(&pp->extensions, &payload)) != NULL) {
2fb975da
TT
588 switch (htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY) {
589 case NHRP_EXTENSION_NAT_ADDRESS:
996c9314
LB
590 ext = nhrp_ext_push(zb, hdr,
591 NHRP_EXTENSION_NAT_ADDRESS);
592 if (!ext)
593 goto err;
5e70e83b
AL
594 if (sockunion_family(&nifp->nat_nbma) != AF_UNSPEC) {
595 cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS,
611915ae
AL
596 &nifp->nat_nbma,
597 &pp->if_ad->addr);
5e70e83b
AL
598 if (!cie)
599 goto err;
e8089de1
AL
600 cie->prefix_length =
601 8 * sockunion_get_addrlen(
602 &pp->if_ad->addr);
603
5e70e83b
AL
604 cie->mtu = htons(pp->if_ad->mtu);
605 nhrp_ext_complete(zb, ext);
606 }
2fb975da
TT
607 break;
608 default:
47d40757 609 if (nhrp_ext_reply(zb, hdr, ifp, ext, &payload) < 0)
2fb975da
TT
610 goto err;
611 break;
612 }
613 }
2fb975da
TT
614 nhrp_packet_complete(zb, hdr);
615 nhrp_peer_send(peer, zb);
616err:
617 nhrp_peer_unref(peer);
618 zbuf_free(zb);
619}
620
621static void nhrp_handle_registration_request(struct nhrp_packet_parser *p)
622{
623 struct interface *ifp = p->ifp;
624 struct zbuf *zb, payload;
625 struct nhrp_packet_header *hdr;
626 struct nhrp_cie_header *cie;
627 struct nhrp_extension_header *ext;
628 struct nhrp_cache *c;
996c9314
LB
629 union sockunion cie_nbma, cie_proto, *proto_addr, *nbma_addr,
630 *nbma_natoa;
55fd6ee9 631 int holdtime, prefix_len, hostprefix_len, natted = 0;
2fb975da
TT
632 size_t paylen;
633 void *pay;
634
635 debugf(NHRP_DEBUG_COMMON, "Parsing and replying to Registration Req");
55fd6ee9 636 hostprefix_len = 8 * sockunion_get_addrlen(&p->if_ad->addr);
2fb975da
TT
637
638 if (!sockunion_same(&p->src_nbma, &p->peer->vc->remote.nbma))
639 natted = 1;
640
641 /* Create reply */
642 zb = zbuf_alloc(1500);
996c9314
LB
643 hdr = nhrp_packet_push(zb, NHRP_PACKET_REGISTRATION_REPLY, &p->src_nbma,
644 &p->src_proto, &p->if_ad->addr);
2fb975da
TT
645
646 /* Copied information from request */
996c9314
LB
647 hdr->flags = p->hdr->flags & htons(NHRP_FLAG_REGISTRATION_UNIQUE
648 | NHRP_FLAG_REGISTRATION_NAT);
2fb975da
TT
649 hdr->u.request_id = p->hdr->u.request_id;
650
651 /* Copy payload CIEs */
652 paylen = zbuf_used(&p->payload);
653 pay = zbuf_pushn(zb, paylen);
996c9314
LB
654 if (!pay)
655 goto err;
2fb975da
TT
656 memcpy(pay, zbuf_pulln(&p->payload, paylen), paylen);
657 zbuf_init(&payload, pay, paylen, paylen);
658
996c9314
LB
659 while ((cie = nhrp_cie_pull(&payload, hdr, &cie_nbma, &cie_proto))
660 != NULL) {
55fd6ee9
TT
661 prefix_len = cie->prefix_length;
662 if (prefix_len == 0 || prefix_len >= hostprefix_len)
663 prefix_len = hostprefix_len;
664
996c9314
LB
665 if (prefix_len != hostprefix_len
666 && !(p->hdr->flags
667 & htons(NHRP_FLAG_REGISTRATION_UNIQUE))) {
2fb975da
TT
668 cie->code = NHRP_CODE_BINDING_NON_UNIQUE;
669 continue;
670 }
671
672 /* We currently support only unique prefix registrations */
55fd6ee9 673 if (prefix_len != hostprefix_len) {
2fb975da
TT
674 cie->code = NHRP_CODE_ADMINISTRATIVELY_PROHIBITED;
675 continue;
676 }
677
996c9314
LB
678 proto_addr = (sockunion_family(&cie_proto) == AF_UNSPEC)
679 ? &p->src_proto
680 : &cie_proto;
681 nbma_addr = (sockunion_family(&cie_nbma) == AF_UNSPEC)
682 ? &p->src_nbma
683 : &cie_nbma;
2fb975da
TT
684 nbma_natoa = NULL;
685 if (natted) {
611915ae
AL
686 nbma_natoa =
687 (sockunion_family(&p->peer->vc->remote.nbma)
688 == AF_UNSPEC)
689 ? nbma_addr
690 : &p->peer->vc->remote.nbma;
2fb975da
TT
691 }
692
693 holdtime = htons(cie->holding_time);
996c9314
LB
694 if (!holdtime)
695 holdtime = p->if_ad->holdtime;
2fb975da
TT
696
697 c = nhrp_cache_get(ifp, proto_addr, 1);
698 if (!c) {
699 cie->code = NHRP_CODE_INSUFFICIENT_RESOURCES;
700 continue;
701 }
702
996c9314
LB
703 if (!nhrp_cache_update_binding(c, NHRP_CACHE_DYNAMIC, holdtime,
704 nhrp_peer_ref(p->peer),
611915ae
AL
705 htons(cie->mtu), nbma_natoa,
706 nbma_addr)) {
2fb975da
TT
707 cie->code = NHRP_CODE_ADMINISTRATIVELY_PROHIBITED;
708 continue;
709 }
710
711 cie->code = NHRP_CODE_SUCCESS;
712 }
713
714 /* Handle extensions */
715 while ((ext = nhrp_ext_pull(&p->extensions, &payload)) != NULL) {
716 switch (htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY) {
717 case NHRP_EXTENSION_NAT_ADDRESS:
996c9314
LB
718 ext = nhrp_ext_push(zb, hdr,
719 NHRP_EXTENSION_NAT_ADDRESS);
720 if (!ext)
721 goto err;
2fb975da
TT
722 zbuf_copy(zb, &payload, zbuf_used(&payload));
723 if (natted) {
1e52c954 724 cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS,
611915ae
AL
725 &p->peer->vc->remote.nbma,
726 &p->src_proto);
727 cie->prefix_length =
728 8 * sockunion_get_addrlen(
729 &p->if_ad->addr);
1e52c954 730 cie->mtu = htons(p->if_ad->mtu);
2fb975da
TT
731 }
732 nhrp_ext_complete(zb, ext);
733 break;
734 default:
735 if (nhrp_ext_reply(zb, hdr, ifp, ext, &payload) < 0)
736 goto err;
737 break;
738 }
739 }
740
741 nhrp_packet_complete(zb, hdr);
742 nhrp_peer_send(p->peer, zb);
743err:
744 zbuf_free(zb);
745}
746
996c9314
LB
747static int parse_ether_packet(struct zbuf *zb, uint16_t protocol_type,
748 union sockunion *src, union sockunion *dst)
2fb975da
TT
749{
750 switch (protocol_type) {
751 case ETH_P_IP: {
996c9314
LB
752 struct iphdr *iph = zbuf_pull(zb, struct iphdr);
753 if (iph) {
754 if (src)
755 sockunion_set(src, AF_INET,
756 (uint8_t *)&iph->saddr,
757 sizeof(iph->saddr));
758 if (dst)
759 sockunion_set(dst, AF_INET,
760 (uint8_t *)&iph->daddr,
761 sizeof(iph->daddr));
2fb975da 762 }
996c9314 763 } break;
2fb975da 764 case ETH_P_IPV6: {
996c9314
LB
765 struct ipv6hdr *iph = zbuf_pull(zb, struct ipv6hdr);
766 if (iph) {
767 if (src)
768 sockunion_set(src, AF_INET6,
769 (uint8_t *)&iph->saddr,
770 sizeof(iph->saddr));
771 if (dst)
772 sockunion_set(dst, AF_INET6,
773 (uint8_t *)&iph->daddr,
774 sizeof(iph->daddr));
2fb975da 775 }
996c9314 776 } break;
2fb975da
TT
777 default:
778 return 0;
779 }
780 return 1;
781}
782
996c9314
LB
783void nhrp_peer_send_indication(struct interface *ifp, uint16_t protocol_type,
784 struct zbuf *pkt)
2fb975da
TT
785{
786 union sockunion dst;
787 struct zbuf *zb, payload;
788 struct nhrp_interface *nifp = ifp->info;
789 struct nhrp_afi_data *if_ad;
790 struct nhrp_packet_header *hdr;
791 struct nhrp_peer *p;
2fb975da 792
996c9314
LB
793 if (!nifp->enabled)
794 return;
2fb975da
TT
795
796 payload = *pkt;
797 if (!parse_ether_packet(&payload, protocol_type, &dst, NULL))
798 return;
799
800 if (nhrp_route_address(ifp, &dst, NULL, &p) != NHRP_ROUTE_NBMA_NEXTHOP)
801 return;
802
803 if_ad = &nifp->afi[family2afi(sockunion_family(&dst))];
804 if (!(if_ad->flags & NHRP_IFF_REDIRECT)) {
996c9314 805 debugf(NHRP_DEBUG_COMMON,
b6c48481
DS
806 "Send Traffic Indication to %pSU about packet to %pSU ignored",
807 &p->vc->remote.nbma, &dst);
2fb975da
TT
808 return;
809 }
810
996c9314 811 debugf(NHRP_DEBUG_COMMON,
b6c48481
DS
812 "Send Traffic Indication to %pSU (online=%d) about packet to %pSU",
813 &p->vc->remote.nbma, p->online, &dst);
2fb975da
TT
814
815 /* Create reply */
816 zb = zbuf_alloc(1500);
996c9314
LB
817 hdr = nhrp_packet_push(zb, NHRP_PACKET_TRAFFIC_INDICATION, &nifp->nbma,
818 &if_ad->addr, &dst);
6f8817b4 819 hdr->hop_count = 1;
2fb975da
TT
820
821 /* Payload is the packet causing indication */
822 zbuf_copy(zb, pkt, zbuf_used(pkt));
823 nhrp_packet_complete(zb, hdr);
824 nhrp_peer_send(p, zb);
825 nhrp_peer_unref(p);
826 zbuf_free(zb);
827}
828
829static void nhrp_handle_error_ind(struct nhrp_packet_parser *pp)
830{
831 struct zbuf origmsg = pp->payload;
832 struct nhrp_packet_header *hdr;
833 struct nhrp_reqid *reqid;
834 union sockunion src_nbma, src_proto, dst_proto;
2fb975da
TT
835
836 hdr = nhrp_packet_pull(&origmsg, &src_nbma, &src_proto, &dst_proto);
996c9314
LB
837 if (!hdr)
838 return;
2fb975da 839
996c9314 840 debugf(NHRP_DEBUG_COMMON,
b6c48481
DS
841 "Error Indication from %pSU about packet to %pSU ignored",
842 &pp->src_proto, &dst_proto);
2fb975da
TT
843
844 reqid = nhrp_reqid_lookup(&nhrp_packet_reqid, htonl(hdr->u.request_id));
845 if (reqid)
846 reqid->cb(reqid, pp);
847}
848
849static void nhrp_handle_traffic_ind(struct nhrp_packet_parser *p)
850{
851 union sockunion dst;
2fb975da 852
996c9314
LB
853 if (!parse_ether_packet(&p->payload, htons(p->hdr->protocol_type), NULL,
854 &dst))
2fb975da
TT
855 return;
856
996c9314 857 debugf(NHRP_DEBUG_COMMON,
b6c48481
DS
858 "Traffic Indication from %pSU about packet to %pSU: %s",
859 &p->src_proto, &dst,
996c9314
LB
860 (p->if_ad->flags & NHRP_IFF_SHORTCUT) ? "trying shortcut"
861 : "ignored");
2fb975da
TT
862
863 if (p->if_ad->flags & NHRP_IFF_SHORTCUT)
864 nhrp_shortcut_initiate(&dst);
865}
866
867enum packet_type_t {
868 PACKET_UNKNOWN = 0,
869 PACKET_REQUEST,
870 PACKET_REPLY,
871 PACKET_INDICATION,
872};
873
47d40757 874static struct {
2fb975da
TT
875 enum packet_type_t type;
876 const char *name;
877 void (*handler)(struct nhrp_packet_parser *);
996c9314
LB
878} packet_types[] = {[0] =
879 {
880 .type = PACKET_UNKNOWN,
881 .name = "UNKNOWN",
882 },
883 [NHRP_PACKET_RESOLUTION_REQUEST] =
884 {
885 .type = PACKET_REQUEST,
886 .name = "Resolution-Request",
887 .handler = nhrp_handle_resolution_req,
888 },
889 [NHRP_PACKET_RESOLUTION_REPLY] =
890 {
891 .type = PACKET_REPLY,
892 .name = "Resolution-Reply",
893 },
894 [NHRP_PACKET_REGISTRATION_REQUEST] =
895 {
896 .type = PACKET_REQUEST,
897 .name = "Registration-Request",
898 .handler = nhrp_handle_registration_request,
899 },
900 [NHRP_PACKET_REGISTRATION_REPLY] =
901 {
902 .type = PACKET_REPLY,
903 .name = "Registration-Reply",
904 },
905 [NHRP_PACKET_PURGE_REQUEST] =
906 {
907 .type = PACKET_REQUEST,
908 .name = "Purge-Request",
909 },
910 [NHRP_PACKET_PURGE_REPLY] =
911 {
912 .type = PACKET_REPLY,
913 .name = "Purge-Reply",
914 },
915 [NHRP_PACKET_ERROR_INDICATION] =
916 {
917 .type = PACKET_INDICATION,
918 .name = "Error-Indication",
919 .handler = nhrp_handle_error_ind,
920 },
921 [NHRP_PACKET_TRAFFIC_INDICATION] = {
922 .type = PACKET_INDICATION,
923 .name = "Traffic-Indication",
924 .handler = nhrp_handle_traffic_ind,
925 }};
926
927static void nhrp_peer_forward(struct nhrp_peer *p,
928 struct nhrp_packet_parser *pp)
2fb975da 929{
b5fc78c0 930 struct zbuf *zb, *zb_copy, extpl;
2fb975da
TT
931 struct nhrp_packet_header *hdr;
932 struct nhrp_extension_header *ext, *dst;
933 struct nhrp_cie_header *cie;
934 struct nhrp_interface *nifp = pp->ifp->info;
935 struct nhrp_afi_data *if_ad = pp->if_ad;
d0c9d09a 936 union sockunion cie_nbma, cie_protocol, cie_protocol_mandatory, *proto;
2fb975da 937 uint16_t type, len;
d0c9d09a 938 struct nhrp_cache *c;
2fb975da
TT
939
940 if (pp->hdr->hop_count == 0)
941 return;
942
943 /* Create forward packet - copy header */
944 zb = zbuf_alloc(1500);
b5fc78c0
GG
945 zb_copy = zbuf_alloc(1500);
946
996c9314
LB
947 hdr = nhrp_packet_push(zb, pp->hdr->type, &pp->src_nbma, &pp->src_proto,
948 &pp->dst_proto);
2fb975da
TT
949 hdr->flags = pp->hdr->flags;
950 hdr->hop_count = pp->hdr->hop_count - 1;
951 hdr->u.request_id = pp->hdr->u.request_id;
952
953 /* Copy payload */
b5fc78c0 954 zbuf_copy_peek(zb_copy, &pp->payload, zbuf_used(&pp->payload));
2fb975da
TT
955 zbuf_copy(zb, &pp->payload, zbuf_used(&pp->payload));
956
b5fc78c0
GG
957 /* Get CIE Extension from Mandatory part */
958 sockunion_family(&cie_protocol_mandatory) = AF_UNSPEC;
959 nhrp_cie_pull(zb_copy, pp->hdr, &cie_nbma, &cie_protocol_mandatory);
960
2fb975da
TT
961 /* Copy extensions */
962 while ((ext = nhrp_ext_pull(&pp->extensions, &extpl)) != NULL) {
963 type = htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY;
964 len = htons(ext->length);
965
966 if (type == NHRP_EXTENSION_END)
967 break;
968
969 dst = nhrp_ext_push(zb, hdr, htons(ext->type));
996c9314
LB
970 if (!dst)
971 goto err;
2fb975da
TT
972
973 switch (type) {
974 case NHRP_EXTENSION_FORWARD_TRANSIT_NHS:
975 case NHRP_EXTENSION_REVERSE_TRANSIT_NHS:
976 zbuf_put(zb, extpl.head, len);
996c9314
LB
977 if ((type == NHRP_EXTENSION_REVERSE_TRANSIT_NHS)
978 == (packet_types[hdr->type].type == PACKET_REPLY)) {
2fb975da 979 /* Check NHS list for forwarding loop */
8ba9026b
DS
980 while (nhrp_cie_pull(&extpl, pp->hdr,
981 &cie_nbma,
982 &cie_protocol) != NULL) {
996c9314
LB
983 if (sockunion_same(&p->vc->remote.nbma,
984 &cie_nbma))
2fb975da
TT
985 goto err;
986 }
987 /* Append our selves to the list */
996c9314
LB
988 cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS,
989 &nifp->nbma, &if_ad->addr);
990 if (!cie)
991 goto err;
1e52c954 992 cie->mtu = htons(if_ad->mtu);
2fb975da
TT
993 cie->holding_time = htons(if_ad->holdtime);
994 }
995 break;
b5fc78c0 996 case NHRP_EXTENSION_NAT_ADDRESS:
d0c9d09a
RD
997 c = NULL;
998 proto = NULL;
999
1000 /* If NAT extension is empty then attempt to populate
1001 * it with cached NBMA information
1002 */
1003 if (len == 0) {
611915ae
AL
1004 if (packet_types[hdr->type].type
1005 == PACKET_REQUEST) {
1006 debugf(NHRP_DEBUG_COMMON,
1007 "Processing NHRP_EXTENSION_NAT_ADDRESS while forwarding the request packet");
1bd508da 1008 proto = &pp->src_proto;
611915ae
AL
1009 } else if (packet_types[hdr->type].type
1010 == PACKET_REPLY) {
1011 debugf(NHRP_DEBUG_COMMON,
1012 "Processing NHRP_EXTENSION_NAT_ADDRESS while forwarding the reply packet");
1013 /* For reply packet use protocol
1014 * specified in CIE of mandatory part
00683a14
RD
1015 * for cache lookup
1016 */
611915ae
AL
1017 if (sockunion_family(
1018 &cie_protocol_mandatory)
1019 != AF_UNSPEC)
1bd508da
AL
1020 proto = &cie_protocol_mandatory;
1021 }
d0c9d09a 1022 }
b5fc78c0 1023
d0c9d09a
RD
1024 if (proto) {
1025 debugf(NHRP_DEBUG_COMMON, "Proto is %pSU",
1026 proto);
1027 c = nhrp_cache_get(nifp->ifp, proto, 0);
1028 }
00683a14 1029
d0c9d09a
RD
1030 if (c) {
1031 debugf(NHRP_DEBUG_COMMON,
1032 "c->cur.remote_nbma_natoa is %pSU",
1033 &c->cur.remote_nbma_natoa);
1034 if (sockunion_family(&c->cur.remote_nbma_natoa)
1035 != AF_UNSPEC) {
1036 cie = nhrp_cie_push(
1037 zb,
1038 NHRP_CODE_SUCCESS,
1039 &c->cur.remote_nbma_natoa,
1040 proto);
1041 if (!cie)
1042 goto err;
b5fc78c0 1043 }
d0c9d09a
RD
1044 } else {
1045 if (proto)
1046 debugf(NHRP_DEBUG_COMMON,
1047 "No cache entry for proto %pSU",
1048 proto);
1049 /* Copy existing NAT extension to new packet if
1050 * either it was already not-empty, or we do not
1051 * have valid cache information
1052 */
1053 zbuf_put(zb, extpl.head, len);
b5fc78c0
GG
1054 }
1055 break;
2fb975da
TT
1056 default:
1057 if (htons(ext->type) & NHRP_EXTENSION_FLAG_COMPULSORY)
1058 /* FIXME: RFC says to just copy, but not
00683a14
RD
1059 * append our selves to the transit NHS list
1060 */
2fb975da 1061 goto err;
996c9314 1062 /* fallthru */
2fb975da
TT
1063 case NHRP_EXTENSION_RESPONDER_ADDRESS:
1064 /* Supported compulsory extensions, and any
1065 * non-compulsory that is not explicitly handled,
00683a14
RD
1066 * should be just copied.
1067 */
2fb975da
TT
1068 zbuf_copy(zb, &extpl, len);
1069 break;
1070 }
1071 nhrp_ext_complete(zb, dst);
1072 }
1073
1074 nhrp_packet_complete(zb, hdr);
1075 nhrp_peer_send(p, zb);
1076 zbuf_free(zb);
b5fc78c0 1077 zbuf_free(zb_copy);
2fb975da
TT
1078 return;
1079err:
1080 nhrp_packet_debug(pp->pkt, "FWD-FAIL");
1081 zbuf_free(zb);
ba113ac6 1082 zbuf_free(zb_copy);
2fb975da
TT
1083}
1084
1085static void nhrp_packet_debug(struct zbuf *zb, const char *dir)
1086{
2fb975da
TT
1087 union sockunion src_nbma, src_proto, dst_proto;
1088 struct nhrp_packet_header *hdr;
1089 struct zbuf zhdr;
1090 int reply;
1091
1092 if (likely(!(debug_flags & NHRP_DEBUG_COMMON)))
1093 return;
1094
996c9314 1095 zbuf_init(&zhdr, zb->buf, zb->tail - zb->buf, zb->tail - zb->buf);
2fb975da
TT
1096 hdr = nhrp_packet_pull(&zhdr, &src_nbma, &src_proto, &dst_proto);
1097
2fb975da 1098 reply = packet_types[hdr->type].type == PACKET_REPLY;
47e12884 1099 debugf(NHRP_DEBUG_COMMON, "%s %s(%d) %pSU -> %pSU", dir,
57b0ac50
A
1100 (packet_types[hdr->type].name ? packet_types[hdr->type].name
1101 : "Unknown"),
47e12884
DA
1102 hdr->type, reply ? &dst_proto : &src_proto,
1103 reply ? &src_proto : &dst_proto);
2fb975da
TT
1104}
1105
8c01a3bd
TT
1106static int proto2afi(uint16_t proto)
1107{
1108 switch (proto) {
996c9314
LB
1109 case ETH_P_IP:
1110 return AFI_IP;
1111 case ETH_P_IPV6:
1112 return AFI_IP6;
8c01a3bd
TT
1113 }
1114 return AF_UNSPEC;
1115}
1116
2fb975da
TT
1117struct nhrp_route_info {
1118 int local;
1119 struct interface *ifp;
1120 struct nhrp_vc *vc;
1121};
1122
1123void nhrp_peer_recv(struct nhrp_peer *p, struct zbuf *zb)
1124{
2fb975da
TT
1125 struct nhrp_packet_header *hdr;
1126 struct nhrp_vc *vc = p->vc;
1127 struct interface *ifp = p->ifp;
1128 struct nhrp_interface *nifp = ifp->info;
1129 struct nhrp_packet_parser pp;
1130 struct nhrp_peer *peer = NULL;
1131 struct nhrp_reqid *reqid;
1132 const char *info = NULL;
1133 union sockunion *target_addr;
1134 unsigned paylen, extoff, extlen, realsize;
8c01a3bd 1135 afi_t nbma_afi, proto_afi;
2fb975da 1136
b6c48481
DS
1137 debugf(NHRP_DEBUG_KERNEL, "PACKET: Recv %pSU -> %pSU", &vc->remote.nbma,
1138 &vc->local.nbma);
2fb975da
TT
1139
1140 if (!p->online) {
1141 info = "peer not online";
1142 goto drop;
1143 }
1144
1145 if (nhrp_packet_calculate_checksum(zb->head, zbuf_used(zb)) != 0) {
1146 info = "bad checksum";
1147 goto drop;
1148 }
1149
1150 realsize = zbuf_used(zb);
1151 hdr = nhrp_packet_pull(zb, &pp.src_nbma, &pp.src_proto, &pp.dst_proto);
1152 if (!hdr) {
1153 info = "corrupt header";
1154 goto drop;
1155 }
1156
1157 pp.ifp = ifp;
1158 pp.pkt = zb;
1159 pp.hdr = hdr;
1160 pp.peer = p;
1161
8c01a3bd
TT
1162 nbma_afi = htons(hdr->afnum);
1163 proto_afi = proto2afi(htons(hdr->protocol_type));
996c9314
LB
1164 if (hdr->type > NHRP_PACKET_MAX || hdr->version != NHRP_VERSION_RFC2332
1165 || nbma_afi >= AFI_MAX || proto_afi == AF_UNSPEC
1166 || packet_types[hdr->type].type == PACKET_UNKNOWN
1167 || htons(hdr->packet_size) > realsize) {
1168 zlog_info(
b6c48481
DS
1169 "From %pSU: error: packet type %d, version %d, AFI %d, proto %x, size %d (real size %d)",
1170 &vc->remote.nbma, (int)hdr->type, (int)hdr->version,
1171 (int)nbma_afi, (int)htons(hdr->protocol_type),
996c9314 1172 (int)htons(hdr->packet_size), (int)realsize);
2fb975da
TT
1173 goto drop;
1174 }
8c01a3bd 1175 pp.if_ad = &((struct nhrp_interface *)ifp->info)->afi[proto_afi];
2fb975da
TT
1176
1177 extoff = htons(hdr->extension_offset);
1178 if (extoff) {
a7051a18
QY
1179 assert(zb->head > zb->buf);
1180 uint32_t header_offset = zb->head - zb->buf;
47d40757
GN
1181 if (extoff >= realsize) {
1182 info = "extoff larger than packet";
1183 goto drop;
1184 }
1185 if (extoff < header_offset) {
1186 info = "extoff smaller than header offset";
2fb975da
TT
1187 goto drop;
1188 }
47d40757 1189 paylen = extoff - header_offset;
2fb975da
TT
1190 } else {
1191 paylen = zbuf_used(zb);
1192 }
1193 zbuf_init(&pp.payload, zbuf_pulln(zb, paylen), paylen, paylen);
1194 extlen = zbuf_used(zb);
1195 zbuf_init(&pp.extensions, zbuf_pulln(zb, extlen), extlen, extlen);
1196
8c01a3bd 1197 if (!nifp->afi[proto_afi].network_id) {
2fb975da
TT
1198 info = "nhrp not enabled";
1199 goto drop;
1200 }
1201
1202 nhrp_packet_debug(zb, "Recv");
1203
1204 /* FIXME: Check authentication here. This extension needs to be
1205 * pre-handled. */
1206
1207 /* Figure out if this is local */
996c9314
LB
1208 target_addr = (packet_types[hdr->type].type == PACKET_REPLY)
1209 ? &pp.src_proto
1210 : &pp.dst_proto;
2fb975da
TT
1211
1212 if (sockunion_same(&pp.src_proto, &pp.dst_proto))
1213 pp.route_type = NHRP_ROUTE_LOCAL;
1214 else
996c9314
LB
1215 pp.route_type = nhrp_route_address(pp.ifp, target_addr,
1216 &pp.route_prefix, &peer);
2fb975da
TT
1217
1218 switch (pp.route_type) {
1219 case NHRP_ROUTE_LOCAL:
1220 nhrp_packet_debug(zb, "!LOCAL");
1221 if (packet_types[hdr->type].type == PACKET_REPLY) {
996c9314
LB
1222 reqid = nhrp_reqid_lookup(&nhrp_packet_reqid,
1223 htonl(hdr->u.request_id));
2fb975da
TT
1224 if (reqid) {
1225 reqid->cb(reqid, &pp);
1226 break;
1227 } else {
1228 nhrp_packet_debug(zb, "!UNKNOWN-REQID");
1229 /* FIXME: send error-indication */
1230 }
1231 }
acd738fc 1232 /* fallthru */ /* FIXME: double check, is this correct? */
2fb975da
TT
1233 case NHRP_ROUTE_OFF_NBMA:
1234 if (packet_types[hdr->type].handler) {
1235 packet_types[hdr->type].handler(&pp);
1236 break;
1237 }
1238 break;
1239 case NHRP_ROUTE_NBMA_NEXTHOP:
1240 nhrp_peer_forward(peer, &pp);
1241 break;
1242 case NHRP_ROUTE_BLACKHOLE:
1243 break;
1244 }
1245
1246drop:
1247 if (info) {
b6c48481 1248 zlog_info("From %pSU: error: %s", &vc->remote.nbma, info);
2fb975da 1249 }
996c9314
LB
1250 if (peer)
1251 nhrp_peer_unref(peer);
2fb975da
TT
1252 zbuf_free(zb);
1253}