| /* |
| * 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 <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 babel_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 babel_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) |
| { |
| 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; |
| |
| 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) |
| { |
| 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; |
| |
| 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 babel_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 babel_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); |
| } |