예제 #1
0
static int
list_set_udel(struct ip_set *set, void *value, const struct ip_set_ext *ext,
	      struct ip_set_ext *mext, u32 flags)
{
	struct list_set *map = set->data;
	struct set_adt_elem *d = value;
	struct set_elem *e;
	u32 i;

	for (i = 0; i < map->size; i++) {
		e = list_set_elem(set, map, i);
		if (e->id == IPSET_INVALID_ID)
			return d->before != 0 ? -IPSET_ERR_REF_EXIST
					      : -IPSET_ERR_EXIST;
		else if (SET_WITH_TIMEOUT(set) &&
			 ip_set_timeout_expired(ext_timeout(e, set)))
			continue;
		else if (e->id != d->id)
			continue;

		if (d->before == 0)
			return list_set_del(set, i);
		else if (d->before > 0) {
			if (!id_eq(set, i + 1, d->refid))
				return -IPSET_ERR_REF_EXIST;
			return list_set_del(set, i);
		} else if (i == 0 || !id_eq(set, i - 1, d->refid))
			return -IPSET_ERR_REF_EXIST;
		else
			return list_set_del(set, i);
	}
	return -IPSET_ERR_EXIST;
}
예제 #2
0
static int
list_set_utest(struct ip_set *set, void *value, const struct ip_set_ext *ext,
	       struct ip_set_ext *mext, u32 flags)
{
	struct list_set *map = set->data;
	struct set_adt_elem *d = value;
	struct set_elem *e;
	u32 i;
	int ret;

	for (i = 0; i < map->size; i++) {
		e = list_set_elem(set, map, i);
		if (e->id == IPSET_INVALID_ID)
			return 0;
		else if (SET_WITH_TIMEOUT(set) &&
			 ip_set_timeout_expired(ext_timeout(e, set)))
			continue;
		else if (e->id != d->id)
			continue;

		if (d->before == 0)
			return 1;
		else if (d->before > 0)
			ret = id_eq(set, i + 1, d->refid);
		else
			ret = i > 0 && id_eq(set, i - 1, d->refid);
		return ret;
	}
	return 0;
}
예제 #3
0
static int
list_set_uadd(struct ip_set *set, void *value, const struct ip_set_ext *ext,
	      struct ip_set_ext *mext, u32 flags)
{
	struct list_set *map = set->data;
	struct set_adt_elem *d = value;
	struct set_elem *e;
	bool flag_exist = flags & IPSET_FLAG_EXIST;
	u32 i, ret = 0;

	if (SET_WITH_TIMEOUT(set))
		set_cleanup_entries(set);

	/* Check already added element */
	for (i = 0; i < map->size; i++) {
		e = list_set_elem(set, map, i);
		if (e->id == IPSET_INVALID_ID)
			goto insert;
		else if (e->id != d->id)
			continue;

		if ((d->before > 1 && !id_eq(set, i + 1, d->refid)) ||
		    (d->before < 0 &&
		     (i == 0 || !id_eq(set, i - 1, d->refid))))
			/* Before/after doesn't match */
			return -IPSET_ERR_REF_EXIST;
		if (!flag_exist)
			/* Can't re-add */
			return -IPSET_ERR_EXIST;
		/* Update extensions */
		ip_set_ext_destroy(set, e);

		if (SET_WITH_TIMEOUT(set))
			ip_set_timeout_set(ext_timeout(e, set), ext->timeout);
		if (SET_WITH_COUNTER(set))
			ip_set_init_counter(ext_counter(e, set), ext);
		if (SET_WITH_COMMENT(set))
			ip_set_init_comment(ext_comment(e, set), ext);
		/* Set is already added to the list */
		ip_set_put_byindex(map->net, d->id);
		return 0;
	}
insert:
	ret = -IPSET_ERR_LIST_FULL;
	for (i = 0; i < map->size && ret == -IPSET_ERR_LIST_FULL; i++) {
		e = list_set_elem(set, map, i);
		if (e->id == IPSET_INVALID_ID)
			ret = d->before != 0 ? -IPSET_ERR_REF_EXIST
				: list_set_add(set, i, d, ext);
		else if (e->id != d->refid)
			continue;
		else if (d->before > 0)
			ret = list_set_add(set, i, d, ext);
		else if (i + 1 < map->size)
			ret = list_set_add(set, i + 1, d, ext);
	}

