/* nhrp_server.c - NHRP request handling * * Copyright (C) 2007-2009 Timo Teräs * All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 or later as * published by the Free Software Foundation. * * See http://www.gnu.org/ for details. */ #include #include #include "nhrp_common.h" #include "nhrp_packet.h" #include "nhrp_interface.h" #include "nhrp_peer.h" #define NHRP_MAX_PENDING_REQUESTS 16 struct nhrp_pending_request { struct list_head request_list_entry; int natted; int num_ok, num_error; struct nhrp_packet *packet; struct nhrp_cie *cie; struct nhrp_payload *payload; struct nhrp_peer *peer, *rpeer; ev_tstamp now; }; static struct list_head request_list = LIST_INITIALIZER(request_list); static int num_pending_requests = 0; static void nhrp_server_start_cie_reg(struct nhrp_pending_request *pr); static struct nhrp_pending_request * nhrp_server_record_request(struct nhrp_packet *packet) { struct nhrp_pending_request *pr; pr = calloc(1, sizeof(struct nhrp_pending_request)); list_init(&pr->request_list_entry); if (pr != NULL) { num_pending_requests++; list_add(&pr->request_list_entry, &request_list); pr->packet = nhrp_packet_get(packet); pr->now = ev_now(); } return pr; } void nhrp_server_finish_request(struct nhrp_pending_request *pr) { list_del(&pr->request_list_entry); if (pr->rpeer != NULL) { struct nhrp_peer *peer = pr->rpeer; if (peer->flags & NHRP_PEER_FLAG_REPLACED) { /* The route peer entry was not accepted. We still * send the replies here, and cancel anything pending * so it'll get deleted cleanly on next put(). */ nhrp_peer_send_packet_queue(peer); nhrp_peer_cancel_async(peer); } nhrp_peer_put(pr->rpeer); } if (pr->peer != NULL) nhrp_peer_put(pr->peer); if (pr->packet != NULL) nhrp_packet_put(pr->packet); free(pr); num_pending_requests--; } static int nhrp_server_request_pending(struct nhrp_packet *packet) { struct nhrp_pending_request *r; list_for_each_entry(r, &request_list, request_list_entry) { if (nhrp_address_cmp(&packet->src_nbma_address, &r->packet->src_nbma_address) != 0) continue; if (nhrp_address_cmp(&packet->src_protocol_address, &r->packet->src_protocol_address) != 0) continue; if (nhrp_address_cmp(&packet->dst_protocol_address, &r->packet->dst_protocol_address) != 0) continue; /* Request from the same address being already processed */ return TRUE; } return FALSE; } static int nhrp_handle_resolution_request(struct nhrp_packet *packet) { char tmp[64], tmp2[64]; struct nhrp_payload *payload; struct nhrp_peer *peer = packet->dst_peer; struct nhrp_peer_selector sel; struct nhrp_cie *cie; nhrp_info("Received Resolution Request from proto src %s to %s", nhrp_address_format(&packet->src_protocol_address, sizeof(tmp), tmp), nhrp_address_format(&packet->dst_protocol_address, sizeof(tmp2), tmp2)); /* As first thing, flush all negative entries for the * requestor */ memset(&sel, 0, sizeof(sel)); sel.flags = NHRP_PEER_FIND_EXACT; sel.type_mask = BIT(NHRP_PEER_TYPE_NEGATIVE); sel.interface = packet->src_iface; sel.protocol_address = packet->src_protocol_address; nhrp_peer_foreach(nhrp_peer_remove_matching, NULL, &sel); /* Send reply */ packet->hdr.type = NHRP_PACKET_RESOLUTION_REPLY; packet->hdr.hop_count = NHRP_PACKET_DEFAULT_HOP_COUNT; packet->hdr.flags &= NHRP_FLAG_RESOLUTION_SOURCE_IS_ROUTER | NHRP_FLAG_RESOLUTION_SOURCE_STABLE | NHRP_FLAG_RESOLUTION_UNIQUE | NHRP_FLAG_RESOLUTION_NAT; packet->hdr.flags |= NHRP_FLAG_RESOLUTION_DESTINATION_STABLE | NHRP_FLAG_RESOLUTION_AUTHORATIVE; cie = nhrp_cie_alloc(); if (cie == NULL) return FALSE; cie->hdr = (struct nhrp_cie_header) { .code = NHRP_CODE_SUCCESS, .prefix_length = peer->prefix_length, }; if (peer->holding_time) cie->hdr.holding_time = htons(peer->holding_time); else if (peer->interface != NULL) cie->hdr.holding_time = htons(peer->interface->holding_time); else cie->hdr.holding_time = NHRP_DEFAULT_HOLDING_TIME; payload = nhrp_packet_payload(packet, NHRP_PAYLOAD_TYPE_ANY); nhrp_payload_free(payload); nhrp_payload_set_type(payload, NHRP_PAYLOAD_TYPE_CIE_LIST); nhrp_payload_add_cie(payload, cie); if (!nhrp_packet_reroute(packet, NULL)) return FALSE; peer = packet->dst_peer; cie->hdr.mtu = htons(peer->my_nbma_mtu); cie->nbma_address = peer->my_nbma_address; cie->protocol_address = packet->dst_iface->protocol_address; nhrp_info("Sending Resolution Reply %s/%d is-at %s (holdtime %d)", nhrp_address_format(&packet->dst_protocol_address, sizeof(tmp), tmp), cie->hdr.prefix_length, nhrp_address_format(&cie->nbma_address, sizeof(tmp2), tmp2), ntohs(cie->hdr.holding_time)); /* Reset NAT header to regenerate it for reply */ payload = nhrp_packet_extension(packet, NHRP_EXTENSION_NAT_ADDRESS | NHRP_EXTENSION_FLAG_NOCREATE, NHRP_PAYLOAD_TYPE_ANY); if (payload != NULL) { nhrp_payload_free(payload); nhrp_payload_set_type(payload, NHRP_PAYLOAD_TYPE_CIE_LIST); } return nhrp_packet_send(packet); } static int find_one(void *ctx, struct nhrp_peer *p) { return 1; } static int remove_old_registrations(void *ctx, struct nhrp_peer *p) { struct nhrp_peer *peer = (struct nhrp_peer *) ctx; /* If re-registration, mark the new connection up */ if (nhrp_address_cmp(&peer->protocol_address, &p->protocol_address) == 0 && nhrp_address_cmp(&peer->next_hop_address, &p->next_hop_address) == 0 && peer->prefix_length == p->prefix_length) peer->flags |= p->flags & (NHRP_PEER_FLAG_UP | NHRP_PEER_FLAG_LOWER_UP); p->flags |= NHRP_PEER_FLAG_REPLACED; nhrp_peer_remove(p); return 0; } static void nhrp_server_finish_reg(struct nhrp_pending_request *pr) { char tmp[64], tmp2[64]; struct nhrp_packet *packet = pr->packet; if (pr->rpeer != NULL && nhrp_packet_reroute(packet, pr->rpeer)) { nhrp_info("Sending Registration Reply from proto src %s to %s (%d bindings accepted, %d rejected)", nhrp_address_format(&packet->dst_protocol_address, sizeof(tmp), tmp), nhrp_address_format(&packet->src_protocol_address, sizeof(tmp2), tmp2), pr->num_ok, pr->num_error); nhrp_packet_send(packet); } else { /* We could not create route peer entry (likely out of memory), * so we can't do much more here. */ nhrp_info("Dropping Registration Reply from proto src %s to %s", nhrp_address_format(&packet->dst_protocol_address, sizeof(tmp), tmp), nhrp_address_format(&packet->src_protocol_address, sizeof(tmp2), tmp2)); } nhrp_server_finish_request(pr); } static void nhrp_server_finish_cie_reg_cb(union nhrp_peer_event e, int revents) { struct nhrp_peer *peer; struct nhrp_pending_request *pr; struct nhrp_packet *packet; struct nhrp_cie *cie; struct nhrp_peer_selector sel; char tmp[64], reason[32]; peer = nhrp_peer_from_event(e, revents); pr = peer->request; packet = pr->packet; cie = pr->cie; peer->request = NULL; nhrp_address_format(&peer->protocol_address, sizeof(tmp), tmp); if (revents != 0 && nhrp_peer_event_ok(e, revents)) { nhrp_debug("[%s] Peer registration authorized", tmp); /* Remove all old stuff and accept registration */ memset(&sel, 0, sizeof(sel)); sel.flags = NHRP_PEER_FIND_EXACT; sel.type_mask = NHRP_PEER_TYPEMASK_REMOVABLE; sel.interface = packet->src_iface; sel.protocol_address = peer->protocol_address; sel.prefix_length = peer->prefix_length; nhrp_peer_foreach(remove_old_registrations, peer, &sel); pr->num_ok++; cie->hdr.code = NHRP_CODE_SUCCESS; nhrp_peer_insert(peer); } else { if (revents == 0) nhrp_error("[%s] Peer registration failed: " "static entry exists", tmp); else nhrp_error("[%s] Peer registration failed: %s", tmp, nhrp_peer_event_reason(e, revents, sizeof(reason), reason)); pr->num_error++; cie->hdr.code = NHRP_CODE_ADMINISTRATIVELY_PROHIBITED; peer->flags |= NHRP_PEER_FLAG_REPLACED; } if (pr->rpeer == NULL) pr->rpeer = nhrp_peer_get(peer); nhrp_peer_put(peer); pr->peer = NULL; /* Process next CIE or finish registration handling */ if (cie->cie_list_entry.next != &pr->payload->u.cie_list) { pr->cie = list_next(&cie->cie_list_entry, struct nhrp_cie, cie_list_entry); nhrp_server_start_cie_reg(pr); } else { nhrp_server_finish_reg(pr); } } static void nhrp_server_start_cie_reg(struct nhrp_pending_request *pr) { struct nhrp_cie *cie = pr->cie; struct nhrp_packet *packet = pr->packet; struct nhrp_peer *peer; struct nhrp_peer_selector sel; peer = nhrp_peer_alloc(packet->src_iface); if (peer == NULL) { /* Mark all remaining registration requests as failed * due to lack of memory, and send reply */ for (; cie->cie_list_entry.next != &pr->payload->u.cie_list; cie = list_next(&cie->cie_list_entry, struct nhrp_cie, cie_list_entry)) { pr->num_error++; cie->hdr.code = NHRP_CODE_INSUFFICIENT_RESOURCES; } pr->num_error++; cie->hdr.code = NHRP_CODE_INSUFFICIENT_RESOURCES; nhrp_server_finish_reg(pr); return; } peer->type = NHRP_PEER_TYPE_DYNAMIC; peer->afnum = packet->hdr.afnum; peer->protocol_type = packet->hdr.protocol_type; peer->expire_time = pr->now + ntohs(cie->hdr.holding_time); peer->mtu = ntohs(cie->hdr.mtu); if (cie->nbma_address.addr_len != 0) peer->next_hop_address = cie->nbma_address; else peer->next_hop_address = packet->src_nbma_address; if (pr->natted) { peer->next_hop_nat_oa = peer->next_hop_address; peer->next_hop_address = packet->src_linklayer_address; } if (cie->protocol_address.addr_len != 0) peer->protocol_address = cie->protocol_address; else peer->protocol_address = packet->src_protocol_address; peer->prefix_length = cie->hdr.prefix_length; if (peer->prefix_length == 0xff) peer->prefix_length = peer->protocol_address.addr_len * 8; memset(&sel, 0, sizeof(sel)); sel.flags = NHRP_PEER_FIND_EXACT; sel.type_mask = ~NHRP_PEER_TYPEMASK_REMOVABLE; sel.interface = packet->src_iface; sel.protocol_address = peer->protocol_address; sel.prefix_length = peer->prefix_length; /* Link the created peer and pending request structures */ pr->peer = peer; peer->request = pr; /* Check that there is no conflicting peers */ if (nhrp_peer_foreach(find_one, peer, &sel) != 0) { cie->hdr.code = NHRP_CODE_ADMINISTRATIVELY_PROHIBITED; peer->flags |= NHRP_PEER_FLAG_REPLACED; nhrp_server_finish_cie_reg_cb(&peer->child, 0); } else { nhrp_peer_run_script(peer, "peer-register", nhrp_server_finish_cie_reg_cb); } } static int nhrp_handle_registration_request(struct nhrp_packet *packet) { char tmp[64], tmp2[64]; struct nhrp_payload *payload; struct nhrp_cie *cie; struct nhrp_pending_request *pr; int natted = 0; nhrp_info("Received Registration Request from proto src %s to %s", nhrp_address_format(&packet->src_protocol_address, sizeof(tmp), tmp), nhrp_address_format(&packet->dst_protocol_address, sizeof(tmp2), tmp2)); if (nhrp_server_request_pending(packet)) { nhrp_info("Already processing: resent packet ignored."); return TRUE; } if (num_pending_requests >= NHRP_MAX_PENDING_REQUESTS) { /* We should probably send Registration Reply with CIE * error NHRP_CODE_INSUFFICIENT_RESOURCES, or an Error * Indication. However, we do not have a direct peer entry * nor can we make sure that the lower layer is up, so * we just lamely drop the packet for now. */ nhrp_info("Too many pending requests: dropping this one"); return TRUE; } /* Cisco NAT extension, CIE added IF all of the following is true: * 1. We are the first hop registration server * (=no entries in forward transit CIE list) * 2. NAT is detected (link layer address != announced address) * 3. NAT extension is requested */ payload = nhrp_packet_extension(packet, NHRP_EXTENSION_FORWARD_TRANSIT_NHS | NHRP_EXTENSION_FLAG_NOCREATE, NHRP_PAYLOAD_TYPE_CIE_LIST); if (payload != NULL && list_empty(&payload->u.cie_list) && packet->src_linklayer_address.type != PF_UNSPEC && nhrp_address_cmp(&packet->src_nbma_address, &packet->src_linklayer_address) != 0) { natted = 1; payload = nhrp_packet_extension(packet, NHRP_EXTENSION_NAT_ADDRESS | NHRP_EXTENSION_FLAG_NOCREATE, NHRP_PAYLOAD_TYPE_CIE_LIST); if (payload != NULL) { cie = nhrp_cie_alloc(); if (cie != NULL) { cie->nbma_address = packet->src_linklayer_address; cie->protocol_address = packet->src_protocol_address; nhrp_payload_add_cie(payload, cie); } } } packet->hdr.type = NHRP_PACKET_REGISTRATION_REPLY; packet->hdr.hop_count = NHRP_PACKET_DEFAULT_HOP_COUNT; packet->hdr.flags &= NHRP_FLAG_REGISTRATION_UNIQUE | NHRP_FLAG_REGISTRATION_NAT; payload = nhrp_packet_payload(packet, NHRP_PAYLOAD_TYPE_CIE_LIST); if (list_empty(&payload->u.cie_list)) { nhrp_error("Received registration request has no CIEs"); return TRUE; } /* Start processing the CIEs */ pr = nhrp_server_record_request(packet); pr->natted = natted; pr->payload = payload; pr->cie = nhrp_payload_get_cie(payload, 1); nhrp_server_start_cie_reg(pr); return TRUE; } static int remove_peer_by_nbma(void *ctx, struct nhrp_peer *peer) { struct nhrp_address *nbma = ctx; struct nhrp_address *peer_nbma = NULL; if (!nhrp_address_is_any_addr(nbma)) { if (peer->type == NHRP_PEER_TYPE_SHORTCUT_ROUTE) { struct nhrp_peer *nexthop; nexthop = nhrp_peer_route(peer->interface, &peer->next_hop_address, NHRP_PEER_FIND_EXACT, NHRP_PEER_TYPEMASK_ADJACENT); if (nexthop != NULL) peer_nbma = &nexthop->next_hop_address; } else { peer_nbma = &peer->next_hop_address; } } else { peer_nbma = nbma; } if (peer_nbma != NULL && nhrp_address_cmp(peer_nbma, nbma) == 0) nhrp_peer_remove(peer); return 0; } static int nhrp_handle_purge_request(struct nhrp_packet *packet) { char tmp[64], tmp2[64]; struct nhrp_peer_selector sel; struct nhrp_payload *payload; struct nhrp_cie *cie; int flags, ret = TRUE; nhrp_info("Received Purge Request from proto src %s to %s", nhrp_address_format(&packet->src_protocol_address, sizeof(tmp), tmp), nhrp_address_format(&packet->dst_protocol_address, sizeof(tmp2), tmp2)); flags = packet->hdr.flags; packet->hdr.type = NHRP_PACKET_PURGE_REPLY; packet->hdr.hop_count = NHRP_PACKET_DEFAULT_HOP_COUNT; packet->hdr.flags = 0; if (!(flags & NHRP_FLAG_PURGE_NO_REPLY)) { if (nhrp_packet_reroute(packet, NULL)) ret = nhrp_packet_send(packet); else ret = FALSE; } payload = nhrp_packet_payload(packet, NHRP_PAYLOAD_TYPE_CIE_LIST); list_for_each_entry(cie, &payload->u.cie_list, cie_list_entry) { nhrp_info("Purge proto %s/%d nbma %s", nhrp_address_format(&cie->protocol_address, sizeof(tmp), tmp), cie->hdr.prefix_length, nhrp_address_format(&cie->nbma_address, sizeof(tmp2), tmp2)); memset(&sel, 0, sizeof(sel)); sel.flags = NHRP_PEER_FIND_EXACT; sel.type_mask = NHRP_PEER_TYPEMASK_REMOVABLE; sel.interface = packet->src_iface; sel.protocol_address = cie->protocol_address; sel.prefix_length = cie->hdr.prefix_length; nhrp_peer_foreach(remove_peer_by_nbma, &cie->nbma_address, &sel); nhrp_rate_limit_clear(&cie->protocol_address, cie->hdr.prefix_length); } return ret; } static int nhrp_handle_traffic_indication(struct nhrp_packet *packet) { char tmp[64], tmp2[64]; struct nhrp_address dst; struct nhrp_payload *pl; pl = nhrp_packet_payload(packet, NHRP_PAYLOAD_TYPE_RAW); if (pl == NULL) return FALSE; if (!nhrp_address_parse_packet(packet->hdr.protocol_type, pl->u.raw->length, pl->u.raw->data, NULL, &dst)) return FALSE; /* Shortcuts enabled? */ if (packet->src_iface->flags & NHRP_INTERFACE_FLAG_SHORTCUT) { nhrp_info("Traffic Indication from proto src %s; " "about packet to %s", nhrp_address_format(&packet->src_protocol_address, sizeof(tmp), tmp), nhrp_address_format(&dst, sizeof(tmp2), tmp2)); nhrp_peer_traffic_indication(packet->src_iface, packet->hdr.afnum, &dst); } else { nhrp_info("Traffic Indication ignored from proto src %s; " "about packet to %s", nhrp_address_format(&packet->src_protocol_address, sizeof(tmp), tmp), nhrp_address_format(&dst, sizeof(tmp2), tmp2)); } return TRUE; } void server_init(void) { nhrp_packet_hook_request(NHRP_PACKET_RESOLUTION_REQUEST, nhrp_handle_resolution_request); nhrp_packet_hook_request(NHRP_PACKET_REGISTRATION_REQUEST, nhrp_handle_registration_request); nhrp_packet_hook_request(NHRP_PACKET_PURGE_REQUEST, nhrp_handle_purge_request); nhrp_packet_hook_request(NHRP_PACKET_TRAFFIC_INDICATION, nhrp_handle_traffic_indication); }