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