	return ret;
}
예제 #4
0
int handle_ping_response(uint8_t* packet, uint32_t length, IP_Port source)
{
    pingres_t* p = (pingres_t*) packet;
    int       rc;
    uint64_t  ping_id;

    if (length != sizeof(pingres_t) || id_eq(&p->client_id, self_id))
        return 1;

    // Decrypt ping_id
    rc = decrypt_data((uint8_t*) &p->client_id,
                      self_secret_key,
                      (uint8_t*) &p->nonce,
                      (uint8_t*) &p->ping_id,
                      sizeof(ping_id) + ENCRYPTION_PADDING,
                      (uint8_t*) &ping_id);

    if (rc != sizeof(ping_id))
        return 1;

    // Make sure ping_id is correct
    if(!is_pinging(source, ping_id))
        return 1;

    // Associate source ip with client_id
    addto_lists(source, (uint8_t*) &p->client_id);
    return 0;
}
예제 #5
0
int handle_ping_request(uint8_t* packet, uint32_t length, IP_Port source)
{
    pingreq_t* p = (pingreq_t*) packet;
    int        rc;
    uint64_t   ping_id;

    if (length != sizeof(pingreq_t) || id_eq(&p->client_id, self_id))
        return 1;

    // Decrypt ping_id
    rc = decrypt_data((uint8_t*) &p->client_id,
                      self_secret_key,
                      (uint8_t*) &p->nonce,
                      (uint8_t*) &p->ping_id,
                      sizeof(ping_id) + ENCRYPTION_PADDING,
                      (uint8_t*) &ping_id);

    if (rc != sizeof(ping_id))
        return 1;

    // Send response
    send_ping_response(source, &p->client_id, ping_id);
    send_ping_request(source, &p->client_id); // Make this smarter?

    return 0;
}
예제 #6
0
int send_ping_request(IP_Port ipp, clientid_t* client_id)
{
    pingreq_t pk;
    int       rc;
    uint64_t  ping_id;

    if (is_pinging(ipp, 0) || id_eq(client_id, self_id))
        return 1;

    // Generate random ping_id
    ping_id = add_ping(ipp);

    pk.magic = PACKET_PING_REQ;
    id_cpy(&pk.client_id, self_id);     // Our pubkey
    random_nonce((uint8_t*) &pk.nonce); // Generate random nonce

    // Encrypt ping_id using recipient privkey
    rc = encrypt_data((uint8_t*) client_id,
                      self_secret_key,
                      (uint8_t*) &pk.nonce,
                      (uint8_t*) &ping_id, sizeof(ping_id),
                      (uint8_t*) &pk.ping_id);

    if (rc != sizeof(ping_id) + ENCRYPTION_PADDING)
        return 1;

    return sendpacket(ipp, (uint8_t*) &pk, sizeof(pk));
}
예제 #7
0
static int handle_ping_response(void *_dht, IP_Port source, uint8_t *packet, uint32_t length)
{
    DHT      *dht = _dht;
    int       rc;
    uint64_t  ping_id;

    if (length != DHT_PING_SIZE)
        return 1;

    PING *ping = dht->ping;

    if (id_eq(packet + 1, ping->c->self_public_key))
        return 1;

    // Decrypt ping_id
    rc = decrypt_data(packet + 1,
                      ping->c->self_secret_key,
                      packet + 1 + CLIENT_ID_SIZE,
                      packet + 1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES,
                      sizeof(ping_id) + ENCRYPTION_PADDING,
                      (uint8_t *) &ping_id);

    if (rc != sizeof(ping_id))
        return 1;

    /* Make sure ping_id is correct. */
    if (!is_pinging(ping, source, ping_id))
        return 1;

    // Associate source ip with client_id
    addto_lists(dht, source, packet + 1);
    return 0;
}
예제 #8
0
static int handle_ping_request(void *_dht, IP_Port source, uint8_t *packet, uint32_t length)
{
    DHT       *dht = _dht;
    int        rc;
    uint64_t   ping_id;

    if (length != DHT_PING_SIZE)
        return 1;

    PING *ping = dht->ping;

    if (id_eq(packet + 1, ping->c->self_public_key))
        return 1;

    // Decrypt ping_id
    rc = decrypt_data(packet + 1,
                      ping->c->self_secret_key,
                      packet + 1 + CLIENT_ID_SIZE,
                      packet + 1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES,
                      sizeof(ping_id) + ENCRYPTION_PADDING,
                      (uint8_t *) &ping_id);

    if (rc != sizeof(ping_id))
        return 1;

    // Send response
    send_ping_response(ping, source, packet + 1, ping_id);
    add_toping(ping, packet + 1, source);

    return 0;
}
예제 #9
0
int send_ping_request(PING *ping, IP_Port ipp, uint8_t *client_id)
{
    uint8_t   pk[DHT_PING_SIZE];
    int       rc;
    uint64_t  ping_id;

    if (is_pinging(ping, ipp, 0) || id_eq(client_id, ping->c->self_public_key))
        return 1;

    // Generate random ping_id.
    ping_id = add_ping(ping, ipp);

    pk[0] = NET_PACKET_PING_REQUEST;
    id_cpy(pk + 1, ping->c->self_public_key);     // Our pubkey
    new_nonce(pk + 1 + CLIENT_ID_SIZE); // Generate new nonce

    // Encrypt ping_id using recipient privkey
    rc = encrypt_data(client_id,
                      ping->c->self_secret_key,
                      pk + 1 + CLIENT_ID_SIZE,
                      (uint8_t *) &ping_id, sizeof(ping_id),
                      pk + 1 + CLIENT_ID_SIZE + crypto_box_NONCEBYTES);

    if (rc != sizeof(ping_id) + ENCRYPTION_PADDING)
        return 1;

    return sendpacket(ping->c->lossless_udp->net, ipp, pk, sizeof(pk));
}
예제 #10
0
static int
list_set_uadt(struct ip_set *set, struct nlattr *tb[],
	      enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
{
	struct list_set *map = set->data;
	bool with_timeout = with_timeout(map->timeout);
	bool flag_exist = flags & IPSET_FLAG_EXIST;
	int before = 0;
	u32 timeout = map->timeout;
	ip_set_id_t id, refid = IPSET_INVALID_ID;
	const struct set_elem *elem;
	struct ip_set *s;
	u32 i;
	int ret = 0;

	if (unlikely(!tb[IPSET_ATTR_NAME] ||
		     !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) ||
		     !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS)))
		return -IPSET_ERR_PROTOCOL;

	if (tb[IPSET_ATTR_LINENO])
		*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);

	id = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAME]), &s);
	if (id == IPSET_INVALID_ID)
		return -IPSET_ERR_NAME;
	/* "Loop detection" */
	if (s->type->features & IPSET_TYPE_NAME) {
		ret = -IPSET_ERR_LOOP;
		goto finish;
	}

	if (tb[IPSET_ATTR_CADT_FLAGS]) {
		u32 f = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
		before = f & IPSET_FLAG_BEFORE;
	}

	if (before && !tb[IPSET_ATTR_NAMEREF]) {
		ret = -IPSET_ERR_BEFORE;
		goto finish;
	}

	if (tb[IPSET_ATTR_NAMEREF]) {
		refid = ip_set_get_byname(nla_data(tb[IPSET_ATTR_NAMEREF]),
					  &s);
		if (refid == IPSET_INVALID_ID) {
			ret = -IPSET_ERR_NAMEREF;
			goto finish;
		}
		if (!before)
			before = -1;
	}
	if (tb[IPSET_ATTR_TIMEOUT]) {
		if (!with_timeout) {
			ret = -IPSET_ERR_TIMEOUT;
			goto finish;
		}
		timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]);
	}
	if (with_timeout && adt != IPSET_TEST)
		cleanup_entries(map);

	switch (adt) {
	case IPSET_TEST:
		for (i = 0; i < map->size && !ret; i++) {
			elem = list_set_elem(map, i);
			if (elem->id == IPSET_INVALID_ID ||
			    (before != 0 && i + 1 >= map->size))
				break;
			else if (with_timeout && list_set_expired(map, i))
				continue;
			else if (before > 0 && elem->id == id)
				ret = id_eq_timeout(map, i + 1, refid);
			else if (before < 0 && elem->id == refid)
				ret = id_eq_timeout(map, i + 1, id);
			else if (before == 0 && elem->id == id)
				ret = 1;
		}
		break;
	case IPSET_ADD:
		for (i = 0; i < map->size; i++) {
			elem = list_set_elem(map, i);
			if (elem->id != id)
				continue;
			if (!(with_timeout && flag_exist)) {
				ret = -IPSET_ERR_EXIST;
				goto finish;
			} else {
				struct set_telem *e = list_set_telem(map, i);

				if ((before > 1 &&
				     !id_eq(map, i + 1, refid)) ||
				    (before < 0 &&
				     (i == 0 || !id_eq(map, i - 1, refid)))) {
					ret = -IPSET_ERR_EXIST;
					goto finish;
				}
				e->timeout = ip_set_timeout_set(timeout);
				ip_set_put_byindex(id);
				ret = 0;
				goto finish;
			}
		}
		ret = -IPSET_ERR_LIST_FULL;
		for (i = 0; i < map->size && ret == -IPSET_ERR_LIST_FULL; i++) {
			elem = list_set_elem(map, i);
			if (elem->id == IPSET_INVALID_ID)
				ret = before != 0 ? -IPSET_ERR_REF_EXIST
					: list_set_add(map, i, id, timeout);
			else if (elem->id != refid)
				continue;
			else if (before > 0)
				ret = list_set_add(map, i, id, timeout);
			else if (i + 1 < map->size)
				ret = list_set_add(map, i + 1, id, timeout);
		}
		break;
	case IPSET_DEL:
		ret = -IPSET_ERR_EXIST;
		for (i = 0; i < map->size && ret == -IPSET_ERR_EXIST; i++) {
			elem = list_set_elem(map, i);
			if (elem->id == IPSET_INVALID_ID) {
				ret = before != 0 ? -IPSET_ERR_REF_EXIST
						  : -IPSET_ERR_EXIST;
				break;
			} else if (elem->id == id &&
				   (before == 0 ||
				    (before > 0 && id_eq(map, i + 1, refid))))
				ret = list_set_del(map, i);
			else if (elem->id == refid &&
				 before < 0 && id_eq(map, i + 1, id))
				ret = list_set_del(map, i + 1);
		}
		break;
	default:
		break;
	}

