summaryrefslogtreecommitdiff
path: root/babeld/message.c
diff options
context:
space:
mode:
authorPaul Jakma <paul@quagga.net>2011-12-25 17:52:09 +0100
committerPaul Jakma <paul@quagga.net>2012-03-25 17:06:51 +0100
commit5734509c0545ebd95a5b8e3f22a911c1a39ffa1b (patch)
tree2f35ad50d793080db1f858c2de2228cfd2946dfd /babeld/message.c
parentfe67e668881b8b8d71b1570575c913cd287fafcf (diff)
babeld: Initial import, for Babel routing protocol.
* Initial import of the Babel routing protocol, ported to Quagga. * LICENCE: Update the original LICENCE file to include all known potentially applicable copyright claims. Ask that any future contributors to babeld/ grant MIT/X11 licence to their work. * *.{c,h}: Add GPL headers, in according with the SFLC guidance on dealing with potentially mixed GPL/other licensed work, at: https://www.softwarefreedom.org/resources/2007/gpl-non-gpl-collaboration.html
Diffstat (limited to 'babeld/message.c')
-rw-r--r--babeld/message.c1456
1 files changed, 1456 insertions, 0 deletions
diff --git a/babeld/message.c b/babeld/message.c
new file mode 100644
index 00000000..bfb17625
--- /dev/null
+++ b/babeld/message.c
@@ -0,0 +1,1456 @@
+/*
+ * This file is free software: you may copy, redistribute and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+
+Copyright (c) 2007, 2008 by Juliusz Chroboczek
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <zebra.h>
+#include "if.h"
+
+#include "babeld.h"
+#include "util.h"
+#include "net.h"
+#include "babel_interface.h"
+#include "source.h"
+#include "neighbour.h"
+#include "route.h"
+#include "xroute.h"
+#include "resend.h"
+#include "message.h"
+#include "kernel.h"
+
+unsigned char packet_header[4] = {42, 2};
+
+int parasitic = 0;
+int split_horizon = 1;
+
+unsigned short myseqno = 0;
+struct timeval seqno_time = {0, 0};
+
+#define UNICAST_BUFSIZE 1024
+int unicast_buffered = 0;
+unsigned char *unicast_buffer = NULL;
+struct neighbour *unicast_neighbour = NULL;
+struct timeval unicast_flush_timeout = {0, 0};
+
+static const unsigned char v4prefix[16] =
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0, 0, 0, 0 };
+
+static int
+network_prefix(int ae, int plen, unsigned int omitted,
+ const unsigned char *p, const unsigned char *dp,
+ unsigned int len, unsigned char *p_r)
+{
+ unsigned pb;
+ unsigned char prefix[16];
+
+ if(plen >= 0)
+ pb = (plen + 7) / 8;
+ else if(ae == 1)
+ pb = 4;
+ else
+ pb = 16;
+
+ if(pb > 16)
+ return -1;
+
+ memset(prefix, 0, 16);
+
+ switch(ae) {
+ case 0: break;
+ case 1:
+ if(omitted > 4 || pb > 4 || (pb > omitted && len < pb - omitted))
+ return -1;
+ memcpy(prefix, v4prefix, 12);
+ if(omitted) {
+ if (dp == NULL || !v4mapped(dp)) return -1;
+ memcpy(prefix, dp, 12 + omitted);
+ }
+ if(pb > omitted) memcpy(prefix + 12 + omitted, p, pb - omitted);
+ break;
+ case 2:
+ if(omitted > 16 || (pb > omitted && len < pb - omitted)) return -1;
+ if(omitted) {
+ if (dp == NULL || v4mapped(dp)) return -1;
+ memcpy(prefix, dp, omitted);
+ }
+ if(pb > omitted) memcpy(prefix + omitted, p, pb - omitted);
+ break;
+ case 3:
+ if(pb > 8 && len < pb - 8) return -1;
+ prefix[0] = 0xfe;
+ prefix[1] = 0x80;
+ if(pb > 8) memcpy(prefix + 8, p, pb - 8);
+ break;
+ default:
+ return -1;
+ }
+
+ mask_prefix(p_r, prefix, plen < 0 ? 128 : ae == 1 ? plen + 96 : plen);
+ return 1;
+}
+
+static int
+network_address(int ae, const unsigned char *a, unsigned int len,
+ unsigned char *a_r)
+{
+ return network_prefix(ae, -1, 0, a, NULL, len, a_r);
+}
+
+void
+parse_packet(const unsigned char *from, struct interface *ifp,
+ const unsigned char *packet, int packetlen)
+{
+ int i;
+ const unsigned char *message;
+ unsigned char type, len;
+ int bodylen;
+ struct neighbour *neigh;
+ int have_router_id = 0, have_v4_prefix = 0, have_v6_prefix = 0,
+ have_v4_nh = 0, have_v6_nh = 0;
+ unsigned char router_id[8], v4_prefix[16], v6_prefix[16],
+ v4_nh[16], v6_nh[16];
+
+ if(!linklocal(from)) {
+ fprintf(stderr, "Received packet from non-local address %s.\n",
+ format_address(from));
+ return;
+ }
+
+ if(packet[0] != 42) {
+ fprintf(stderr, "Received malformed packet on %s from %s.\n",
+ ifp->name, format_address(from));
+ return;
+ }
+
+ if(packet[1] != 2) {
+ fprintf(stderr,
+ "Received packet with unknown version %d on %s from %s.\n",
+ packet[1], ifp->name, format_address(from));
+ return;
+ }
+
+ neigh = find_neighbour(from, ifp);
+ if(neigh == NULL) {
+ fprintf(stderr, "Couldn't allocate neighbour.\n");
+ return;
+ }
+
+ DO_NTOHS(bodylen, packet + 2);
+
+ if(bodylen + 4 > packetlen) {
+ fprintf(stderr, "Received truncated packet (%d + 4 > %d).\n",
+ bodylen, packetlen);
+ bodylen = packetlen - 4;
+ }
+
+ i = 0;
+ while(i < bodylen) {
+ message = packet + 4 + i;
+ type = message[0];
+ if(type == MESSAGE_PAD1) {
+ debugf(BABEL_DEBUG_COMMON,"Received pad1 from %s on %s.",
+ format_address(from), ifp->name);
+ i++;
+ continue;
+ }
+ if(i + 1 > bodylen) {
+ fprintf(stderr, "Received truncated message.\n");
+ break;
+ }
+ len = message[1];
+ if(i + len > bodylen) {
+ fprintf(stderr, "Received truncated message.\n");
+ break;
+ }
+
+ if(type == MESSAGE_PADN) {
+ debugf(BABEL_DEBUG_COMMON,"Received pad%d from %s on %s.",
+ len, format_address(from), ifp->name);
+ } else if(type == MESSAGE_ACK_REQ) {
+ unsigned short nonce, interval;
+ if(len < 6) goto fail;
+ DO_NTOHS(nonce, message + 4);
+ DO_NTOHS(interval, message + 6);
+ debugf(BABEL_DEBUG_COMMON,"Received ack-req (%04X %d) from %s on %s.",
+ nonce, interval, format_address(from), ifp->name);
+ send_ack(neigh, nonce, interval);
+ } else if(type == MESSAGE_ACK) {
+ debugf(BABEL_DEBUG_COMMON,"Received ack from %s on %s.",
+ format_address(from), ifp->name);
+ /* Nothing right now */
+ } else if(type == MESSAGE_HELLO) {
+ unsigned short seqno, interval;
+ int changed;
+ if(len < 6) goto fail;
+ DO_NTOHS(seqno, message + 4);
+ DO_NTOHS(interval, message + 6);
+ debugf(BABEL_DEBUG_COMMON,"Received hello %d (%d) from %s on %s.",
+ seqno, interval,
+ format_address(from), ifp->name);
+ babel_get_if_nfo(ifp)->activity_time = babel_now.tv_sec;
+ changed = update_neighbour(neigh, seqno, interval);
+ update_neighbour_metric(neigh, changed);
+ if(interval > 0)
+ schedule_neighbours_check(interval * 10, 0);
+ } else if(type == MESSAGE_IHU) {
+ unsigned short txcost, interval;
+ unsigned char address[16];
+ int rc;
+ if(len < 6) goto fail;
+ DO_NTOHS(txcost, message + 4);
+ DO_NTOHS(interval, message + 6);
+ rc = network_address(message[2], message + 8, len - 6, address);
+ if(rc < 0) goto fail;
+ debugf(BABEL_DEBUG_COMMON,"Received ihu %d (%d) from %s on %s for %s.",
+ txcost, interval,
+ format_address(from), ifp->name,
+ format_address(address));
+ if(message[2] == 0 || is_interface_ll_address(ifp, address)) {
+ int changed = txcost != neigh->txcost;
+ neigh->txcost = txcost;
+ neigh->ihu_time = babel_now;
+ neigh->ihu_interval = interval;
+ update_neighbour_metric(neigh, changed);
+ if(interval > 0)
+ schedule_neighbours_check(interval * 10 * 3, 0);
+ }
+ } else if(type == MESSAGE_ROUTER_ID) {
+ if(len < 10) {
+ have_router_id = 0;
+ goto fail;
+ }
+ memcpy(router_id, message + 4, 8);
+ have_router_id = 1;
+ debugf(BABEL_DEBUG_COMMON,"Received router-id %s from %s on %s.",
+ format_eui64(router_id), format_address(from), ifp->name);
+ } else if(type == MESSAGE_NH) {
+ unsigned char nh[16];
+ int rc;
+ if(len < 2) {
+ have_v4_nh = 0;
+ have_v6_nh = 0;
+ goto fail;
+ }
+ rc = network_address(message[2], message + 4, len - 2,
+ nh);
+ if(rc < 0) {
+ have_v4_nh = 0;
+ have_v6_nh = 0;
+ goto fail;
+ }
+ debugf(BABEL_DEBUG_COMMON,"Received nh %s (%d) from %s on %s.",
+ format_address(nh), message[2],
+ format_address(from), ifp->name);
+ if(message[2] == 1) {
+ memcpy(v4_nh, nh, 16);
+ have_v4_nh = 1;
+ } else {
+ memcpy(v6_nh, nh, 16);
+ have_v6_nh = 1;
+ }
+ } else if(type == MESSAGE_UPDATE) {
+ unsigned char prefix[16], *nh;
+ unsigned char plen;
+ unsigned short interval, seqno, metric;
+ int rc;
+ if(len < 10) {
+ if(len < 2 || message[3] & 0x80)
+ have_v4_prefix = have_v6_prefix = 0;
+ goto fail;
+ }
+ DO_NTOHS(interval, message + 6);
+ DO_NTOHS(seqno, message + 8);
+ DO_NTOHS(metric, message + 10);
+ if(message[5] == 0 ||
+ (message[3] == 1 ? have_v4_prefix : have_v6_prefix))
+ rc = network_prefix(message[2], message[4], message[5],
+ message + 12,
+ message[2] == 1 ? v4_prefix : v6_prefix,
+ len - 10, prefix);
+ else
+ rc = -1;
+ if(rc < 0) {
+ if(message[3] & 0x80)
+ have_v4_prefix = have_v6_prefix = 0;
+ goto fail;
+ }
+
+ plen = message[4] + (message[2] == 1 ? 96 : 0);
+
+ if(message[3] & 0x80) {
+ if(message[2] == 1) {
+ memcpy(v4_prefix, prefix, 16);
+ have_v4_prefix = 1;
+ } else {
+ memcpy(v6_prefix, prefix, 16);
+ have_v6_prefix = 1;
+ }
+ }
+ if(message[3] & 0x40) {
+ if(message[2] == 1) {
+ memset(router_id, 0, 4);
+ memcpy(router_id + 4, prefix + 12, 4);
+ } else {
+ memcpy(router_id, prefix + 8, 8);
+ }
+ have_router_id = 1;
+ }
+ if(!have_router_id && message[2] != 0) {
+ fprintf(stderr, "Received prefix with no router id.\n");
+ goto fail;
+ }
+ debugf(BABEL_DEBUG_COMMON,"Received update%s%s for %s from %s on %s.",
+ (message[3] & 0x80) ? "/prefix" : "",
+ (message[3] & 0x40) ? "/id" : "",
+ format_prefix(prefix, plen),
+ format_address(from), ifp->name);
+
+ if(message[2] == 0) {
+ if(metric < 0xFFFF) {
+ fprintf(stderr,
+ "Received wildcard update with finite metric.\n");
+ goto done;
+ }
+ retract_neighbour_routes(neigh);
+ goto done;
+ } else if(message[2] == 1) {
+ if(!have_v4_nh)
+ goto fail;
+ nh = v4_nh;
+ } else if(have_v6_nh) {
+ nh = v6_nh;
+ } else {
+ nh = neigh->address;
+ }
+
+ if(message[2] == 1) {
+ if(!babel_get_if_nfo(ifp)->ipv4)
+ goto done;
+ }
+
+ update_route(router_id, prefix, plen, seqno, metric, interval,
+ neigh, nh);
+ } else if(type == MESSAGE_REQUEST) {
+ unsigned char prefix[16], plen;
+ int rc;
+ if(len < 2) goto fail;
+ rc = network_prefix(message[2], message[3], 0,
+ message + 4, NULL, len - 2, prefix);
+ if(rc < 0) goto fail;
+ plen = message[3] + (message[2] == 1 ? 96 : 0);
+ debugf(BABEL_DEBUG_COMMON,"Received request for %s from %s on %s.",
+ message[2] == 0 ? "any" : format_prefix(prefix, plen),
+ format_address(from), ifp->name);
+ if(message[2] == 0) {
+ /* If a neighbour is requesting a full route dump from us,
+ we might as well send it an IHU. */
+ send_ihu(neigh, NULL);
+ send_update(neigh->ifp, 0, NULL, 0);
+ } else {
+ send_update(neigh->ifp, 0, prefix, plen);
+ }
+ } else if(type == MESSAGE_MH_REQUEST) {
+ unsigned char prefix[16], plen;
+ unsigned short seqno;
+ int rc;
+ if(len < 14) goto fail;
+ DO_NTOHS(seqno, message + 4);
+ rc = network_prefix(message[2], message[3], 0,
+ message + 16, NULL, len - 14, prefix);
+ if(rc < 0) goto fail;
+ plen = message[3] + (message[2] == 1 ? 96 : 0);
+ debugf(BABEL_DEBUG_COMMON,"Received request (%d) for %s from %s on %s (%s, %d).",
+ message[6],
+ format_prefix(prefix, plen),
+ format_address(from), ifp->name,
+ format_eui64(message + 8), seqno);
+ handle_request(neigh, prefix, plen, message[6],
+ seqno, message + 8);
+ } else {
+ debugf(BABEL_DEBUG_COMMON,"Received unknown packet type %d from %s on %s.",
+ type, format_address(from), ifp->name);
+ }
+ done:
+ i += len + 2;
+ continue;
+
+ fail:
+ fprintf(stderr, "Couldn't parse packet (%d, %d) from %s on %s.\n",
+ message[0], message[1], format_address(from), ifp->name);
+ goto done;
+ }
+ return;
+}
+
+/* Under normal circumstances, there are enough moderation mechanisms
+ elsewhere in the protocol to make sure that this last-ditch check
+ should never trigger. But I'm superstitious. */
+
+static int
+check_bucket(struct interface *ifp)
+{
+ babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp);
+ if(babel_ifp->bucket <= 0) {
+ int seconds = babel_now.tv_sec - babel_ifp->bucket_time;
+ if(seconds > 0) {
+ babel_ifp->bucket = MIN(BUCKET_TOKENS_MAX,
+ seconds * BUCKET_TOKENS_PER_SEC);
+ }
+ /* Reset bucket time unconditionally, in case clock is stepped. */
+ babel_ifp->bucket_time = babel_now.tv_sec;
+ }
+
+ if(babel_ifp->bucket > 0) {
+ babel_ifp->bucket--;
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+void
+flushbuf(struct interface *ifp)
+{
+ int rc;
+ struct sockaddr_in6 sin6;
+ babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp);
+
+ assert(babel_ifp->buffered <= babel_ifp->bufsize);
+
+ flushupdates(ifp);
+
+ if(babel_ifp->buffered > 0) {
+ debugf(BABEL_DEBUG_COMMON," (flushing %d buffered bytes on %s)",
+ babel_ifp->buffered, ifp->name);
+ if(check_bucket(ifp)) {
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ memcpy(&sin6.sin6_addr, protocol_group, 16);
+ sin6.sin6_port = htons(protocol_port);
+ sin6.sin6_scope_id = ifp->ifindex;
+ DO_HTONS(packet_header + 2, babel_ifp->buffered);
+ rc = babel_send(protocol_socket,
+ packet_header, sizeof(packet_header),
+ babel_ifp->sendbuf, babel_ifp->buffered,
+ (struct sockaddr*)&sin6, sizeof(sin6));
+ if(rc < 0)
+ zlog_err("send: %s", safe_strerror(errno));
+ } else {
+ fprintf(stderr, "Warning: bucket full, dropping packet to %s.\n",
+ ifp->name);
+ }
+ }
+ VALGRIND_MAKE_MEM_UNDEFINED(babel_ifp->sendbuf, babel_ifp->bufsize);
+ babel_ifp->buffered = 0;
+ babel_ifp->have_buffered_hello = 0;
+ babel_ifp->have_buffered_id = 0;
+ babel_ifp->have_buffered_nh = 0;
+ babel_ifp->have_buffered_prefix = 0;
+ babel_ifp->flush_timeout.tv_sec = 0;
+ babel_ifp->flush_timeout.tv_usec = 0;
+}
+
+static void
+schedule_flush(struct interface *ifp)
+{
+ babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp);
+ unsigned msecs = jitter(babel_ifp, 0);
+ if(babel_ifp->flush_timeout.tv_sec != 0 &&
+ timeval_minus_msec(&babel_ifp->flush_timeout, &babel_now) < msecs)
+ return;
+ set_timeout(&babel_ifp->flush_timeout, msecs);
+}
+
+static void
+schedule_flush_now(struct interface *ifp)
+{
+ babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp);
+ /* Almost now */
+ unsigned msecs = roughly(10);
+ if(babel_ifp->flush_timeout.tv_sec != 0 &&
+ timeval_minus_msec(&babel_ifp->flush_timeout, &babel_now) < msecs)
+ return;
+ set_timeout(&babel_ifp->flush_timeout, msecs);
+}
+
+static void
+schedule_unicast_flush(unsigned msecs)
+{
+ if(!unicast_neighbour)
+ return;
+ if(unicast_flush_timeout.tv_sec != 0 &&
+ timeval_minus_msec(&unicast_flush_timeout, &babel_now) < msecs)
+ return;
+ unicast_flush_timeout.tv_usec = (babel_now.tv_usec + msecs * 1000) %1000000;
+ unicast_flush_timeout.tv_sec =
+ babel_now.tv_sec + (babel_now.tv_usec / 1000 + msecs) / 1000;
+}
+
+static void
+ensure_space(struct interface *ifp, int space)
+{
+ babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp);
+ if(babel_ifp->bufsize - babel_ifp->buffered < space)
+ flushbuf(ifp);
+}
+
+static void
+start_message(struct interface *ifp, int type, int len)
+{
+ babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp);
+ if(babel_ifp->bufsize - babel_ifp->buffered < len + 2)
+ flushbuf(ifp);
+ babel_ifp->sendbuf[babel_ifp->buffered++] = type;
+ babel_ifp->sendbuf[babel_ifp->buffered++] = len;
+}
+
+static void
+end_message(struct interface *ifp, int type, int bytes)
+{
+ babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp);
+ assert(babel_ifp->buffered >= bytes + 2 &&
+ babel_ifp->sendbuf[babel_ifp->buffered - bytes - 2] == type &&
+ babel_ifp->sendbuf[babel_ifp->buffered - bytes - 1] == bytes);
+ schedule_flush(ifp);
+}
+
+static void
+accumulate_byte(struct interface *ifp, unsigned char value)
+{
+ babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp);
+ babel_ifp->sendbuf[babel_ifp->buffered++] = value;
+}
+
+static void
+accumulate_short(struct interface *ifp, unsigned short value)
+{
+ babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp);
+ DO_HTONS(babel_ifp->sendbuf + babel_ifp->buffered, value);
+ babel_ifp->buffered += 2;
+}
+
+static void
+accumulate_bytes(struct interface *ifp,
+ const unsigned char *value, unsigned len)
+{
+ babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp);
+ memcpy(babel_ifp->sendbuf + babel_ifp->buffered, value, len);
+ babel_ifp->buffered += len;
+}
+
+static int
+start_unicast_message(struct neighbour *neigh, int type, int len)
+{
+ if(unicast_neighbour) {
+ if(neigh != unicast_neighbour ||
+ unicast_buffered + len + 2 >=
+ MIN(UNICAST_BUFSIZE, babel_get_if_nfo(neigh->ifp)->bufsize))
+ flush_unicast(0);
+ }
+ if(!unicast_buffer)
+ unicast_buffer = malloc(UNICAST_BUFSIZE);
+ if(!unicast_buffer) {
+ zlog_err("malloc(unicast_buffer): %s", safe_strerror(errno));
+ return -1;
+ }
+
+ unicast_neighbour = neigh;
+
+ unicast_buffer[unicast_buffered++] = type;
+ unicast_buffer[unicast_buffered++] = len;
+ return 1;
+}
+
+static void
+end_unicast_message(struct neighbour *neigh, int type, int bytes)
+{
+ assert(unicast_neighbour == neigh && unicast_buffered >= bytes + 2 &&
+ unicast_buffer[unicast_buffered - bytes - 2] == type &&
+ unicast_buffer[unicast_buffered - bytes - 1] == bytes);
+ schedule_unicast_flush(jitter(babel_get_if_nfo(neigh->ifp), 0));
+}
+
+static void
+accumulate_unicast_byte(struct neighbour *neigh, unsigned char value)
+{
+ unicast_buffer[unicast_buffered++] = value;
+}
+
+static void
+accumulate_unicast_short(struct neighbour *neigh, unsigned short value)
+{
+ DO_HTONS(unicast_buffer + unicast_buffered, value);
+ unicast_buffered += 2;
+}
+
+static void
+accumulate_unicast_bytes(struct neighbour *neigh,
+ const unsigned char *value, unsigned len)
+{
+ memcpy(unicast_buffer + unicast_buffered, value, len);
+ unicast_buffered += len;
+}
+
+void
+send_ack(struct neighbour *neigh, unsigned short nonce, unsigned short interval)
+{
+ int rc;
+ debugf(BABEL_DEBUG_COMMON,"Sending ack (%04x) to %s on %s.",
+ nonce, format_address(neigh->address), neigh->ifp->name);
+ rc = start_unicast_message(neigh, MESSAGE_ACK, 2); if(rc < 0) return;
+ accumulate_unicast_short(neigh, nonce);
+ end_unicast_message(neigh, MESSAGE_ACK, 2);
+ /* Roughly yields a value no larger than 3/2, so this meets the deadline */
+ schedule_unicast_flush(roughly(interval * 6));
+}
+
+void
+send_hello_noupdate(struct interface *ifp, unsigned interval)
+{
+ babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp);
+ /* This avoids sending multiple hellos in a single packet, which breaks
+ link quality estimation. */
+ if(babel_ifp->have_buffered_hello)
+ flushbuf(ifp);
+
+ babel_ifp->hello_seqno = seqno_plus(babel_ifp->hello_seqno, 1);
+ set_timeout(&babel_ifp->hello_timeout, babel_ifp->hello_interval);
+
+ if(!if_up(ifp))
+ return;
+
+ debugf(BABEL_DEBUG_COMMON,"Sending hello %d (%d) to %s.",
+ babel_ifp->hello_seqno, interval, ifp->name);
+
+ start_message(ifp, MESSAGE_HELLO, 6);
+ accumulate_short(ifp, 0);
+ accumulate_short(ifp, babel_ifp->hello_seqno);
+ accumulate_short(ifp, interval > 0xFFFF ? 0xFFFF : interval);
+ end_message(ifp, MESSAGE_HELLO, 6);
+ babel_ifp->have_buffered_hello = 1;
+}
+
+void
+send_hello(struct interface *ifp)
+{
+ babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp);
+ send_hello_noupdate(ifp, (babel_ifp->hello_interval + 9) / 10);
+ /* Send full IHU every 3 hellos, and marginal IHU each time */
+ if(babel_ifp->hello_seqno % 3 == 0)
+ send_ihu(NULL, ifp);
+ else
+ send_marginal_ihu(ifp);
+}
+
+void
+flush_unicast(int dofree)
+{
+ struct sockaddr_in6 sin6;
+ int rc;
+
+ if(unicast_buffered == 0)
+ goto done;
+
+ if(!if_up(unicast_neighbour->ifp))
+ goto done;
+
+ /* Preserve ordering of messages */
+ flushbuf(unicast_neighbour->ifp);
+
+ if(check_bucket(unicast_neighbour->ifp)) {
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ memcpy(&sin6.sin6_addr, unicast_neighbour->address, 16);
+ sin6.sin6_port = htons(protocol_port);
+ sin6.sin6_scope_id = unicast_neighbour->ifp->ifindex;
+ DO_HTONS(packet_header + 2, unicast_buffered);
+ rc = babel_send(protocol_socket,
+ packet_header, sizeof(packet_header),
+ unicast_buffer, unicast_buffered,
+ (struct sockaddr*)&sin6, sizeof(sin6));
+ if(rc < 0)
+ zlog_err("send(unicast): %s", safe_strerror(errno));
+ } else {
+ fprintf(stderr,
+ "Warning: bucket full, dropping unicast packet"
+ "to %s if %s.\n",
+ format_address(unicast_neighbour->address),
+ unicast_neighbour->ifp->name);
+ }
+
+ done:
+ VALGRIND_MAKE_MEM_UNDEFINED(unicast_buffer, UNICAST_BUFSIZE);
+ unicast_buffered = 0;
+ if(dofree && unicast_buffer) {
+ free(unicast_buffer);
+ unicast_buffer = NULL;
+ }
+ unicast_neighbour = NULL;
+ unicast_flush_timeout.tv_sec = 0;
+ unicast_flush_timeout.tv_usec = 0;
+}
+
+static void
+really_send_update(struct interface *ifp,
+ const unsigned char *id,
+ const unsigned char *prefix, unsigned char plen,
+ unsigned short seqno, unsigned short metric)
+{
+ babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp);
+ int add_metric, v4, real_plen, omit = 0;
+ const unsigned char *real_prefix;
+ unsigned short flags = 0;
+
+ if(!if_up(ifp))
+ return;
+
+ add_metric = output_filter(id, prefix, plen, ifp->ifindex);
+ if(add_metric >= INFINITY)
+ return;
+
+ metric = MIN(metric + add_metric, INFINITY);
+ /* Worst case */
+ ensure_space(ifp, 20 + 12 + 28);
+
+ v4 = plen >= 96 && v4mapped(prefix);
+
+ if(v4) {
+ if(!babel_ifp->ipv4)
+ return;
+ if(!babel_ifp->have_buffered_nh ||
+ memcmp(babel_ifp->buffered_nh, babel_ifp->ipv4, 4) != 0) {
+ start_message(ifp, MESSAGE_NH, 6);
+ accumulate_byte(ifp, 1);
+ accumulate_byte(ifp, 0);
+ accumulate_bytes(ifp, babel_ifp->ipv4, 4);
+ end_message(ifp, MESSAGE_NH, 6);
+ memcpy(babel_ifp->buffered_nh, babel_ifp->ipv4, 4);
+ babel_ifp->have_buffered_nh = 1;
+ }
+
+ real_prefix = prefix + 12;
+ real_plen = plen - 96;
+ } else {
+ if(babel_ifp->have_buffered_prefix) {
+ while(omit < plen / 8 &&
+ babel_ifp->buffered_prefix[omit] == prefix[omit])
+ omit++;
+ }
+ if(!babel_ifp->have_buffered_prefix || plen >= 48)
+ flags |= 0x80;
+ real_prefix = prefix;
+ real_plen = plen;
+ }
+
+ if(!babel_ifp->have_buffered_id
+ || memcmp(id, babel_ifp->buffered_id, 8) != 0) {
+ if(real_plen == 128 && memcmp(real_prefix + 8, id, 8) == 0) {
+ flags |= 0x40;
+ } else {
+ start_message(ifp, MESSAGE_ROUTER_ID, 10);
+ accumulate_short(ifp, 0);
+ accumulate_bytes(ifp, id, 8);
+ end_message(ifp, MESSAGE_ROUTER_ID, 10);
+ }
+ memcpy(babel_ifp->buffered_id, id, 16);
+ babel_ifp->have_buffered_id = 1;
+ }
+
+ start_message(ifp, MESSAGE_UPDATE, 10 + (real_plen + 7) / 8 - omit);
+ accumulate_byte(ifp, v4 ? 1 : 2);
+ accumulate_byte(ifp, flags);
+ accumulate_byte(ifp, real_plen);
+ accumulate_byte(ifp, omit);
+ accumulate_short(ifp, (babel_ifp->update_interval + 5) / 10);
+ accumulate_short(ifp, seqno);
+ accumulate_short(ifp, metric);
+ accumulate_bytes(ifp, real_prefix + omit, (real_plen + 7) / 8 - omit);
+ end_message(ifp, MESSAGE_UPDATE, 10 + (real_plen + 7) / 8 - omit);
+
+ if(flags & 0x80) {
+ memcpy(babel_ifp->buffered_prefix, prefix, 16);
+ babel_ifp->have_buffered_prefix = 1;
+ }
+}
+
+static int
+compare_buffered_updates(const void *av, const void *bv)
+{
+ const struct buffered_update *a = av, *b = bv;
+ int rc, v4a, v4b, ma, mb;
+
+ rc = memcmp(a->id, b->id, 8);
+ if(rc != 0)
+ return rc;
+
+ v4a = (a->plen >= 96 && v4mapped(a->prefix));
+ v4b = (b->plen >= 96 && v4mapped(b->prefix));
+
+ if(v4a > v4b)
+ return 1;
+ else if(v4a < v4b)
+ return -1;
+
+ ma = (!v4a && a->plen == 128 && memcmp(a->prefix + 8, a->id, 8) == 0);
+ mb = (!v4b && b->plen == 128 && memcmp(b->prefix + 8, b->id, 8) == 0);
+
+ if(ma > mb)
+ return -1;
+ else if(mb > ma)
+ return 1;
+
+ if(a->plen < b->plen)
+ return 1;
+ else if(a->plen > b->plen)
+ return -1;
+
+ return memcmp(a->prefix, b->prefix, 16);
+}
+
+void
+flushupdates(struct interface *ifp)
+{
+ babel_interface_nfo *babel_ifp = NULL;
+ struct xroute *xroute;
+ struct route *route;
+ const unsigned char *last_prefix = NULL;
+ unsigned char last_plen = 0xFF;
+ int i;
+
+ if(ifp == NULL) {
+ struct interface *ifp_aux;
+ struct listnode *linklist_node = NULL;
+ FOR_ALL_INTERFACES(ifp_aux, linklist_node)
+ flushupdates(ifp_aux);
+ return;
+ }
+
+ babel_ifp = babel_get_if_nfo(ifp);
+ if(babel_ifp->num_buffered_updates > 0) {
+ struct buffered_update *b = babel_ifp->buffered_updates;
+ int n = babel_ifp->num_buffered_updates;
+
+ babel_ifp->buffered_updates = NULL;
+ babel_ifp->update_bufsize = 0;
+ babel_ifp->num_buffered_updates = 0;
+
+ if(!if_up(ifp))
+ goto done;
+
+ debugf(BABEL_DEBUG_COMMON," (flushing %d buffered updates on %s (%d))",
+ n, ifp->name, ifp->ifindex);
+
+ /* In order to send fewer update messages, we want to send updates
+ with the same router-id together, with IPv6 going out before IPv4. */
+
+ for(i = 0; i < n; i++) {
+ route = find_installed_route(b[i].prefix, b[i].plen);
+ if(route)
+ memcpy(b[i].id, route->src->id, 8);
+ else
+ memcpy(b[i].id, myid, 8);
+ }
+
+ qsort(b, n, sizeof(struct buffered_update), compare_buffered_updates);
+
+ for(i = 0; i < n; i++) {
+ unsigned short seqno;
+ unsigned short metric;
+
+ /* The same update may be scheduled multiple times before it is
+ sent out. Since our buffer is now sorted, it is enough to
+ compare with the previous update. */
+
+ if(last_prefix) {
+ if(b[i].plen == last_plen &&
+ memcmp(b[i].prefix, last_prefix, 16) == 0)
+ continue;
+ }
+
+ xroute = find_xroute(b[i].prefix, b[i].plen);
+ route = find_installed_route(b[i].prefix, b[i].plen);
+
+ if(xroute && (!route || xroute->metric <= kernel_metric)) {
+ really_send_update(ifp, myid,
+ xroute->prefix, xroute->plen,
+ myseqno, xroute->metric);
+ last_prefix = xroute->prefix;
+ last_plen = xroute->plen;
+ } else if(route) {
+ seqno = route->seqno;
+ metric = route_metric(route);
+ if(metric < INFINITY)
+ satisfy_request(route->src->prefix, route->src->plen,
+ seqno, route->src->id, ifp);
+ if((babel_ifp->flags & BABEL_IF_SPLIT_HORIZON) &&
+ route->neigh->ifp == ifp)
+ continue;
+ really_send_update(ifp, route->src->id,
+ route->src->prefix,
+ route->src->plen,
+ seqno, metric);
+ update_source(route->src, seqno, metric);
+ last_prefix = route->src->prefix;
+ last_plen = route->src->plen;
+ } else {
+ /* There's no route for this prefix. This can happen shortly
+ after an xroute has been retracted, so send a retraction. */
+ really_send_update(ifp, myid, b[i].prefix, b[i].plen,
+ myseqno, INFINITY);
+ }
+ }
+ schedule_flush_now(ifp);
+ done:
+ free(b);
+ }
+ babel_ifp->update_flush_timeout.tv_sec = 0;
+ babel_ifp->update_flush_timeout.tv_usec = 0;
+}
+
+static void
+schedule_update_flush(struct interface *ifp, int urgent)
+{
+ babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp);
+ unsigned msecs;
+ msecs = update_jitter(babel_ifp, urgent);
+ if(babel_ifp->update_flush_timeout.tv_sec != 0 &&
+ timeval_minus_msec(&babel_ifp->update_flush_timeout, &babel_now) < msecs)
+ return;
+ set_timeout(&babel_ifp->update_flush_timeout, msecs);
+}
+
+static void
+buffer_update(struct interface *ifp,
+ const unsigned char *prefix, unsigned char plen)
+{
+ babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp);
+ if(babel_ifp->num_buffered_updates > 0 &&
+ babel_ifp->num_buffered_updates >= babel_ifp->update_bufsize)
+ flushupdates(ifp);
+
+ if(babel_ifp->update_bufsize == 0) {
+ int n;
+ assert(babel_ifp->buffered_updates == NULL);
+ n = MAX(babel_ifp->bufsize / 16, 4);
+ again:
+ babel_ifp->buffered_updates = malloc(n *sizeof(struct buffered_update));
+ if(babel_ifp->buffered_updates == NULL) {
+ zlog_err("malloc(buffered_updates): %s", safe_strerror(errno));
+ if(n > 4) {
+ n = 4;
+ goto again;
+ }
+ return;
+ }
+ babel_ifp->update_bufsize = n;
+ babel_ifp->num_buffered_updates = 0;
+ }
+
+ memcpy(babel_ifp->buffered_updates[babel_ifp->num_buffered_updates].prefix,
+ prefix, 16);
+ babel_ifp->buffered_updates[babel_ifp->num_buffered_updates].plen = plen;
+ babel_ifp->num_buffered_updates++;
+}
+
+void
+send_update(struct interface *ifp, int urgent,
+ const unsigned char *prefix, unsigned char plen)
+{
+ babel_interface_nfo *babel_ifp = NULL;
+ int i;
+
+ if(ifp == NULL) {
+ struct interface *ifp_aux;
+ struct listnode *linklist_node = NULL;
+ struct route *route;
+ FOR_ALL_INTERFACES(ifp_aux, linklist_node)
+ send_update(ifp_aux, urgent, prefix, plen);
+ if(prefix) {
+ /* Since flushupdates only deals with non-wildcard interfaces, we
+ need to do this now. */
+ route = find_installed_route(prefix, plen);
+ if(route && route_metric(route) < INFINITY)
+ satisfy_request(prefix, plen, route->src->seqno, route->src->id,
+ NULL);
+ }
+ return;
+ }
+
+ if(!if_up(ifp))
+ return;
+
+ babel_ifp = babel_get_if_nfo(ifp);
+ if(prefix) {
+ if(!parasitic || find_xroute(prefix, plen)) {
+ debugf(BABEL_DEBUG_COMMON,"Sending update to %s for %s.",
+ ifp->name, format_prefix(prefix, plen));
+ buffer_update(ifp, prefix, plen);
+ }
+ } else {
+ if(!interface_idle(babel_ifp)) {
+ send_self_update(ifp);
+ if(!parasitic) {
+ debugf(BABEL_DEBUG_COMMON,"Sending update to %s for any.", ifp->name);
+ for(i = 0; i < numroutes; i++)
+ if(routes[i].installed)
+ buffer_update(ifp,
+ routes[i].src->prefix,
+ routes[i].src->plen);
+ }
+ }
+ set_timeout(&babel_ifp->update_timeout, babel_ifp->update_interval);
+ }
+ schedule_update_flush(ifp, urgent);
+}
+
+void
+send_update_resend(struct interface *ifp,
+ const unsigned char *prefix, unsigned char plen)
+{
+ int delay;
+
+ assert(prefix != NULL);
+
+ send_update(ifp, 1, prefix, plen);
+
+ delay = 2000;
+ delay = MIN(delay, wireless_hello_interval / 2);
+ delay = MIN(delay, wired_hello_interval / 2);
+ delay = MAX(delay, 10);
+ record_resend(RESEND_UPDATE, prefix, plen, 0, 0, NULL, delay);
+}
+
+void
+send_wildcard_retraction(struct interface *ifp)
+{
+ babel_interface_nfo *babel_ifp = NULL;
+ if(ifp == NULL) {
+ struct interface *ifp_aux;
+ struct listnode *linklist_node = NULL;
+ FOR_ALL_INTERFACES(ifp_aux, linklist_node)
+ send_wildcard_retraction(ifp_aux);
+ return;
+ }
+
+ if(!if_up(ifp))
+ return;
+
+ babel_ifp = babel_get_if_nfo(ifp);
+ start_message(ifp, MESSAGE_UPDATE, 10);
+ accumulate_byte(ifp, 0);
+ accumulate_byte(ifp, 0x40);
+ accumulate_byte(ifp, 0);
+ accumulate_byte(ifp, 0);
+ accumulate_short(ifp, 0xFFFF);
+ accumulate_short(ifp, myseqno);
+ accumulate_short(ifp, 0xFFFF);
+ end_message(ifp, MESSAGE_UPDATE, 10);
+
+ babel_ifp->have_buffered_id = 0;
+}
+
+void
+update_myseqno()
+{
+ myseqno = seqno_plus(myseqno, 1);
+ seqno_time = babel_now;
+}
+
+void
+send_self_update(struct interface *ifp)
+{
+ int i;
+
+ if(ifp == NULL) {
+ struct interface *ifp_aux;
+ struct listnode *linklist_node = NULL;
+ FOR_ALL_INTERFACES(ifp_aux, linklist_node) {
+ if(!if_up(ifp_aux))
+ continue;
+ send_self_update(ifp_aux);
+ }
+ return;
+ }
+
+ if(!interface_idle(babel_get_if_nfo(ifp))) {
+ debugf(BABEL_DEBUG_COMMON,"Sending self update to %s.", ifp->name);
+ for(i = 0; i < numxroutes; i++)
+ send_update(ifp, 0, xroutes[i].prefix, xroutes[i].plen);
+ }
+}
+
+void
+send_ihu(struct neighbour *neigh, struct interface *ifp)
+{
+ babel_interface_nfo *babel_ifp = NULL;
+ int rxcost, interval;
+ int ll;
+
+ if(neigh == NULL && ifp == NULL) {
+ struct interface *ifp_aux;
+ struct listnode *linklist_node = NULL;
+ FOR_ALL_INTERFACES(ifp_aux, linklist_node) {
+ if(if_up(ifp_aux))
+ continue;
+ send_ihu(NULL, ifp_aux);
+ }
+ return;
+ }
+
+ if(neigh == NULL) {
+ struct neighbour *ngh;
+ FOR_ALL_NEIGHBOURS(ngh) {
+ if(ngh->ifp == ifp)
+ send_ihu(ngh, ifp);
+ }
+ return;
+ }
+
+
+ if(ifp && neigh->ifp != ifp)
+ return;
+
+ ifp = neigh->ifp;
+ babel_ifp = babel_get_if_nfo(ifp);
+ if(!if_up(ifp))
+ return;
+
+ rxcost = neighbour_rxcost(neigh);
+ interval = (babel_ifp->hello_interval * 3 + 9) / 10;
+
+ /* Conceptually, an IHU is a unicast message. We usually send them as
+ multicast, since this allows aggregation into a single packet and
+ avoids an ARP exchange. If we already have a unicast message queued
+ for this neighbour, however, we might as well piggyback the IHU. */
+ debugf(BABEL_DEBUG_COMMON,"Sending %sihu %d on %s to %s.",
+ unicast_neighbour == neigh ? "unicast " : "",
+ rxcost,
+ neigh->ifp->name,
+ format_address(neigh->address));
+
+ ll = linklocal(neigh->address);
+
+ if(unicast_neighbour != neigh) {
+ start_message(ifp, MESSAGE_IHU, ll ? 14 : 22);
+ accumulate_byte(ifp, ll ? 3 : 2);
+ accumulate_byte(ifp, 0);
+ accumulate_short(ifp, rxcost);
+ accumulate_short(ifp, interval);
+ if(ll)
+ accumulate_bytes(ifp, neigh->address + 8, 8);
+ else
+ accumulate_bytes(ifp, neigh->address, 16);
+ end_message(ifp, MESSAGE_IHU, ll ? 14 : 22);
+ } else {
+ int rc;
+ rc = start_unicast_message(neigh, MESSAGE_IHU, ll ? 14 : 22);
+ if(rc < 0) return;
+ accumulate_unicast_byte(neigh, ll ? 3 : 2);
+ accumulate_unicast_byte(neigh, 0);
+ accumulate_unicast_short(neigh, rxcost);
+ accumulate_unicast_short(neigh, interval);
+ if(ll)
+ accumulate_unicast_bytes(neigh, neigh->address + 8, 8);
+ else
+ accumulate_unicast_bytes(neigh, neigh->address, 16);
+ end_unicast_message(neigh, MESSAGE_IHU, ll ? 14 : 22);
+ }
+}
+
+/* Send IHUs to all marginal neighbours */
+void
+send_marginal_ihu(struct interface *ifp)
+{
+ struct neighbour *neigh;
+ FOR_ALL_NEIGHBOURS(neigh) {
+ if(ifp && neigh->ifp != ifp)
+ continue;
+ if(neigh->txcost >= 384 || (neigh->reach & 0xF000) != 0xF000)
+ send_ihu(neigh, ifp);
+ }
+}
+
+void
+send_request(struct interface *ifp,
+ const unsigned char *prefix, unsigned char plen)
+{
+ babel_interface_nfo *babel_ifp = NULL;
+ int v4, len;
+
+ if(ifp == NULL) {
+ struct interface *ifp_aux;
+ struct listnode *linklist_node = NULL;
+ FOR_ALL_INTERFACES(ifp_aux, linklist_node) {
+ if(if_up(ifp_aux))
+ continue;
+ send_request(ifp_aux, prefix, plen);
+ }
+ return;
+ }
+
+ /* make sure any buffered updates go out before this request. */
+ flushupdates(ifp);
+
+ if(!if_up(ifp))
+ return;
+
+ babel_ifp = babel_get_if_nfo(ifp);
+ debugf(BABEL_DEBUG_COMMON,"sending request to %s for %s.",
+ ifp->name, prefix ? format_prefix(prefix, plen) : "any");
+ v4 = plen >= 96 && v4mapped(prefix);
+ len = !prefix ? 2 : v4 ? 6 : 18;
+
+ start_message(ifp, MESSAGE_REQUEST, len);
+ accumulate_byte(ifp, !prefix ? 0 : v4 ? 1 : 2);
+ accumulate_byte(ifp, !prefix ? 0 : v4 ? plen - 96 : plen);
+ if(prefix) {
+ if(v4)
+ accumulate_bytes(ifp, prefix + 12, 4);
+ else
+ accumulate_bytes(ifp, prefix, 16);
+ }
+ end_message(ifp, MESSAGE_REQUEST, len);
+}
+
+void
+send_unicast_request(struct neighbour *neigh,
+ const unsigned char *prefix, unsigned char plen)
+{
+ int rc, v4, len;
+
+ /* make sure any buffered updates go out before this request. */
+ flushupdates(neigh->ifp);
+
+ debugf(BABEL_DEBUG_COMMON,"sending unicast request to %s for %s.",
+ format_address(neigh->address),
+ prefix ? format_prefix(prefix, plen) : "any");
+ v4 = plen >= 96 && v4mapped(prefix);
+ len = !prefix ? 2 : v4 ? 6 : 18;
+
+ rc = start_unicast_message(neigh, MESSAGE_REQUEST, len);
+ if(rc < 0) return;
+ accumulate_unicast_byte(neigh, !prefix ? 0 : v4 ? 1 : 2);
+ accumulate_unicast_byte(neigh, !prefix ? 0 : v4 ? plen - 96 : plen);
+ if(prefix) {
+ if(v4)
+ accumulate_unicast_bytes(neigh, prefix + 12, 4);
+ else
+ accumulate_unicast_bytes(neigh, prefix, 16);
+ }
+ end_unicast_message(neigh, MESSAGE_REQUEST, len);
+}
+
+void
+send_multihop_request(struct interface *ifp,
+ const unsigned char *prefix, unsigned char plen,
+ unsigned short seqno, const unsigned char *id,
+ unsigned short hop_count)
+{
+ babel_interface_nfo *babel_ifp = NULL;
+ int v4, pb, len;
+
+ /* Make sure any buffered updates go out before this request. */
+ flushupdates(ifp);
+
+ if(ifp == NULL) {
+ struct interface *ifp_aux;
+ struct listnode *linklist_node = NULL;
+ FOR_ALL_INTERFACES(ifp_aux, linklist_node) {
+ if(!if_up(ifp_aux))
+ continue;
+ send_multihop_request(ifp_aux, prefix, plen, seqno, id, hop_count);
+ }
+ return;
+ }
+
+ if(!if_up(ifp))
+ return;
+
+ babel_ifp = babel_get_if_nfo(ifp);
+ debugf(BABEL_DEBUG_COMMON,"Sending request (%d) on %s for %s.",
+ hop_count, ifp->name, format_prefix(prefix, plen));
+ v4 = plen >= 96 && v4mapped(prefix);
+ pb = v4 ? ((plen - 96) + 7) / 8 : (plen + 7) / 8;
+ len = 6 + 8 + pb;
+
+ start_message(ifp, MESSAGE_MH_REQUEST, len);
+ accumulate_byte(ifp, v4 ? 1 : 2);
+ accumulate_byte(ifp, v4 ? plen - 96 : plen);
+ accumulate_short(ifp, seqno);
+ accumulate_byte(ifp, hop_count);
+ accumulate_byte(ifp, 0);
+ accumulate_bytes(ifp, id, 8);
+ if(prefix) {
+ if(v4)
+ accumulate_bytes(ifp, prefix + 12, pb);
+ else
+ accumulate_bytes(ifp, prefix, pb);
+ }
+ end_message(ifp, MESSAGE_MH_REQUEST, len);
+}
+
+void
+send_unicast_multihop_request(struct neighbour *neigh,
+ const unsigned char *prefix, unsigned char plen,
+ unsigned short seqno, const unsigned char *id,
+ unsigned short hop_count)
+{
+ int rc, v4, pb, len;
+
+ /* Make sure any buffered updates go out before this request. */
+ flushupdates(neigh->ifp);
+
+ debugf(BABEL_DEBUG_COMMON,"Sending multi-hop request to %s for %s (%d hops).",
+ format_address(neigh->address),
+ format_prefix(prefix, plen), hop_count);
+ v4 = plen >= 96 && v4mapped(prefix);
+ pb = v4 ? ((plen - 96) + 7) / 8 : (plen + 7) / 8;
+ len = 6 + 8 + pb;
+
+ rc = start_unicast_message(neigh, MESSAGE_MH_REQUEST, len);
+ if(rc < 0) return;
+ accumulate_unicast_byte(neigh, v4 ? 1 : 2);
+ accumulate_unicast_byte(neigh, v4 ? plen - 96 : plen);
+ accumulate_unicast_short(neigh, seqno);
+ accumulate_unicast_byte(neigh, hop_count);
+ accumulate_unicast_byte(neigh, 0);
+ accumulate_unicast_bytes(neigh, id, 8);
+ if(prefix) {
+ if(v4)
+ accumulate_unicast_bytes(neigh, prefix + 12, pb);
+ else
+ accumulate_unicast_bytes(neigh, prefix, pb);
+ }
+ end_unicast_message(neigh, MESSAGE_MH_REQUEST, len);
+}
+
+void
+send_request_resend(struct neighbour *neigh,
+ const unsigned char *prefix, unsigned char plen,
+ unsigned short seqno, unsigned char *id)
+{
+ int delay;
+
+ if(neigh)
+ send_unicast_multihop_request(neigh, prefix, plen, seqno, id, 127);
+ else
+ send_multihop_request(NULL, prefix, plen, seqno, id, 127);
+
+ delay = 2000;
+ delay = MIN(delay, wireless_hello_interval / 2);
+ delay = MIN(delay, wired_hello_interval / 2);
+ delay = MAX(delay, 10);
+ record_resend(RESEND_REQUEST, prefix, plen, seqno, id,
+ neigh ? neigh->ifp : NULL, delay);
+}
+
+void
+handle_request(struct neighbour *neigh, const unsigned char *prefix,
+ unsigned char plen, unsigned char hop_count,
+ unsigned short seqno, const unsigned char *id)
+{
+ struct xroute *xroute;
+ struct route *route;
+ struct neighbour *successor = NULL;
+
+ xroute = find_xroute(prefix, plen);
+ route = find_installed_route(prefix, plen);
+
+ if(xroute && (!route || xroute->metric <= kernel_metric)) {
+ if(hop_count > 0 && memcmp(id, myid, 8) == 0) {
+ if(seqno_compare(seqno, myseqno) > 0) {
+ if(seqno_minus(seqno, myseqno) > 100) {
+ /* Hopelessly out-of-date request */
+ return;
+ }
+ update_myseqno();
+ }
+ }
+ send_update(neigh->ifp, 1, prefix, plen);
+ return;
+ }
+
+ if(route &&
+ (memcmp(id, route->src->id, 8) != 0 ||
+ seqno_compare(seqno, route->seqno) <= 0)) {
+ send_update(neigh->ifp, 1, prefix, plen);
+ return;
+ }
+
+ if(hop_count <= 1)
+ return;
+
+ if(route && memcmp(id, route->src->id, 8) == 0 &&
+ seqno_minus(seqno, route->seqno) > 100) {
+ /* Hopelessly out-of-date */
+ return;
+ }
+
+ if(request_redundant(neigh->ifp, prefix, plen, seqno, id))
+ return;
+
+ /* Let's try to forward this request. */
+ if(route && route_metric(route) < INFINITY)
+ successor = route->neigh;
+
+ if(!successor || successor == neigh) {
+ /* We were about to forward a request to its requestor. Try to
+ find a different neighbour to forward the request to. */
+ struct route *other_route;
+
+ other_route = find_best_route(prefix, plen, 0, neigh);
+ if(other_route && route_metric(other_route) < INFINITY)
+ successor = other_route->neigh;
+ }
+
+ if(!successor || successor == neigh)
+ /* Give up */
+ return;
+
+ send_unicast_multihop_request(successor, prefix, plen, seqno, id,
+ hop_count - 1);
+ record_resend(RESEND_REQUEST, prefix, plen, seqno, id,
+ neigh->ifp, 0);
+}