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