finish:
	if (refid != IPSET_INVALID_ID)
		ip_set_put_byindex(refid);
	if (adt != IPSET_ADD || ret)
		ip_set_put_byindex(id);

	return ip_set_eexist(ret, flags) ? 0 : ret;
}
예제 #11
0
int
parse_packet(const unsigned char *buf, int buflen,
             struct sockaddr_in6 *from, int unicast,
             struct interface *interface)
{
    unsigned char id[4];
    unsigned int eid = 42;      /* silence gcc */
    int have_id = 0;
    int i = 0, rc;
    int recompute = 0;

    if(debug_level >= 3)
        debugf("Received %d bytes.\n", buflen);

    while(i < buflen) {
        unsigned const char *tlv = buf + i;
        int type, bodylen;

        if(buflen - i < 4) {
            fprintf(stderr, "Received truncated TLV.\n");
            goto fail;
        }

        DO_NTOHS(type, tlv);
        DO_NTOHS(bodylen, tlv + 2);
        if(buflen - i < 4 + bodylen) {
            fprintf(stderr, "Received truncated TLV.\n");
            goto fail;
        }

        switch(type) {
        case 1: {
            debugf("   REQ-NETWORK-STATE\n");
            int i;
            buffer_network_state(from, NULL);
            for(i = 0; i < numnodes; i++)
                buffer_node_state(&nodes[i], 0, from, NULL);
            break;
        }
        case 2: {
            struct node *node;
            if(bodylen < 4) {
                fprintf(stderr, "Truncated REQ-NODE-STATE\n");
                goto fail;
            }
            debugf("   REQ-NODE-STATE %s\n", format_32(tlv + 4));
            node = find_node(tlv + 4, 0);
            if(node)
                buffer_node_state(node, 1, from, NULL);
            break;
        }
        case 3: {
            struct neighbour *neigh;
            if(bodylen < 8) {
                fprintf(stderr, "Truncated NODE-ENDPOINT.\n");
                break;
            }
            if(have_id) {
                fprintf(stderr, "Duplicate NODE-ENDPOINT.\n");
                break;
            }
            memcpy(id, tlv + 4, 4);
            DO_NTOHL(eid, tlv + 8);
            debugf("   NODE-ENDPOINT %s %u\n", format_32(id), eid);
            if(id_eq(id, myid)) {
                fprintf(stderr, "Node id collision.\n");
                goto fail;
            }
            have_id = 1;
            if(unicast) {
                neigh = find_neighbour(interface, id, eid, from);
                if(neigh && unicast)
                    neigh->last_contact = now;
            }
            break;
        }
        case 4: {
            int rc;
            unsigned char h[8];
            struct neighbour *neigh = NULL;
            struct timespec t;

            if(have_id)
                neigh = find_neighbour(interface, id, eid, NULL);

            if(bodylen < 8) {
                fprintf(stderr, "Truncated NETWORK-STATE.\n");
                goto fail;
            }

            rc = network_hash(h);
            if(rc < 0) {
                fprintf(stderr, "Eek!\n");
                goto fail;
            }

            if(memcmp(h, tlv + 4, 8) == 0) {
                debugf("   NETWORK-STATE %s (consistent)\n",
                       format_64(tlv + 4));
                if(neigh)
                    neigh->last_contact = now;
                trickle_reset(&interface->trickle, 1);
                break;
            }

            debugf("   NETWORK-STATE %s (inconsistent, %s)\n",
                   format_64(tlv + 4), format_64(h));
            /* But don't reset Trickle. */

            ts_add_msec(&t, &interface->last_request_sent, HNCP_I_min);
            if(ts_compare(&now, &t) >= 0) {
                interface->last_request_sent = now;
                debugf("-> REQ-NETWORK-STATE\n");
                buffer_tlv(1, NULL, 0, from, NULL);
            }
            break;
        }
        case 5: {
            struct node *node;
            unsigned int seqno;
            int msecs, mine;
            int datalen = bodylen - 20;

            if(bodylen < 20) {
                fprintf(stderr, "Truncated NODE-STATE.\n");
                goto fail;
            }

            if(!have_id) {
                fprintf(stderr, "NODE-STATE with no NODE-ENDPOINT.\n");
            }

            DO_NTOHL(seqno, tlv + 8);
            DO_NTOHL(msecs, tlv + 12);

            debugf("   NODE-STATE %s %d %d", format_32(tlv + 4), seqno, msecs);

            mine = id_eq(tlv + 4, myid);
            if(mine)
                debugf(" (mine)");

            node = find_node(tlv + 4, 0);

            if(node && (seqno - node->seqno) & 0x80000000) {
                debugf(" (older)\n");
            } else if(!node || seqno != node->seqno ||
                      memcmp(tlv + 16, node->datahash, 8) != 0) {
                debugf(" (newer%s, %s)\n",
                       datalen ? ", data" : "", format_64(tlv + 16));

                if(mine) {
                    fprintf(stderr,
                            "Duplicate node identifier -- reclaiming.\n");
                    node->seqno = seqno + 42;
                    break;
                }

                if(datalen) {
                    unsigned char *new_data;
                    unsigned char h[8];
                    int rc;

                    node_hash(h, tlv + 24, datalen);
                    if(memcmp(h, tlv + 16, 8) != 0) {
                        fprintf(stderr, "Corrupt hash.\n");
                        goto fail;
                    }

                    if(!node)
                        node = find_node(tlv + 4, 1);
                    if(!node) {
                        fprintf(stderr, "Couldn't create node.\n");
                        goto fail;
                    }

                    new_data = realloc(node->data, datalen);
                    if(new_data == NULL) {
                        fprintf(stderr, "Eek!\n");
                        goto fail;
                    }
                    node->interface = interface;
                    node->seqno = seqno;
                    ts_add_msec(&node->orig_time, &now, msecs + 1);
                    node->data = new_data;
                    memcpy(node->data, tlv + 24, datalen);
                    node->datalen = datalen;
                    memcpy(node->datahash, tlv + 16, 8);
                    rc = parse_node_state(node);
                    if(rc < 0)
                        fprintf(stderr, "Couldn't parse node state.\n");
                    else
                        trickle_reset_all();
                    recompute = 1;
                } else {
                    struct neighbour *neigh = NULL;
                    if(have_id)
                        neigh = find_neighbour(interface, id, eid, from);
                    if(neigh) {
                        debugf("-> REQ-NODE-STATE %s\n", format_32(tlv + 4));
                        buffer_tlv(2, tlv + 4, 4, from, NULL);
                    } else
                        fprintf(stderr, "No neighbour to send request to.\n");
                }
            } else {
                debugf(" (consistent)\n");
            }
            break;
        }
        default:
            if(debug_level >= 3)
                debugf("   %d: %d\n", type, bodylen);
            break;
            break;
        }
        i += 4 + bodylen;
        i += -i & 3;
    }

