summaryrefslogtreecommitdiff
path: root/nhrp/nhrp_packet.c
diff options
context:
space:
mode:
Diffstat (limited to 'nhrp/nhrp_packet.c')
-rw-r--r--nhrp/nhrp_packet.c1331
1 files changed, 1331 insertions, 0 deletions
diff --git a/nhrp/nhrp_packet.c b/nhrp/nhrp_packet.c
new file mode 100644
index 0000000..f46b481
--- /dev/null
+++ b/nhrp/nhrp_packet.c
@@ -0,0 +1,1331 @@
+/* nhrp_packet.c - NHRP packet marshalling and tranceiving
+ *
+ * Copyright (C) 2007-2009 Timo Teräs <timo.teras@iki.fi>
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 or later as
+ * published by the Free Software Foundation.
+ *
+ * See http://www.gnu.org/ for details.
+ */
+
+#include <malloc.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <netinet/in.h>
+
+#include "libev.h"
+#include "nhrp_common.h"
+#include "nhrp_packet.h"
+#include "nhrp_peer.h"
+#include "nhrp_interface.h"
+
+#define PACKET_RETRIES 6
+#define PACKET_RETRY_INTERVAL 5.0
+
+#define RATE_LIMIT_HASH_SIZE 256
+#define RATE_LIMIT_MAX_TOKENS 4
+#define RATE_LIMIT_SEND_INTERVAL 5.0
+#define RATE_LIMIT_SILENCE 360.0
+#define RATE_LIMIT_PURGE_INTERVAL 600.0
+
+#define MAX_PDU_SIZE 1500
+
+struct nhrp_rate_limit {
+ struct hlist_node hash_entry;
+ struct nhrp_address src;
+ struct nhrp_address dst;
+ ev_tstamp rate_last;
+ int rate_tokens;
+};
+
+static uint32_t request_id = 0;
+static struct list_head pending_requests = LIST_INITIALIZER(pending_requests);
+static struct hlist_head rate_limit_hash[RATE_LIMIT_HASH_SIZE];
+static ev_timer rate_limit_timer;
+static int num_rate_limit_entries = 0;
+
+static void nhrp_packet_xmit_timeout_cb(struct ev_timer *w, int revents);
+static int unmarshall_packet_header(uint8_t **pdu, size_t *pdusize,
+ struct nhrp_packet *packet);
+
+static void nhrp_rate_limit_delete(struct nhrp_rate_limit *rl)
+{
+ hlist_del(&rl->hash_entry);
+ free(rl);
+ num_rate_limit_entries--;
+}
+
+int nhrp_rate_limit_clear(struct nhrp_address *a, int pref)
+{
+ struct nhrp_rate_limit *rl;
+ struct hlist_node *n, *c;
+ int i, ret = 0;
+
+ for (i = 0; i < RATE_LIMIT_HASH_SIZE; i++) {
+ hlist_for_each_entry_safe(rl, c, n, &rate_limit_hash[i],
+ hash_entry) {
+ if (a->type == AF_UNSPEC ||
+ nhrp_address_prefix_cmp(a, &rl->src, pref) == 0 ||
+ nhrp_address_prefix_cmp(a, &rl->dst, pref) == 0) {
+ nhrp_rate_limit_delete(rl);
+ ret++;
+ }
+ }
+ }
+
+ if (num_rate_limit_entries == 0)
+ ev_timer_stop(&rate_limit_timer);
+
+ return ret;
+}
+
+static void prune_rate_limit_entries_cb(struct ev_timer *w, int revents)
+{
+ struct nhrp_rate_limit *rl;
+ struct hlist_node *c, *n;
+ int i;
+
+ for (i = 0; i < RATE_LIMIT_HASH_SIZE; i++) {
+ hlist_for_each_entry_safe(rl, c, n, &rate_limit_hash[i],
+ hash_entry) {
+
+ if (ev_now() > rl->rate_last + 2 * RATE_LIMIT_SILENCE)
+ nhrp_rate_limit_delete(rl);
+ }
+ }
+
+ if (num_rate_limit_entries == 0)
+ ev_timer_stop(&rate_limit_timer);
+}
+
+static struct nhrp_rate_limit *get_rate_limit(struct nhrp_address *src,
+ struct nhrp_address *dst)
+{
+ unsigned int key;
+ struct nhrp_rate_limit *e;
+ struct hlist_node *n;
+
+ key = nhrp_address_hash(src) ^ nhrp_address_hash(dst);
+ key %= RATE_LIMIT_HASH_SIZE;
+
+ hlist_for_each_entry(e, n, &rate_limit_hash[key], hash_entry) {
+ if (nhrp_address_cmp(&e->src, src) == 0 &&
+ nhrp_address_cmp(&e->dst, dst) == 0)
+ return e;
+ }
+
+ e = calloc(1, sizeof(struct nhrp_rate_limit));
+ e->src = *src;
+ e->dst = *dst;
+ hlist_add_head(&e->hash_entry, &rate_limit_hash[key]);
+
+ if (num_rate_limit_entries == 0) {
+ ev_timer_init(&rate_limit_timer, prune_rate_limit_entries_cb,
+ RATE_LIMIT_PURGE_INTERVAL,
+ RATE_LIMIT_PURGE_INTERVAL);
+ ev_timer_start(&rate_limit_timer);
+ }
+
+ num_rate_limit_entries++;
+
+ return e;
+}
+
+static uint16_t nhrp_calculate_checksum(uint8_t *pdu, uint16_t len)
+{
+ uint16_t *pdu16 = (uint16_t *) pdu;
+ uint32_t csum = 0;
+ int i;
+
+ for (i = 0; i < len / 2; i++)
+ csum += pdu16[i];
+ if (len & 1)
+ csum += htons(pdu[len - 1]);
+
+ while (csum & 0xffff0000)
+ csum = (csum & 0xffff) + (csum >> 16);
+
+ return (~csum) & 0xffff;
+}
+
+struct nhrp_buffer *nhrp_buffer_alloc(uint32_t size)
+{
+ struct nhrp_buffer *buf;
+
+ buf = malloc(sizeof(struct nhrp_buffer) + size);
+ buf->length = size;
+
+ return buf;
+}
+
+struct nhrp_buffer *nhrp_buffer_copy(struct nhrp_buffer *buffer)
+{
+ struct nhrp_buffer *copy;
+
+ copy = nhrp_buffer_alloc(buffer->length);
+ memcpy(copy->data, buffer->data, buffer->length);
+ return copy;
+}
+
+int nhrp_buffer_cmp(struct nhrp_buffer *a, struct nhrp_buffer *b)
+{
+ if (a->length > b->length)
+ return 1;
+ if (a->length < b->length)
+ return -1;
+ return memcmp(a->data, b->data, a->length);
+}
+
+void nhrp_buffer_free(struct nhrp_buffer *buffer)
+{
+ free(buffer);
+}
+
+struct nhrp_cie *nhrp_cie_alloc(void)
+{
+ return calloc(1, sizeof(struct nhrp_cie));
+}
+
+void nhrp_cie_free(struct nhrp_cie *cie)
+{
+ free(cie);
+}
+
+void nhrp_cie_reset(struct nhrp_cie *cie)
+{
+ memset(&cie->cie_list_entry, 0, sizeof(cie->cie_list_entry));
+}
+
+void nhrp_payload_free(struct nhrp_payload *payload)
+{
+ struct nhrp_cie *cie, *n;
+
+ switch (payload->payload_type) {
+ case NHRP_PAYLOAD_TYPE_RAW:
+ nhrp_buffer_free(payload->u.raw);
+ break;
+ case NHRP_PAYLOAD_TYPE_CIE_LIST:
+ list_for_each_entry_safe(cie, n, &payload->u.cie_list, cie_list_entry) {
+ list_del(&cie->cie_list_entry);
+ nhrp_cie_free(cie);
+ }
+ break;
+ }
+ payload->payload_type = NHRP_PAYLOAD_TYPE_NONE;
+}
+
+void nhrp_payload_set_type(struct nhrp_payload *payload, int type)
+{
+ if (payload->payload_type == type)
+ return;
+
+ nhrp_payload_free(payload);
+ payload->payload_type = type;
+ switch (type) {
+ case NHRP_PAYLOAD_TYPE_CIE_LIST:
+ list_init(&payload->u.cie_list);
+ break;
+ default:
+ payload->u.raw = NULL;
+ break;
+ }
+}
+
+void nhrp_payload_set_raw(struct nhrp_payload *payload, struct nhrp_buffer *raw)
+{
+ nhrp_payload_set_type(payload, NHRP_PAYLOAD_TYPE_RAW);
+ payload->u.raw = raw;
+}
+
+void nhrp_payload_add_cie(struct nhrp_payload *payload, struct nhrp_cie *cie)
+{
+ if (payload->payload_type != NHRP_PAYLOAD_TYPE_CIE_LIST) {
+ nhrp_cie_free(cie);
+ nhrp_info("Trying to add CIE payload to non-CIE payload %d\n",
+ payload->payload_type);
+ return;
+ }
+
+ list_add_tail(&cie->cie_list_entry, &payload->u.cie_list);
+}
+
+struct nhrp_cie *nhrp_payload_get_cie(struct nhrp_payload *payload, int index)
+{
+ struct nhrp_cie *cie;
+
+ if (payload->payload_type != NHRP_PAYLOAD_TYPE_CIE_LIST)
+ return NULL;
+
+ list_for_each_entry(cie, &payload->u.cie_list, cie_list_entry) {
+ index--;
+ if (index == 0)
+ return cie;
+ }
+
+ return NULL;
+}
+
+struct nhrp_packet *nhrp_packet_alloc(void)
+{
+ struct nhrp_packet *packet;
+ packet = calloc(1, sizeof(struct nhrp_packet));
+ packet->ref = 1;
+ packet->hdr.hop_count = NHRP_PACKET_DEFAULT_HOP_COUNT;
+ list_init(&packet->request_list_entry);
+ ev_timer_init(&packet->timeout, nhrp_packet_xmit_timeout_cb,
+ PACKET_RETRY_INTERVAL, PACKET_RETRY_INTERVAL);
+ return packet;
+}
+
+struct nhrp_packet *nhrp_packet_get(struct nhrp_packet *packet)
+{
+ packet->ref++;
+ return packet;
+}
+
+struct nhrp_payload *nhrp_packet_payload(struct nhrp_packet *packet, int payload_type)
+{
+ return nhrp_packet_extension(packet, NHRP_EXTENSION_PAYLOAD, payload_type);
+}
+
+struct nhrp_payload *nhrp_packet_extension(struct nhrp_packet *packet,
+ uint32_t extension, int payload_type)
+{
+ struct nhrp_payload *p;
+
+ p = packet->extension_by_type[extension & 0x7fff];
+ if (p != NULL) {
+ if (payload_type == NHRP_PAYLOAD_TYPE_ANY ||
+ payload_type == p->payload_type)
+ return p;
+ if (extension & NHRP_EXTENSION_FLAG_NOCREATE)
+ return NULL;
+ nhrp_payload_set_type(p, payload_type);
+ return p;
+ }
+
+ if (extension & NHRP_EXTENSION_FLAG_NOCREATE)
+ return NULL;
+
+ p = &packet->extension_by_order[packet->num_extensions++];
+ p->extension_type = extension & 0xffff;
+ packet->extension_by_type[extension & 0x7fff] = p;
+ if (payload_type != NHRP_PAYLOAD_TYPE_ANY)
+ nhrp_payload_set_type(p, payload_type);
+
+ return p;
+}
+
+static void nhrp_packet_release(struct nhrp_packet *packet)
+{
+ int i;
+
+ if (packet->dst_peer != NULL)
+ nhrp_peer_put(packet->dst_peer);
+ for (i = 0; i < packet->num_extensions; i++)
+ nhrp_payload_free(&packet->extension_by_order[i]);
+ free(packet);
+}
+
+void nhrp_packet_put(struct nhrp_packet *packet)
+{
+ NHRP_BUG_ON(packet->ref == 0);
+
+ packet->ref--;
+ if (packet->ref == 0)
+ nhrp_packet_release(packet);
+}
+
+int nhrp_packet_reroute(struct nhrp_packet *packet, struct nhrp_peer *dst_peer)
+{
+ packet->dst_iface = packet->src_iface;
+ if (packet->dst_peer != NULL)
+ nhrp_peer_put(packet->dst_peer);
+ packet->dst_peer = nhrp_peer_get(dst_peer);
+ return nhrp_packet_route(packet);
+}
+
+static void nhrp_packet_dequeue(struct nhrp_packet *packet)
+{
+ ev_timer_stop(&packet->timeout);
+ if (list_hashed(&packet->request_list_entry))
+ list_del(&packet->request_list_entry);
+ nhrp_packet_put(packet);
+}
+
+static int nhrp_do_handle_error_indication(struct nhrp_packet *error_pkt,
+ struct nhrp_packet *orig_pkt)
+{
+ struct nhrp_packet *req;
+
+ list_for_each_entry(req, &pending_requests, request_list_entry) {
+ if (orig_pkt->hdr.u.request_id != req->hdr.u.request_id)
+ continue;
+
+ if (nhrp_address_cmp(&orig_pkt->src_nbma_address,
+ &req->src_nbma_address))
+ continue;
+ if (nhrp_address_cmp(&orig_pkt->src_protocol_address,
+ &req->src_protocol_address))
+ continue;
+
+ if (req->handler != NULL)
+ req->handler(req->handler_ctx, error_pkt);
+ nhrp_packet_dequeue(req);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static int nhrp_handle_error_indication(struct nhrp_packet *error_packet)
+{
+ struct nhrp_packet *packet;
+ struct nhrp_payload *payload;
+ uint8_t *pdu;
+ size_t pduleft;
+ int r;
+
+ packet = nhrp_packet_alloc();
+ if (packet == NULL)
+ return FALSE;
+
+ payload = nhrp_packet_payload(error_packet, NHRP_PAYLOAD_TYPE_RAW);
+ pdu = payload->u.raw->data;
+ pduleft = payload->u.raw->length;
+
+ if (!unmarshall_packet_header(&pdu, &pduleft, packet)) {
+ nhrp_packet_put(packet);
+ return FALSE;
+ }
+
+ r = nhrp_do_handle_error_indication(error_packet, packet);
+ nhrp_packet_put(packet);
+
+ return r;
+}
+
+#define NHRP_TYPE_REQUEST 0
+#define NHRP_TYPE_REPLY 1
+#define NHRP_TYPE_INDICATION 2
+
+static struct {
+ int type;
+ uint16_t payload_type;
+ int (*handler)(struct nhrp_packet *packet);
+} packet_types[] = {
+ [NHRP_PACKET_RESOLUTION_REQUEST] = {
+ .type = NHRP_TYPE_REQUEST,
+ .payload_type = NHRP_PAYLOAD_TYPE_CIE_LIST,
+ },
+ [NHRP_PACKET_RESOLUTION_REPLY] = {
+ .type = NHRP_TYPE_REPLY,
+ .payload_type = NHRP_PAYLOAD_TYPE_CIE_LIST,
+ },
+ [NHRP_PACKET_REGISTRATION_REQUEST] = {
+ .type = NHRP_TYPE_REQUEST,
+ .payload_type = NHRP_PAYLOAD_TYPE_CIE_LIST,
+ },
+ [NHRP_PACKET_REGISTRATION_REPLY] = {
+ .type = NHRP_TYPE_REPLY,
+ .payload_type = NHRP_PAYLOAD_TYPE_CIE_LIST,
+ },
+ [NHRP_PACKET_PURGE_REQUEST] = {
+ .type = NHRP_TYPE_REQUEST,
+ .payload_type = NHRP_PAYLOAD_TYPE_CIE_LIST,
+ },
+ [NHRP_PACKET_PURGE_REPLY] = {
+ .type = NHRP_TYPE_REPLY,
+ .payload_type = NHRP_PAYLOAD_TYPE_CIE_LIST,
+ },
+ [NHRP_PACKET_ERROR_INDICATION] = {
+ .type = NHRP_TYPE_INDICATION,
+ .payload_type = NHRP_PAYLOAD_TYPE_RAW,
+ .handler = nhrp_handle_error_indication,
+ },
+ [NHRP_PACKET_TRAFFIC_INDICATION] = {
+ .type = NHRP_TYPE_INDICATION,
+ .payload_type = NHRP_PAYLOAD_TYPE_RAW,
+ }
+};
+static int extension_types[] = {
+ [NHRP_EXTENSION_RESPONDER_ADDRESS] = NHRP_PAYLOAD_TYPE_CIE_LIST,
+ [NHRP_EXTENSION_FORWARD_TRANSIT_NHS] = NHRP_PAYLOAD_TYPE_CIE_LIST,
+ [NHRP_EXTENSION_REVERSE_TRANSIT_NHS] = NHRP_PAYLOAD_TYPE_CIE_LIST,
+ [NHRP_EXTENSION_NAT_ADDRESS] = NHRP_PAYLOAD_TYPE_CIE_LIST
+};
+
+static int unmarshall_binary(uint8_t **pdu, size_t *pduleft, size_t size, void *raw)
+{
+ if (*pduleft < size)
+ return FALSE;
+
+ memcpy(raw, *pdu, size);
+ *pdu += size;
+ *pduleft -= size;
+ return TRUE;
+}
+
+static inline int unmarshall_protocol_address(uint8_t **pdu, size_t *pduleft, struct nhrp_address *pa)
+{
+ if (*pduleft < pa->addr_len)
+ return FALSE;
+
+ if (pa->addr_len) {
+ if (!nhrp_address_set(pa, pa->type, pa->addr_len, *pdu))
+ return FALSE;
+ } else {
+ nhrp_address_set_type(pa, PF_UNSPEC);
+ }
+
+ *pdu += pa->addr_len;
+ *pduleft -= pa->addr_len;
+ return TRUE;
+}
+
+static inline int unmarshall_nbma_address(uint8_t **pdu, size_t *pduleft, struct nhrp_address *na)
+{
+ if (*pduleft < na->addr_len + na->subaddr_len)
+ return FALSE;
+
+ if (na->addr_len || na->subaddr_len) {
+ if (!nhrp_address_set_full(na, na->type,
+ na->addr_len, *pdu,
+ na->subaddr_len, *pdu + na->addr_len))
+ return FALSE;
+ } else {
+ nhrp_address_set_type(na, PF_UNSPEC);
+ }
+
+ *pdu += na->addr_len + na->subaddr_len;
+ *pduleft -= na->addr_len + na->subaddr_len;
+ return TRUE;
+}
+
+static int unmarshall_cie(uint8_t **pdu, size_t *pduleft, struct nhrp_packet *p, struct nhrp_cie *cie)
+{
+ if (!unmarshall_binary(pdu, pduleft, sizeof(struct nhrp_cie_header), &cie->hdr))
+ return FALSE;
+
+ cie->nbma_address.type = nhrp_pf_from_afnum(p->hdr.afnum);
+ cie->nbma_address.addr_len = cie->hdr.nbma_address_len;
+ cie->nbma_address.subaddr_len = cie->hdr.nbma_subaddress_len;
+ cie->protocol_address.type = nhrp_pf_from_protocol(p->hdr.protocol_type);
+ cie->protocol_address.addr_len = cie->hdr.protocol_address_len;
+
+ if (!unmarshall_nbma_address(pdu, pduleft, &cie->nbma_address))
+ return FALSE;
+ return unmarshall_protocol_address(pdu, pduleft, &cie->protocol_address);
+}
+
+static int unmarshall_payload(uint8_t **pdu, size_t *pduleft,
+ struct nhrp_packet *packet,
+ int type, size_t size,
+ struct nhrp_payload *p)
+{
+ struct nhrp_cie *cie;
+ size_t cieleft;
+
+ if (*pduleft < size)
+ return FALSE;
+
+ nhrp_payload_set_type(p, type);
+ switch (p->payload_type) {
+ case NHRP_PAYLOAD_TYPE_NONE:
+ *pdu += size;
+ *pduleft -= size;
+ return TRUE;
+ case NHRP_PAYLOAD_TYPE_RAW:
+ p->u.raw = nhrp_buffer_alloc(size);
+ return unmarshall_binary(pdu, pduleft, size, p->u.raw->data);
+ case NHRP_PAYLOAD_TYPE_CIE_LIST:
+ cieleft = size;
+ while (cieleft) {
+ cie = nhrp_cie_alloc();
+ list_add_tail(&cie->cie_list_entry, &p->u.cie_list);
+ if (!unmarshall_cie(pdu, &cieleft, packet, cie))
+ return FALSE;
+ }
+ *pduleft -= size;
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+static int unmarshall_packet_header(uint8_t **pdu, size_t *pduleft, struct nhrp_packet *packet)
+{
+ struct nhrp_packet_header *phdr = (struct nhrp_packet_header *) *pdu;
+
+ if (!unmarshall_binary(pdu, pduleft, sizeof(packet->hdr), &packet->hdr))
+ return FALSE;
+
+ if (packet->hdr.type >= ARRAY_SIZE(packet_types))
+ return FALSE;
+
+ packet->src_nbma_address.type = nhrp_pf_from_afnum(packet->hdr.afnum);
+ packet->src_nbma_address.addr_len = phdr->src_nbma_address_len;
+ packet->src_nbma_address.subaddr_len = phdr->src_nbma_subaddress_len;
+ packet->src_protocol_address.type = nhrp_pf_from_protocol(packet->hdr.protocol_type);
+ packet->src_protocol_address.addr_len = phdr->src_protocol_address_len;
+ packet->dst_protocol_address.type = nhrp_pf_from_protocol(packet->hdr.protocol_type);
+ packet->dst_protocol_address.addr_len = phdr->dst_protocol_address_len;
+
+ if (!unmarshall_nbma_address(pdu, pduleft, &packet->src_nbma_address))
+ return FALSE;
+ if (!unmarshall_protocol_address(pdu, pduleft, &packet->src_protocol_address))
+ return FALSE;
+ return unmarshall_protocol_address(pdu, pduleft, &packet->dst_protocol_address);
+}
+
+static int unmarshall_packet(uint8_t *pdu, size_t pdusize, struct nhrp_packet *packet)
+{
+ size_t pduleft = pdusize;
+ uint8_t *pos = pdu;
+ int size, extension_offset;
+
+ if (!unmarshall_packet_header(&pos, &pduleft, packet))
+ return FALSE;
+
+ extension_offset = ntohs(packet->hdr.extension_offset);
+ if (extension_offset == 0) {
+ /* No extensions; rest of data is payload */
+ size = pduleft;
+ } else {
+ /* Extensions present; exclude those from payload */
+ size = extension_offset - (pos - pdu);
+ if (size < 0 || size > pduleft) {
+ nhrp_packet_send_error(packet, NHRP_ERROR_PROTOCOL_ERROR, pos - pdu);
+ return FALSE;
+ }
+ }
+
+ if (!unmarshall_payload(&pos, &pduleft, packet,
+ packet_types[packet->hdr.type].payload_type,
+ size, nhrp_packet_payload(packet, NHRP_PAYLOAD_TYPE_ANY))) {
+ nhrp_packet_send_error(packet, NHRP_ERROR_PROTOCOL_ERROR, pos - pdu);
+ return FALSE;
+ }
+
+ if (extension_offset == 0)
+ return TRUE;
+
+ pos = &pdu[extension_offset];
+ pduleft = pdusize - extension_offset;
+ do {
+ struct nhrp_extension_header eh;
+ int extension_type, payload_type;
+
+ if (!unmarshall_binary(&pos, &pduleft, sizeof(eh), &eh)) {
+ nhrp_packet_send_error(packet, NHRP_ERROR_PROTOCOL_ERROR, pos - pdu);
+ return FALSE;
+ }
+
+ extension_type = ntohs(eh.type) & ~NHRP_EXTENSION_FLAG_COMPULSORY;
+ if (extension_type == NHRP_EXTENSION_END)
+ break;
+
+ payload_type = NHRP_PAYLOAD_TYPE_NONE;
+ if (extension_type < ARRAY_SIZE(extension_types))
+ payload_type = extension_types[extension_type];
+ if (payload_type == NHRP_PAYLOAD_TYPE_NONE)
+ payload_type = NHRP_PAYLOAD_TYPE_RAW;
+ if (payload_type == NHRP_PAYLOAD_TYPE_RAW &&
+ ntohs(eh.length) == 0)
+ payload_type = NHRP_PAYLOAD_TYPE_NONE;
+
+ if (!unmarshall_payload(&pos, &pduleft, packet,
+ payload_type, ntohs(eh.length),
+ nhrp_packet_extension(packet, ntohs(eh.type), NHRP_PAYLOAD_TYPE_ANY))) {
+ nhrp_packet_send_error(packet, NHRP_ERROR_PROTOCOL_ERROR, pos - pdu);
+ return FALSE;
+ }
+ } while (1);
+
+ return TRUE;
+}
+
+static int nhrp_packet_forward(struct nhrp_packet *packet)
+{
+ char tmp[64], tmp2[64], tmp3[64];
+ struct nhrp_payload *p = NULL;
+
+ nhrp_info("Forwarding packet from nbma src %s, proto src %s to proto dst %s, hop count %d",
+ nhrp_address_format(&packet->src_nbma_address,
+ sizeof(tmp), tmp),
+ nhrp_address_format(&packet->src_protocol_address,
+ sizeof(tmp2), tmp2),
+ nhrp_address_format(&packet->dst_protocol_address,
+ sizeof(tmp3), tmp3),
+ packet->hdr.hop_count);
+
+ if (packet->hdr.hop_count == 0) {
+ nhrp_packet_send_error(packet, NHRP_ERROR_HOP_COUNT_EXCEEDED, 0);
+ return TRUE;
+ }
+ packet->hdr.hop_count--;
+
+ if (!nhrp_packet_reroute(packet, NULL)) {
+ nhrp_packet_send_error(packet, NHRP_ERROR_PROTOCOL_ADDRESS_UNREACHABLE, 0);
+ return FALSE;
+ }
+
+ switch (packet_types[packet->hdr.type].type) {
+ case NHRP_TYPE_REQUEST:
+ case NHRP_TYPE_INDICATION:
+ p = nhrp_packet_extension(packet,
+ NHRP_EXTENSION_FORWARD_TRANSIT_NHS |
+ NHRP_EXTENSION_FLAG_NOCREATE,
+ NHRP_PAYLOAD_TYPE_CIE_LIST);
+ break;
+ case NHRP_TYPE_REPLY:
+ p = nhrp_packet_extension(packet,
+ NHRP_EXTENSION_REVERSE_TRANSIT_NHS |
+ NHRP_EXTENSION_FLAG_NOCREATE,
+ NHRP_PAYLOAD_TYPE_CIE_LIST);
+ break;
+ }
+ if (p != NULL) {
+ struct nhrp_cie *cie;
+
+ if (nhrp_address_match_cie_list(&packet->dst_peer->my_nbma_address,
+ &packet->dst_iface->protocol_address,
+ &p->u.cie_list)) {
+ nhrp_packet_send_error(packet, NHRP_ERROR_LOOP_DETECTED, 0);
+ return FALSE;
+ }
+
+ cie = nhrp_cie_alloc();
+ if (cie != NULL) {
+ cie->hdr = (struct nhrp_cie_header) {
+ .code = NHRP_CODE_SUCCESS,
+ .holding_time = htons(packet->dst_iface->holding_time),
+ };
+ cie->nbma_address = packet->dst_peer->my_nbma_address;
+ cie->protocol_address = packet->dst_iface->protocol_address;
+ nhrp_payload_add_cie(p, cie);
+ }
+ }
+
+ return nhrp_packet_route_and_send(packet);
+}
+
+static int nhrp_packet_receive_local(struct nhrp_packet *packet)
+{
+ struct nhrp_packet *req;
+ char tmp[64], tmp2[64], tmp3[64];
+
+ if (packet_types[packet->hdr.type].type == NHRP_TYPE_REPLY) {
+ list_for_each_entry(req, &pending_requests, request_list_entry) {
+ if (packet->hdr.u.request_id != req->hdr.u.request_id)
+ continue;
+ if (nhrp_address_cmp(&packet->src_nbma_address,
+ &req->src_nbma_address))
+ continue;
+ if (nhrp_address_cmp(&packet->src_protocol_address,
+ &req->src_protocol_address))
+ continue;
+
+ if (req->handler != NULL)
+ req->handler(req->handler_ctx, packet);
+ nhrp_packet_dequeue(req);
+
+ return TRUE;
+ }
+
+ /* Reply to unsent request? */
+ nhrp_info("Packet type %d from nbma src %s, proto src %s, "
+ "proto dst %s dropped: no matching request",
+ packet->hdr.type,
+ nhrp_address_format(&packet->src_nbma_address,
+ sizeof(tmp), tmp),
+ nhrp_address_format(&packet->src_protocol_address,
+ sizeof(tmp2), tmp2),
+ nhrp_address_format(&packet->dst_protocol_address,
+ sizeof(tmp3), tmp3));
+
+ nhrp_packet_send_error(
+ packet, NHRP_ERROR_INVALID_RESOLUTION_REPLY, 0);
+ return TRUE;
+ }
+
+ if (packet_types[packet->hdr.type].handler == NULL) {
+ nhrp_info("Packet type %d from nbma src %s, proto src %s, "
+ "proto dst %s not supported",
+ packet->hdr.type,
+ nhrp_address_format(&packet->src_nbma_address,
+ sizeof(tmp), tmp),
+ nhrp_address_format(&packet->src_protocol_address,
+ sizeof(tmp2), tmp2),
+ nhrp_address_format(&packet->dst_protocol_address,
+ sizeof(tmp3), tmp3));
+ return FALSE;
+ }
+
+ if (packet->dst_peer->next_hop_address.type != PF_UNSPEC) {
+ /* Broadcast destinations gets rewritten as if destinied to
+ * our local address */
+ packet->dst_protocol_address =
+ packet->dst_peer->next_hop_address;
+ }
+
+ return packet_types[packet->hdr.type].handler(packet);
+}
+
+int nhrp_packet_receive(uint8_t *pdu, size_t pdulen,
+ struct nhrp_interface *iface,
+ struct nhrp_address *from)
+{
+ char tmp[64];
+ struct nhrp_packet *packet;
+ struct nhrp_address *dest;
+ struct nhrp_peer *peer;
+ int ret = FALSE;
+
+ if (nhrp_calculate_checksum(pdu, pdulen) != 0) {
+ nhrp_error("Bad checksum in packet from %s",
+ nhrp_address_format(from, sizeof(tmp), tmp));
+ return FALSE;
+ }
+
+ packet = nhrp_packet_alloc();
+ if (packet == NULL)
+ return FALSE;
+
+ if (!unmarshall_packet(pdu, pdulen, packet)) {
+ nhrp_error("Failed to unmarshall packet from %s",
+ nhrp_address_format(from, sizeof(tmp), tmp));
+ goto error;
+ }
+
+ packet->req_pdu = pdu;
+ packet->req_pdulen = pdulen;
+
+ if (packet_types[packet->hdr.type].type == NHRP_TYPE_REPLY)
+ dest = &packet->src_protocol_address;
+ else
+ dest = &packet->dst_protocol_address;
+
+ peer = nhrp_peer_route(iface, dest, 0, BIT(NHRP_PEER_TYPE_LOCAL_ADDR));
+ packet->src_linklayer_address = *from;
+ packet->src_iface = iface;
+ packet->dst_peer = nhrp_peer_get(peer);
+
+ /* RFC2332 5.3.4 - Authentication is always done pairwise on an NHRP
+ * hop-by-hop basis; i.e. regenerated at each hop. */
+ if (packet->src_iface->auth_token &&
+ (packet->hdr.type != NHRP_PACKET_ERROR_INDICATION ||
+ packet->hdr.u.error.code != NHRP_ERROR_AUTHENTICATION_FAILURE)) {
+ struct nhrp_payload *p;
+ p = nhrp_packet_extension(packet,
+ NHRP_EXTENSION_AUTHENTICATION |
+ NHRP_EXTENSION_FLAG_NOCREATE,
+ NHRP_PAYLOAD_TYPE_RAW);
+ if (p == NULL ||
+ nhrp_buffer_cmp(packet->src_iface->auth_token, p->u.raw) != 0) {
+ nhrp_error("Dropping packet from %s with bad authentication",
+ nhrp_address_format(from, sizeof(tmp), tmp));
+ nhrp_packet_send_error(packet, NHRP_ERROR_AUTHENTICATION_FAILURE, 0);
+ goto error;
+ }
+ }
+
+ if (peer != NULL &&
+ peer->type == NHRP_PEER_TYPE_LOCAL_ADDR)
+ ret = nhrp_packet_receive_local(packet);
+ else
+ ret = nhrp_packet_forward(packet);
+
+ packet->req_pdu = NULL;
+ packet->req_pdulen = 0;
+
+error:
+ nhrp_packet_put(packet);
+ return ret;
+}
+
+static int marshall_binary(uint8_t **pdu, size_t *pduleft, size_t size, void *raw)
+{
+ if (*pduleft < size)
+ return FALSE;
+
+ memcpy(*pdu, raw, size);
+ *pdu += size;
+ *pduleft -= size;
+
+ return TRUE;
+}
+
+static inline int marshall_protocol_address(uint8_t **pdu, size_t *pduleft, struct nhrp_address *pa)
+{
+ if (pa->subaddr_len != 0)
+ return FALSE;
+ return marshall_binary(pdu, pduleft, pa->addr_len, pa->addr);
+}
+
+static inline int marshall_nbma_address(uint8_t **pdu, size_t *pduleft, struct nhrp_address *na)
+{
+ return marshall_binary(pdu, pduleft, na->addr_len + na->subaddr_len, na->addr);
+}
+
+static int marshall_cie(uint8_t **pdu, size_t *pduleft, struct nhrp_cie *cie)
+{
+ cie->hdr.nbma_address_len = cie->nbma_address.addr_len;
+ cie->hdr.nbma_subaddress_len = cie->nbma_address.subaddr_len;
+ cie->hdr.protocol_address_len = cie->protocol_address.addr_len;
+
+ if (!marshall_binary(pdu, pduleft, sizeof(struct nhrp_cie_header), &cie->hdr))
+ return FALSE;
+ if (!marshall_nbma_address(pdu, pduleft, &cie->nbma_address))
+ return FALSE;
+ return marshall_protocol_address(pdu, pduleft, &cie->protocol_address);
+}
+
+static int marshall_payload(uint8_t **pdu, size_t *pduleft, struct nhrp_payload *p)
+{
+ struct nhrp_cie *cie;
+
+ switch (p->payload_type) {
+ case NHRP_PAYLOAD_TYPE_NONE:
+ return TRUE;
+ case NHRP_PAYLOAD_TYPE_RAW:
+ if (p->u.raw->length == 0)
+ return TRUE;
+ return marshall_binary(pdu, pduleft, p->u.raw->length, p->u.raw->data);
+ case NHRP_PAYLOAD_TYPE_CIE_LIST:
+ list_for_each_entry(cie, &p->u.cie_list, cie_list_entry) {
+ if (!marshall_cie(pdu, pduleft, cie))
+ return FALSE;
+ }
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+static int marshall_packet_header(uint8_t **pdu, size_t *pduleft, struct nhrp_packet *packet)
+{
+ if (!marshall_binary(pdu, pduleft, sizeof(packet->hdr), &packet->hdr))
+ return FALSE;
+ if (!marshall_nbma_address(pdu, pduleft, &packet->src_nbma_address))
+ return FALSE;
+ if (!marshall_protocol_address(pdu, pduleft, &packet->src_protocol_address))
+ return FALSE;
+ return marshall_protocol_address(pdu, pduleft, &packet->dst_protocol_address);
+}
+
+static int marshall_packet(uint8_t *pdu, size_t pduleft, struct nhrp_packet *packet)
+{
+ uint8_t *pos = pdu;
+ struct nhrp_packet_header *phdr = (struct nhrp_packet_header *) pdu;
+ struct nhrp_extension_header neh;
+ int i, size;
+
+ if (!marshall_packet_header(&pos, &pduleft, packet))
+ return -1;
+ if (!marshall_payload(&pos, &pduleft, nhrp_packet_payload(packet, NHRP_PAYLOAD_TYPE_ANY)))
+ return -2;
+
+ phdr->extension_offset = htons((int)(pos - pdu));
+ for (i = 1; i < packet->num_extensions; i++) {
+ struct nhrp_extension_header *eh = (struct nhrp_extension_header *) pos;
+
+ if (packet->extension_by_order[i].payload_type == NHRP_PAYLOAD_TYPE_NONE)
+ continue;
+
+ neh.type = htons(packet->extension_by_order[i].extension_type);
+ neh.length = 0;
+
+ if (!marshall_binary(&pos, &pduleft, sizeof(neh), &neh))
+ return -3;
+ if (!marshall_payload(&pos, &pduleft, &packet->extension_by_order[i]))
+ return -4;
+ eh->length = htons((pos - (uint8_t *) eh) - sizeof(neh));
+ }
+ neh.type = htons(NHRP_EXTENSION_END | NHRP_EXTENSION_FLAG_COMPULSORY);
+ neh.length = 0;
+ if (!marshall_binary(&pos, &pduleft, sizeof(neh), &neh))
+ return -5;
+
+ /* Cisco is seriously brain damaged. It needs some extra garbage
+ * at the end of error indication or it'll barf out spurious errors. */
+ if (packet->hdr.type == NHRP_PACKET_ERROR_INDICATION &&
+ pduleft >= 0x10) {
+ memset(pos, 0, 0x10);
+ pos += 0x10;
+ pduleft -= 0x10;
+ }
+
+ size = (int)(pos - pdu);
+ phdr->packet_size = htons(size);
+ phdr->checksum = 0;
+ phdr->src_nbma_address_len = packet->src_nbma_address.addr_len;
+ phdr->src_nbma_subaddress_len = packet->src_nbma_address.subaddr_len;
+ phdr->src_protocol_address_len = packet->src_protocol_address.addr_len;
+ phdr->dst_protocol_address_len = packet->dst_protocol_address.addr_len;
+ phdr->checksum = nhrp_calculate_checksum(pdu, size);
+
+ return size;
+}
+
+int nhrp_packet_route(struct nhrp_packet *packet)
+{
+ struct nhrp_address proto_nexthop, *src, *dst;
+ struct list_head *cielist = NULL;
+ struct nhrp_payload *payload;
+ struct nhrp_peer *peer;
+ char tmp[64];
+ int r;
+
+ if (packet->dst_iface == NULL) {
+ nhrp_error("nhrp_packet_route called without destination interface");
+ return FALSE;
+ }
+
+ if (packet_types[packet->hdr.type].type == NHRP_TYPE_REPLY) {
+ dst = &packet->src_protocol_address;
+ src = &packet->dst_protocol_address;
+ r = NHRP_EXTENSION_REVERSE_TRANSIT_NHS;
+ } else {
+ dst = &packet->dst_protocol_address;
+ src = &packet->src_protocol_address;
+ r = NHRP_EXTENSION_FORWARD_TRANSIT_NHS;
+ }
+ payload = nhrp_packet_extension(packet,
+ r | NHRP_EXTENSION_FLAG_NOCREATE,
+ NHRP_PAYLOAD_TYPE_CIE_LIST);
+ if (payload != NULL)
+ cielist = &payload->u.cie_list;
+
+ if (packet->dst_peer != NULL) {
+ proto_nexthop = packet->dst_peer->next_hop_address;
+ } else {
+ proto_nexthop = *dst;
+ do {
+ peer = nhrp_peer_route_full(
+ packet->dst_iface, &proto_nexthop, 0,
+ NHRP_PEER_TYPEMASK_ROUTE_VIA_NHS, src, cielist);
+ if (peer == NULL || peer->type == NHRP_PEER_TYPE_NEGATIVE) {
+ nhrp_error("No peer entry for protocol address %s",
+ nhrp_address_format(&proto_nexthop,
+ sizeof(tmp), tmp));
+ return FALSE;
+ }
+ if (peer->type != NHRP_PEER_TYPE_LOCAL_ROUTE)
+ break;
+ if (peer->next_hop_address.type == AF_UNSPEC)
+ break;
+ proto_nexthop = peer->next_hop_address;
+ } while (1);
+
+ packet->dst_peer = nhrp_peer_get(peer);
+ }
+
+ return TRUE;
+}
+
+int nhrp_packet_marshall_and_send(struct nhrp_packet *packet)
+{
+ uint8_t pdu[MAX_PDU_SIZE];
+ char tmp[4][64];
+ int size;
+
+ nhrp_debug("Sending packet %d, from: %s (nbma %s), to: %s (nbma %s)",
+ packet->hdr.type,
+ nhrp_address_format(&packet->src_protocol_address,
+ sizeof(tmp[0]), tmp[0]),
+ nhrp_address_format(&packet->src_nbma_address,
+ sizeof(tmp[1]), tmp[1]),
+ nhrp_address_format(&packet->dst_protocol_address,
+ sizeof(tmp[2]), tmp[2]),
+ nhrp_address_format(&packet->dst_peer->next_hop_address,
+ sizeof(tmp[3]), tmp[3]));
+
+ size = marshall_packet(pdu, sizeof(pdu), packet);
+ if (size < 0) {
+ nhrp_error("Packet marshalling failed (r=%d)", size);
+ return FALSE;
+ }
+
+ if (!kernel_send(pdu, size, packet->dst_iface,
+ &packet->dst_peer->next_hop_address))
+ return FALSE;
+
+ return TRUE;
+}
+
+int nhrp_packet_route_and_send(struct nhrp_packet *packet)
+{
+ struct nhrp_payload *payload;
+
+ if (packet->dst_peer == NULL || packet->dst_iface == NULL) {
+ if (!nhrp_packet_route(packet)) {
+ nhrp_packet_send_error(packet, NHRP_ERROR_PROTOCOL_ADDRESS_UNREACHABLE, 0);
+ return TRUE;
+ }
+ }
+
+ if (packet->src_nbma_address.addr_len == 0)
+ packet->src_nbma_address = packet->dst_peer->my_nbma_address;
+ if (packet->src_protocol_address.addr_len == 0)
+ packet->src_protocol_address = packet->dst_iface->protocol_address;
+ if (packet->hdr.afnum == AFNUM_RESERVED)
+ packet->hdr.afnum = packet->dst_peer->afnum;
+ if (packet->hdr.protocol_type == 0)
+ packet->hdr.protocol_type = packet->dst_peer->protocol_type;
+
+ /* RFC2332 5.3.1 */
+ payload = nhrp_packet_extension(
+ packet, NHRP_EXTENSION_RESPONDER_ADDRESS |
+ NHRP_EXTENSION_FLAG_COMPULSORY | NHRP_EXTENSION_FLAG_NOCREATE,
+ NHRP_PAYLOAD_TYPE_CIE_LIST);
+ if (packet_types[packet->hdr.type].type == NHRP_TYPE_REPLY &&
+ (payload != NULL && list_empty(&payload->u.cie_list))) {
+ struct nhrp_cie *cie;
+
+ cie = nhrp_cie_alloc();
+ if (cie == NULL)
+ return FALSE;
+
+ cie->hdr.holding_time = htons(packet->dst_iface->holding_time);
+ cie->nbma_address = packet->dst_peer->my_nbma_address;
+ cie->protocol_address = packet->dst_iface->protocol_address;
+ nhrp_payload_set_type(payload, NHRP_PAYLOAD_TYPE_CIE_LIST);
+ nhrp_payload_add_cie(payload, cie);
+ }
+
+ /* RFC2332 5.3.4 - Authentication is always done pairwise on an NHRP
+ * hop-by-hop basis; i.e. regenerated at each hop. */
+ payload = nhrp_packet_extension(packet,
+ NHRP_EXTENSION_AUTHENTICATION |
+ NHRP_EXTENSION_FLAG_COMPULSORY,
+ NHRP_PAYLOAD_TYPE_RAW);
+ nhrp_payload_free(payload);
+ if (packet->dst_iface->auth_token != NULL)
+ nhrp_payload_set_raw(payload,
+ nhrp_buffer_copy(packet->dst_iface->auth_token));
+
+ if (packet->dst_peer->type == NHRP_PEER_TYPE_LOCAL_ADDR) {
+ packet->src_iface = packet->dst_peer->interface;
+ return nhrp_packet_receive_local(packet);
+ }
+
+ if (packet->dst_peer->flags & (NHRP_PEER_FLAG_UP |
+ NHRP_PEER_FLAG_LOWER_UP))
+ return nhrp_packet_marshall_and_send(packet);
+
+ if (packet->dst_peer->queued_packet != NULL)
+ nhrp_packet_put(packet->dst_peer->queued_packet);
+ packet->dst_peer->queued_packet = nhrp_packet_get(packet);
+
+ return TRUE;
+}
+
+int nhrp_packet_send(struct nhrp_packet *packet)
+{
+ struct nhrp_payload *payload;
+ struct nhrp_cie *cie;
+
+ if (packet->dst_iface == NULL) {
+ if (!nhrp_packet_route(packet)) {
+ nhrp_packet_send_error(packet, NHRP_ERROR_PROTOCOL_ADDRESS_UNREACHABLE, 0);
+ return TRUE;
+ }
+ }
+
+ /* Cisco NAT extension CIE */
+ if (packet_types[packet->hdr.type].type != NHRP_TYPE_INDICATION &&
+ (packet->hdr.flags & NHRP_FLAG_REGISTRATION_NAT)) {
+ payload = nhrp_packet_extension(packet, NHRP_EXTENSION_NAT_ADDRESS,
+ NHRP_PAYLOAD_TYPE_CIE_LIST);
+
+ if (packet->dst_iface->nat_cie.nbma_address.addr_len &&
+ payload != NULL && list_empty(&payload->u.cie_list)) {
+ cie = nhrp_cie_alloc();
+ if (cie != NULL) {
+ *cie = packet->dst_iface->nat_cie;
+ nhrp_cie_reset(cie);
+ nhrp_payload_add_cie(payload, cie);
+ }
+ }
+ }
+
+ return nhrp_packet_route_and_send(packet);
+}
+
+static void nhrp_packet_xmit_timeout_cb(struct ev_timer *w, int revents)
+{
+ struct nhrp_packet *packet =
+ container_of(w, struct nhrp_packet, timeout);
+
+ list_del(&packet->request_list_entry);
+
+ if (packet->dst_peer != NULL &&
+ ++packet->retry < PACKET_RETRIES) {
+ nhrp_packet_marshall_and_send(packet);
+
+ list_add(&packet->request_list_entry, &pending_requests);
+ } else {
+ ev_timer_stop(&packet->timeout);
+ if (packet->dst_peer == NULL)
+ nhrp_error("nhrp_packet_xmit_timeout: no destination peer!");
+ if (packet->handler != NULL)
+ packet->handler(packet->handler_ctx, NULL);
+ nhrp_packet_dequeue(packet);
+ }
+}
+
+int nhrp_packet_send_request(struct nhrp_packet *pkt,
+ void (*handler)(void *ctx, struct nhrp_packet *packet),
+ void *ctx)
+{
+ struct nhrp_packet *packet;
+
+ packet = nhrp_packet_get(pkt);
+
+ packet->retry = 0;
+ if (packet->hdr.u.request_id == constant_htonl(0)) {
+ request_id++;
+ packet->hdr.u.request_id = htonl(request_id);
+ }
+
+ packet->handler = handler;
+ packet->handler_ctx = ctx;
+ list_add(&packet->request_list_entry, &pending_requests);
+ ev_timer_again(&packet->timeout);
+
+ return nhrp_packet_send(packet);
+}
+
+int nhrp_packet_send_error(struct nhrp_packet *error_packet,
+ uint16_t indication_code, uint16_t offset)
+{
+ struct nhrp_packet *p;
+ struct nhrp_payload *pl;
+ int r;
+
+ /* RFC2332 5.2.7 Never generate errors about errors */
+ if (error_packet->hdr.type == NHRP_PACKET_ERROR_INDICATION)
+ return TRUE;
+
+ p = nhrp_packet_alloc();
+ p->hdr = error_packet->hdr;
+ p->hdr.type = NHRP_PACKET_ERROR_INDICATION;
+ p->hdr.hop_count = 0;
+ p->hdr.u.error.code = indication_code;
+ p->hdr.u.error.offset = htons(offset);
+ p->dst_iface = error_packet->src_iface;
+
+ if (packet_types[error_packet->hdr.type].type == NHRP_TYPE_REPLY)
+ p->dst_protocol_address = error_packet->dst_protocol_address;
+ else
+ p->dst_protocol_address = error_packet->src_protocol_address;
+
+ pl = nhrp_packet_payload(p, NHRP_PAYLOAD_TYPE_RAW);
+ pl->u.raw = nhrp_buffer_alloc(error_packet->req_pdulen);
+ memcpy(pl->u.raw->data, error_packet->req_pdu, error_packet->req_pdulen);
+
+ /* Standard extensions */
+ nhrp_packet_extension(p,
+ NHRP_EXTENSION_FORWARD_TRANSIT_NHS |
+ NHRP_EXTENSION_FLAG_COMPULSORY,
+ NHRP_PAYLOAD_TYPE_CIE_LIST);
+
+ if (p->dst_protocol_address.type == PF_UNSPEC)
+ r = nhrp_do_handle_error_indication(p, error_packet);
+ else
+ r = nhrp_packet_send(p);
+
+ nhrp_packet_put(p);
+
+ return r;
+}
+
+int nhrp_packet_send_traffic(struct nhrp_interface *iface,
+ struct nhrp_address *nbma_src,
+ struct nhrp_address *protocol_src,
+ struct nhrp_address *protocol_dst,
+ int protocol_type, uint8_t *pdu, size_t pdulen)
+{
+ struct nhrp_rate_limit *rl;
+ struct nhrp_packet *p;
+ struct nhrp_payload *pl;
+ struct nhrp_peer *peer;
+ char tmp1[64], tmp2[64], tmp3[64], tmp4[64];
+ int r;
+
+ if (!(iface->flags & NHRP_INTERFACE_FLAG_REDIRECT))
+ return FALSE;
+
+ /* Are we serving the NBMA source */
+ peer = nhrp_interface_find_peer(iface, nbma_src);
+ if (peer == NULL || peer->type != NHRP_PEER_TYPE_DYNAMIC)
+ return FALSE;
+
+ rl = get_rate_limit(protocol_src, protocol_dst);
+ if (rl == NULL)
+ return FALSE;
+
+ /* If silence period has elapsed, reset algorithm */
+ if (ev_now() > rl->rate_last + RATE_LIMIT_SILENCE)
+ rl->rate_tokens = 0;
+
+ /* Too many ignored redirects; just update time of last packet */
+ if (rl->rate_tokens >= RATE_LIMIT_MAX_TOKENS) {
+ rl->rate_last = ev_now();
+ return FALSE;
+ }
+
+ /* Check for load limit; set rate_last to last sent redirect */
+ if (rl->rate_tokens != 0 &&
+ ev_now() < rl->rate_last + RATE_LIMIT_SEND_INTERVAL)
+ return FALSE;
+
+ rl->rate_tokens++;
+ rl->rate_last = ev_now();
+
+ p = nhrp_packet_alloc();
+ p->hdr = (struct nhrp_packet_header) {
+ .protocol_type = protocol_type,
+ .version = NHRP_VERSION_RFC2332,
+ .type = NHRP_PACKET_TRAFFIC_INDICATION,
+ .hop_count = 0,
+ };
+ p->dst_protocol_address = *protocol_src;
+
+ pl = nhrp_packet_payload(p, NHRP_PAYLOAD_TYPE_RAW);
+ pl->u.raw = nhrp_buffer_alloc(pdulen);
+ memcpy(pl->u.raw->data, pdu, pdulen);
+
+ /* Standard extensions */
+ nhrp_packet_extension(p,
+ NHRP_EXTENSION_FORWARD_TRANSIT_NHS |
+ NHRP_EXTENSION_FLAG_COMPULSORY,
+ NHRP_PAYLOAD_TYPE_CIE_LIST);
+
+ nhrp_info("Sending Traffic Indication about packet from %s to %s (to %s/%s)",
+ nhrp_address_format(protocol_src, sizeof(tmp1), tmp1),
+ nhrp_address_format(protocol_dst, sizeof(tmp2), tmp2),
+ nhrp_address_format(&peer->protocol_address, sizeof(tmp3), tmp3),
+ nhrp_address_format(&peer->next_hop_address, sizeof(tmp4), tmp4));
+
+ p->dst_iface = iface;
+ p->dst_peer = nhrp_peer_get(peer);
+ r = nhrp_packet_send(p);
+ nhrp_packet_put(p);
+
+ return r;
+}
+
+void nhrp_packet_hook_request(int request,
+ int (*handler)(struct nhrp_packet *packet))
+{
+ NHRP_BUG_ON(request < 0 || request >= ARRAY_SIZE(packet_types));
+ NHRP_BUG_ON(packet_types[request].handler != NULL);
+
+ packet_types[request].handler = handler;
+}