    rc = 1;
    goto done;

 fail:
    rc = -1;

 done:
    if(recompute) {
        int r;
        silly_walk(find_node(myid, 0));
        r = prefix_assignment(1);
        if(r > 0)
            republish(0, 1);
    }
    return rc;
}
예제 #12
0
int
parse_node_state(struct node *node)
{
    const unsigned char *buf = node->data;
    int buflen = node->datalen;
    int i = 0, j, rc;
    struct node_neighbour *nn = NULL;
    int numnn = 0, maxnn = 0;
    struct external **exts = NULL;
    int numexts = 0, maxexts = 0;

    for(j = 0; j < numneighs; j++) {
        if(id_eq(neighs[j].id, node->id))
            neighs[j].keepalive_interval = 0;
    }

    while(i < buflen) {
        unsigned const char *tlv = buf + i;
        int type, bodylen;

        if(buflen - i < 4) {
            fprintf(stderr, "Received truncated TLV.\n");
            goto fail;
        }

        DO_NTOHS(type, tlv);
        DO_NTOHS(bodylen, tlv + 2);
        if(buflen - i < 4 + bodylen) {
            fprintf(stderr, "Received truncated TLV.\n");
            goto fail;
        }

        switch(type) {
        case 7:
            fprintf(stderr, "Received fragmented nonsense.\n");
            goto fail;
        case 8: {
            if(bodylen < 12) {
                fprintf(stderr, "Truncated neighbour TLV.\n");
                break;
            }

            if(numnn >= maxnn) {
                struct node_neighbour *nnn =
                    realloc(nn,
                            (2 * maxnn + 2) *
                            sizeof(struct node_neighbour));
                if(nnn == NULL) {
                    fprintf(stderr, "Eek.\n");
                    /* Oh, well. */
                    break;
                }
                nn = nnn;
                maxnn = 2 * maxnn + 2;
            }

            memcpy(nn[numnn].neigh, tlv + 4, 4);
            DO_NTOHL(nn[numnn].nei, tlv + 8);
            DO_NTOHL(nn[numnn].lei, tlv + 12);
            debugf("     NEIGHBOR %s (nei=%u, lei=%u)\n",
                   format_32(nn[numnn].neigh),
                   nn[numnn].nei, nn[numnn].lei);
            numnn++;
            break;
        }
        case 9: {
            unsigned eid, interval;
            if(bodylen < 8) {
                fprintf(stderr, "Truncated KEEP-ALIVE-INTERVAL.\n");
                break;
            }
            DO_NTOHL(eid, tlv + 4);
            DO_NTOHL(interval, tlv + 8);
            debugf("    KEEP-ALIVE-INTERVAL %u (eid=%u)\n", interval, eid);
            for(j = 0; j < numneighs; j++) {
                if(id_eq(neighs[j].id, node->id) && neighs[j].eid == eid)
                    neighs[j].keepalive_interval =
                        interval ? interval : 0xFFFFFFFF;
            }
            break;
        }
        case 32: {
            if(bodylen < 4) {
                debugf("Truncated VERSION\n");
                goto fail;
            }
            debugf("     VERSION\n");
            memcpy(node->capabilities, tlv + 6, 2);
            break;
        }
        case 33: {
            struct external *ext;
            debugf("     EXTERNAL-CONNECTION\n");
            ext = parse_external(node, tlv + 4, bodylen);
            if(ext) {
                if(numexts >= maxexts) {
                    struct external **e =
                        realloc(exts,
                                (2 * maxexts + 2) * sizeof(struct external *));
                    if(e == NULL) {
                        fprintf(stderr, "Eek.\n");
                        break;
                    }
                    maxexts = 2 * maxexts + 2;
                    exts = e;
                }
                exts[numexts++] = ext;
            }
            break;
        }
        case 35: {
            unsigned char plen;
            struct in6_addr addr;
            int prio;
            unsigned int eid;
            struct prefix_list *pl;

            if(bodylen < 6) {
                fprintf(stderr, "Truncated ASSIGNED-PREFIX.\n");
                break;
            }
            DO_NTOHL(eid, tlv + 4);
            plen = *(tlv + 9);
            if(bodylen < 6 + (plen + 7) / 8) {
                fprintf(stderr, "Truncated ASSIGNED-PREFIX.\n");
                break;
            }
            prio = tlv[8] & 0x0F;
            parse_pref(&addr, tlv + 10, plen);
            debugf("     ASSIGNED-PREFIX ");
            debug_prefix_raw(&addr, plen);
            debugf(" (%d, prio=%d)\n", eid, prio);
            pl = prefix_list_cons(node->assigned,
                                  &addr, plen, node->id, eid, prio);
            if(pl)
                node->assigned = pl;
            break;
        }
        case 36: {
            unsigned int eid;
            struct prefix_list *pl;
            struct in6_addr addr;
            if(bodylen < 20) {
                fprintf(stderr, "Truncated NODE-ADDRESS.\n");
                break;
            }
            DO_NTOHL(eid, tlv + 4);
            memcpy(&addr, tlv + 8, 16);
            debugf("     NODE-ADDRESS ");
            debug_address(&addr);
            debugf(" %d\n",eid);
            pl = prefix_list_cons(node->addresses,
                                  &addr, 128, node->id, eid, 0);
            if(pl)
                node->addresses = pl;
            break;
        }
        default:
            if(debug_level >= 3)
                debugf("     %d: %d\n", type, bodylen);
            break;
        }
        i += 4 + bodylen;
        i += -i & 3;
    }

    rc = 1;
    goto done;

 fail:
    rc = -1;

 done:
    if(node->neighs)
        free(node->neighs);
    node->neighs = nn;
    node->numneighs = numnn;
    if(node->exts) {
        for(i = 0; i < node->numexts; i++)
            destroy_external(node->exts[i]);
        free(node->exts);
    }
    node->exts = exts;
    node->numexts = numexts;

    return rc;
}