From e7341ada4cdbb7ac3cfa0776e83e30459dc76ef5 Mon Sep 17 00:00:00 2001 From: kefren Date: Wed, 8 Dec 2010 07:20:14 +0000 Subject: [PATCH] Add ldpd, a RFC 3036 compatible LDP speaker. --- usr.sbin/Makefile | 4 +- usr.sbin/ldpd/Makefile | 26 + usr.sbin/ldpd/TODO | 12 + usr.sbin/ldpd/fsm.c | 220 +++++++ usr.sbin/ldpd/fsm.h | 43 ++ usr.sbin/ldpd/label.c | 279 +++++++++ usr.sbin/ldpd/label.h | 73 +++ usr.sbin/ldpd/ldp.d | 50 ++ usr.sbin/ldpd/ldp.h | 79 +++ usr.sbin/ldpd/ldp_command.c | 564 +++++++++++++++++ usr.sbin/ldpd/ldp_command.h | 79 +++ usr.sbin/ldpd/ldp_errors.c | 110 ++++ usr.sbin/ldpd/ldp_errors.h | 58 ++ usr.sbin/ldpd/ldp_peer.c | 498 +++++++++++++++ usr.sbin/ldpd/ldp_peer.h | 118 ++++ usr.sbin/ldpd/ldpd.8 | 94 +++ usr.sbin/ldpd/main.c | 146 +++++ usr.sbin/ldpd/mpls_interface.c | 226 +++++++ usr.sbin/ldpd/mpls_interface.h | 43 ++ usr.sbin/ldpd/mpls_routes.c | 893 +++++++++++++++++++++++++++ usr.sbin/ldpd/mpls_routes.h | 78 +++ usr.sbin/ldpd/notifications.c | 79 +++ usr.sbin/ldpd/notifications.h | 98 +++ usr.sbin/ldpd/pdu.c | 90 +++ usr.sbin/ldpd/pdu.h | 58 ++ usr.sbin/ldpd/socketops.c | 1055 ++++++++++++++++++++++++++++++++ usr.sbin/ldpd/socketops.h | 69 +++ usr.sbin/ldpd/tlv.c | 75 +++ usr.sbin/ldpd/tlv.h | 206 +++++++ usr.sbin/ldpd/tlv_stack.c | 415 +++++++++++++ usr.sbin/ldpd/tlv_stack.h | 54 ++ 31 files changed, 5890 insertions(+), 2 deletions(-) create mode 100644 usr.sbin/ldpd/Makefile create mode 100644 usr.sbin/ldpd/TODO create mode 100644 usr.sbin/ldpd/fsm.c create mode 100644 usr.sbin/ldpd/fsm.h create mode 100644 usr.sbin/ldpd/label.c create mode 100644 usr.sbin/ldpd/label.h create mode 100644 usr.sbin/ldpd/ldp.d create mode 100644 usr.sbin/ldpd/ldp.h create mode 100644 usr.sbin/ldpd/ldp_command.c create mode 100644 usr.sbin/ldpd/ldp_command.h create mode 100644 usr.sbin/ldpd/ldp_errors.c create mode 100644 usr.sbin/ldpd/ldp_errors.h create mode 100644 usr.sbin/ldpd/ldp_peer.c create mode 100644 usr.sbin/ldpd/ldp_peer.h create mode 100644 usr.sbin/ldpd/ldpd.8 create mode 100644 usr.sbin/ldpd/main.c create mode 100644 usr.sbin/ldpd/mpls_interface.c create mode 100644 usr.sbin/ldpd/mpls_interface.h create mode 100644 usr.sbin/ldpd/mpls_routes.c create mode 100644 usr.sbin/ldpd/mpls_routes.h create mode 100644 usr.sbin/ldpd/notifications.c create mode 100644 usr.sbin/ldpd/notifications.h create mode 100644 usr.sbin/ldpd/pdu.c create mode 100644 usr.sbin/ldpd/pdu.h create mode 100644 usr.sbin/ldpd/socketops.c create mode 100644 usr.sbin/ldpd/socketops.h create mode 100644 usr.sbin/ldpd/tlv.c create mode 100644 usr.sbin/ldpd/tlv.h create mode 100644 usr.sbin/ldpd/tlv_stack.c create mode 100644 usr.sbin/ldpd/tlv_stack.h diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index 0612fa246c1f..b2f972586e72 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.248 2010/12/05 05:59:17 christos Exp $ +# $NetBSD: Makefile,v 1.249 2010/12/08 07:20:14 kefren Exp $ # from: @(#)Makefile 5.20 (Berkeley) 6/12/93 .include @@ -11,7 +11,7 @@ SUBDIR= ac accton acpitools altq apm apmd arp bad144 bootp \ gpioctl grfconfig grfinfo gspa hdaudioctl hilinfo ifwatchd inetd \ installboot \ iopctl iostat ipwctl irdaattach isdn iteconfig iwictl\ - kgmon lastlogin link lmcconfig lockstat lpr mailwrapper makefs \ + kgmon lastlogin ldpd link lmcconfig lockstat lpr mailwrapper makefs \ map-mbone mdconfig memswitch mlxctl mmcformat mopd mountd moused \ mrinfo mrouted mscdlabel mtrace \ mtree ndbootd ndiscvt netgroup_mkdb nfsd ofctl paxctl pcictl \ diff --git a/usr.sbin/ldpd/Makefile b/usr.sbin/ldpd/Makefile new file mode 100644 index 000000000000..67a66dfddc41 --- /dev/null +++ b/usr.sbin/ldpd/Makefile @@ -0,0 +1,26 @@ +# $NetBSD: Makefile,v 1.1 2010/12/08 07:20:14 kefren Exp $ + +.include + +PROG= ldpd +MAN= ldpd.8 + +SRCS= fsm.c \ + label.c \ + ldp_command.c \ + ldp_errors.c \ + ldp_peer.c \ + main.c \ + mpls_interface.c \ + mpls_routes.c \ + notifications.c \ + pdu.c \ + socketops.c \ + tlv.c \ + tlv_stack.c + +CFLAGS= -Wall -g + +LDADD+= -lcrypt + +.include diff --git a/usr.sbin/ldpd/TODO b/usr.sbin/ldpd/TODO new file mode 100644 index 000000000000..dc068e3f65e1 --- /dev/null +++ b/usr.sbin/ldpd/TODO @@ -0,0 +1,12 @@ +# $NetBSD: TODO,v 1.1 2010/12/08 07:20:14 kefren Exp $ + +TODO +==== + +* send notifications for every error I encounter - kefren +* document better Label Distribution (downstream on demand or + unsolicited downstream), distribution control (independent or + ordered) and retention mode (liberal or conservative) - kefren +* config/options file +* future: IPv6 support. Have no infrastructure to test right + now - kefren diff --git a/usr.sbin/ldpd/fsm.c b/usr.sbin/ldpd/fsm.c new file mode 100644 index 000000000000..32d692813f04 --- /dev/null +++ b/usr.sbin/ldpd/fsm.c @@ -0,0 +1,220 @@ +/* $NetBSD: fsm.c,v 1.1 2010/12/08 07:20:14 kefren Exp $ */ + +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Mihai Chelaru + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "ldp.h" +#include "ldp_peer.h" +#include "socketops.h" +#include "ldp_errors.h" +#include "fsm.h" + +char my_ldp_id[20]; +struct sockaddr mplssockaddr; + +/* Processing a hello */ +void +run_ldp_hello(struct ldp_pdu * pduid, struct hello_tlv * ht, + struct in_addr * padd, struct in_addr * ladd, int mysock) +{ + struct ldp_peer *peer = NULL; + struct in_addr peer_addr; + struct transport_address_tlv *trtlv; + struct hello_info *hi; + + if ((!pduid) || (!ht)) + return; + + debugp("Received it on address: %s\n", inet_ntoa(*ladd)); + debugp("Hello: Type: 0x%.4X Length: %.2d ID: %.8X\n", ht->type, + ht->length, ht->messageid); + + /* Add it to hello list or just update timer */ + SLIST_FOREACH(hi, &hello_info_head, infos) + if (hi->ldp_id.s_addr == pduid->ldp_id.s_addr) + break; + if (hi == NULL) { + hi = (struct hello_info *)malloc(sizeof(struct hello_info)); + if (!hi) { + fatalp("Cannot alloc a hello info"); + return; + } + hi->ldp_id.s_addr = pduid->ldp_id.s_addr; + SLIST_INSERT_HEAD(&hello_info_head, hi, infos); + } else + /* Just update timer */ + hi->keepalive = LDP_HELLO_KEEP; + + if (ht->length > 4) { /* Common hello parameters */ + ht->ch.type = ntohs(ht->ch.type); + ht->ch.length = ntohs(ht->ch.length); + ht->ch.holdtime = ntohs(ht->ch.holdtime); + ht->ch.res = ntohs(ht->ch.res); + debugp("Common hello Type: 0x%.4X Length: %.2d R:%d T:%d" + "Hold time: %d\n", ht->ch.type, ht->ch.length, + ht->ch.tr / 2, ht->ch.tr % 2, ht->ch.holdtime); + if (ht->ch.holdtime) + hi->keepalive = ht->ch.holdtime; + if (!get_ldp_peer(&pduid->ldp_id)) { + /* First of all set peer_addr to announced LDP_ID */ + memcpy(&peer_addr, &pduid->ldp_id, + sizeof(struct in_addr)); + /* + * Now let's see if there is any transport TLV in + * there + */ + if (pduid->length - PDU_PAYLOAD_LENGTH - + sizeof(struct hello_tlv) > 3) { + trtlv = (struct transport_address_tlv *) &ht[1]; + if (trtlv->type == TLV_IPV4_TRANSPORT) + memcpy(&peer_addr, &trtlv->address, + sizeof(struct in_addr)); + } + /* + * RFC says: If A1 > A2, LSR1 plays the active role; + * otherwise it is passive. + */ + if (ntohl(peer_addr.s_addr) < ntohl(ladd->s_addr)) { +#define TRADDR (trtlv && trtlv->type == TLV_IPV4_TRANSPORT) ? &peer_addr : NULL + peer = ldp_peer_new(&pduid->ldp_id, padd, + TRADDR, ht->ch.holdtime, 0); + if (!peer) + return; + if (peer && peer->state == LDP_PEER_CONNECTED) + send_initialize(peer); + } + } + } +} + +struct address_list_tlv * +build_address_list_tlv(void) +{ + struct address_list_tlv *t; + struct ifaddrs *ifa, *ifb; + struct sockaddr_in *sa; + struct in_addr *ia; + uint16_t adrcount = 0; + + if (getifaddrs(&ifa) == -1) + return NULL; + + /* Find out the number of addresses */ + /* Ignore loopback */ + for (ifb = ifa; ifb; ifb = ifb->ifa_next) + if ((ifb->ifa_addr->sa_family == AF_INET) && + (ifb->ifa_flags & IFF_UP)) { + sa = (struct sockaddr_in *) ifb->ifa_addr; + if (sa->sin_addr.s_addr << 24 >> 24 != 127) + adrcount++; + } + t = (struct address_list_tlv *) malloc(sizeof(struct address_list_tlv) + + (adrcount - 1) * sizeof(struct in_addr)); + + if (!t) { + fatalp("build_address_list_tlv: malloc problem\n"); + return NULL; + } + + t->type = htons(LDP_ADDRESS); + t->length = htons(sizeof(struct address_list_tlv) - TLV_TYPE_LENGTH + + (adrcount - 1) * sizeof(struct in_addr)); + t->messageid = htonl(get_message_id()); + + t->a_type = htons(TLV_ADDRESS_LIST); + t->a_length = htons(sizeof(t->a_af) + + adrcount * sizeof(struct in_addr)); + t->a_af = htons(LDP_AF_INET); + + ia = &t->a_address; + for (adrcount = 0, ifb = ifa; ifb; ifb = ifb->ifa_next) { + if ((ifb->ifa_addr->sa_family != AF_INET) || + (!(ifb->ifa_flags & IFF_UP)) || + (ifb->ifa_flags & IFF_LOOPBACK)) + continue; + sa = (struct sockaddr_in *) ifb->ifa_addr; + memcpy(&ia[adrcount], &sa->sin_addr, sizeof(struct in_addr)); + adrcount++; + } + freeifaddrs(ifa); + + add_my_if_addrs(ia, adrcount); + return t; +} + +/* + * Calculate LDP ID + * Get also mpls pseudo-interface address + */ +int +set_my_ldp_id() +{ + struct ifaddrs *ifa, *ifb; + struct in_addr a; + struct sockaddr_in *sa; + + a.s_addr = 0; + my_ldp_id[0] = 0; + mplssockaddr.sa_len = 0; + + if (getifaddrs(&ifa) == -1) + return LDP_E_GENERIC; + + for (ifb = ifa; ifb; ifb = ifb->ifa_next) + if(ifb->ifa_flags & IFF_UP) { + if (strncmp("mpls", ifb->ifa_name, 4) == 0 && + ifb->ifa_addr->sa_family == AF_LINK) + memcpy(&mplssockaddr, ifb->ifa_addr, + ifb->ifa_addr->sa_len); + + if (ifb->ifa_addr->sa_family != AF_INET) + continue; + + sa = (struct sockaddr_in *) ifb->ifa_addr; + if (ntohl(sa->sin_addr.s_addr) >> 24 == 127) + continue; /* No 127/8 */ + if (ntohl(sa->sin_addr.s_addr) > ntohl(a.s_addr)) + a.s_addr = sa->sin_addr.s_addr; + } + freeifaddrs(ifa); + debugp("LDP ID: %s\n", inet_ntoa(a)); + strlcpy(my_ldp_id, inet_ntoa(a), INET_ADDRSTRLEN); + return LDP_E_OK; +} diff --git a/usr.sbin/ldpd/fsm.h b/usr.sbin/ldpd/fsm.h new file mode 100644 index 000000000000..0c0f1a696973 --- /dev/null +++ b/usr.sbin/ldpd/fsm.h @@ -0,0 +1,43 @@ +/* $NetBSD: fsm.h,v 1.1 2010/12/08 07:20:14 kefren Exp $ */ + +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Mihai Chelaru + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _FSM_H_ +#define _FSM_H_ + +#include "tlv.h" +#include "pdu.h" + +void run_ldp_hello(struct ldp_pdu *, struct hello_tlv *, + struct in_addr *, struct in_addr *, int); +struct address_list_tlv * build_address_list_tlv(void); +int set_my_ldp_id(void); + +#endif /* !_FSM_H_ */ diff --git a/usr.sbin/ldpd/label.c b/usr.sbin/ldpd/label.c new file mode 100644 index 000000000000..dd8c76ec5a36 --- /dev/null +++ b/usr.sbin/ldpd/label.c @@ -0,0 +1,279 @@ +/* $NetBSD: label.c,v 1.1 2010/12/08 07:20:14 kefren Exp $ */ + +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Mihai Chelaru + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include + +#include "ldp.h" +#include "tlv_stack.h" +#include "mpls_routes.h" +#include "label.h" +#include "ldp_errors.h" + +void +label_init() +{ + SLIST_INIT(&label_head); +} + +/* + * if binding == 0 it receives a free one + */ +struct label * +label_add(union sockunion * so_dest, union sockunion * so_pref, + union sockunion * so_gate, uint32_t binding, struct ldp_peer * p, + uint32_t label) +{ + struct label *l; + char spreftmp[INET_ADDRSTRLEN]; + + l = (struct label *) malloc(sizeof(struct label)); + memset(l, 0, sizeof(struct label)); + + if (!l) { + fatalp("label_add: malloc problem\n"); + return NULL; + } + + assert(so_dest); + assert(so_pref); + assert(so_dest->sa.sa_family == so_pref->sa.sa_family); + + memcpy(&l->so_dest, so_dest, sizeof(union sockunion)); + memcpy(&l->so_pref, so_pref, sizeof(union sockunion)); + + if (so_gate) + memcpy(&l->so_gate, so_gate, sizeof(union sockunion)); + if (binding) + l->binding = binding; + else + l->binding = get_free_local_label(); + l->p = p; + l->label = label; + + SLIST_INSERT_HEAD(&label_head, l, labels); + + strlcpy(spreftmp, union_ntoa(so_pref), INET_ADDRSTRLEN); + warnp("[label_add] added binding %d for %s/%s\n", l->binding, + union_ntoa(so_dest), spreftmp); + + send_label_tlv_to_all(&(so_dest->sin.sin_addr), + from_union_to_cidr(so_pref), l->binding); + return l; +} + +/* Unlink a label */ +void +label_del(struct label * l) +{ + warnp("[label_del] deleted binding %d for %s\n", l->binding, + union_ntoa(&l->so_dest)); + SLIST_REMOVE(&label_head, l, label, labels); + free(l); +} + +/* + * Delete or Reuse the old IPv4 route, delete MPLS route (if any) + */ +void +label_reattach_route(struct label *l, int readd) +{ + union sockunion *u; + union sockunion emptysu; + struct rt_msg rg; + int oldbinding = l->binding; + + warnp("[label_reattach_route] binding %d deleted\n", + l->binding); + + l->p = NULL; + l->binding = MPLS_LABEL_IMPLNULL; + + /* No gateway ? */ + memset(&emptysu, 0, sizeof (union sockunion)); + if (memcmp(&l->so_gate, &emptysu, sizeof(union sockunion)) == 0) + return; + + if (l->label != MPLS_LABEL_IMPLNULL && readd == LDP_READD_CHANGE) { + /* Delete and re-add IPv4 route */ + if (get_route(&rg, &l->so_dest, &l->so_pref, 1) == LDP_E_OK) { + delete_route(&l->so_dest, &l->so_pref, NO_FREESO); + add_route(&l->so_dest, &l->so_pref, &l->so_gate, NULL, NULL, + NO_FREESO, RTM_READD); + } else if (from_union_to_cidr(&l->so_pref) == 32 && + l->so_dest.sa.sa_family == AF_INET && + get_route(&rg, &l->so_dest, NULL, 1) == LDP_E_OK) { + delete_route(&l->so_dest, NULL, NO_FREESO); + add_route(&l->so_dest, NULL, &l->so_gate, NULL, NULL, + NO_FREESO, RTM_READD); + } else + add_route(&l->so_dest, &l->so_pref, + &l->so_gate, NULL, NULL, NO_FREESO, RTM_READD); + } else + if (readd != LDP_READD_NODEL) + delete_route(&l->so_dest, &l->so_pref, NO_FREESO); + + l->label = 0; + + /* Deletes pure MPLS route */ + if (oldbinding >= MIN_LABEL) { + u = make_mpls_union(oldbinding); + delete_route(u, NULL, FREESO); + } +} +/* + * Get a label by dst and pref + */ +struct label* +label_get(union sockunion *sodest, union sockunion *sopref) +{ + struct label *l; + + SLIST_FOREACH (l, &label_head, labels) + if (sodest->sin.sin_addr.s_addr == + l->so_dest.sin.sin_addr.s_addr && + sopref->sin.sin_addr.s_addr == + l->so_pref.sin.sin_addr.s_addr) + return l; + return NULL; +} + +/* + * Find all labels that points to a peer + * and reattach them to IPv4 + */ +void +label_reattach_all_peer_labels(struct ldp_peer *p, int readd) +{ + struct label *l; + + SLIST_FOREACH(l, &label_head, labels) + if (l->p == p) + label_reattach_route(l, readd); +} + +/* + * Find all labels that points to a peer + * and delete them + */ +void +del_all_peer_labels(struct ldp_peer * p, int readd) +{ + struct label *l; + int do_remove = 1; + + while(do_remove == 1) { + do_remove = 0; + SLIST_FOREACH(l, &label_head, labels) { + if(l->p != p) + continue; + label_reattach_route(l, readd); + label_del(l); + /* remove must not interact with foreach */ + SLIST_REMOVE(&label_head, l, label, labels); + do_remove = 1; + break; /* XXX: suboptimal */ + } + } // while +} + +/* + * Finds a label by its binding and deletes it + */ +void +label_del_by_binding(uint32_t binding, int readd) +{ + struct label *l; + + SLIST_FOREACH(l, &label_head, labels) + if ((uint32_t)l->binding == binding) { + label_reattach_route(l, readd); + label_del(l); + SLIST_REMOVE(&label_head, l, label, labels); + break; + } +} + +/* + * For Compatibility with old bindinds code + */ +struct label* +label_get_by_prefix(struct in_addr *a, int prefixlen) +{ + union sockunion *so_dest, *so_pref; + struct label *l; + + so_dest = make_inet_union(inet_ntoa(*a)); + so_pref = from_cidr_to_union(prefixlen); + + l = label_get(so_dest, so_pref); + + free(so_dest); + free(so_pref); + + return l; +} + +/* + * Get a free binding + */ +uint32_t +get_free_local_label() +{ + struct label *l; + uint32_t lbl; + + for (lbl = MIN_LABEL; lbl <= MAX_LABEL; lbl++) { + SLIST_FOREACH(l, &label_head, labels) + if ((uint32_t)l->binding == lbl) + break; + if (l == NULL) + return lbl; + } + return 0; +} + +/* + * Change local binding + */ +void +change_local_label(struct label *l, uint32_t newbind) +{ + send_withdraw_tlv_to_all(&(l->so_dest.sin.sin_addr), + from_union_to_cidr(&(l->so_pref))); + l->binding = newbind; + send_label_tlv_to_all(&(l->so_dest.sin.sin_addr), + from_union_to_cidr(&(l->so_pref)), + l->binding); +} diff --git a/usr.sbin/ldpd/label.h b/usr.sbin/ldpd/label.h new file mode 100644 index 000000000000..5d0bd152be8a --- /dev/null +++ b/usr.sbin/ldpd/label.h @@ -0,0 +1,73 @@ +/* $NetBSD: label.h,v 1.1 2010/12/08 07:20:14 kefren Exp $ */ + +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Mihai Chelaru + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _LABEL_H_ +#define _LABEL_H_ + +#include + +#include "mpls_routes.h" +#include "ldp_peer.h" + +#define LDP_READD_NODEL 0 +#define LDP_READD_CHANGE 1 +#define LDP_READD_NOCHANGE 2 + +/* + * MPLS label descriptor + * + * so_dest and so_pref are obvious + * so_gate is the IPV4 gate + * binding is the local label + * label is the peer associated label + */ +struct label { + union sockunion so_dest, so_pref, so_gate; + int binding, label; + struct ldp_peer *p; + SLIST_ENTRY(label) labels; +}; +SLIST_HEAD(,label) label_head; + +void label_init(void); +struct label * label_add(union sockunion *, union sockunion *, + union sockunion *, uint32_t, struct ldp_peer *, uint32_t); +void label_del(struct label *); +void del_all_peer_labels(struct ldp_peer*, int); +void label_reattach_all_peer_labels(struct ldp_peer*, int); +void label_del_by_binding(uint32_t, int); +struct label * label_get(union sockunion *sodest, union sockunion *sopref); +struct label * label_get_by_prefix(struct in_addr*, int); +uint32_t get_free_local_label(void); +void change_local_label(struct label*, uint32_t); +void label_reattach_route(struct label*, int); + +#endif /* !_LABEL_H_ */ diff --git a/usr.sbin/ldpd/ldp.d b/usr.sbin/ldpd/ldp.d new file mode 100644 index 000000000000..d96a2ba8b05e --- /dev/null +++ b/usr.sbin/ldpd/ldp.d @@ -0,0 +1,50 @@ +ldp.o: ldp.c /usr/include/netinet/in.h /usr/include/machine/int_types.h \ + /usr/include/sys/cdefs.h /usr/include/machine/cdefs.h \ + /usr/include/sys/cdefs_elf.h /usr/include/sys/ansi.h \ + /usr/include/machine/ansi.h /usr/include/netinet6/in6.h \ + /usr/include/sys/socket.h /usr/include/sys/featuretest.h \ + /usr/include/sys/uio.h /usr/include/sys/stat.h /usr/include/sys/types.h \ + /usr/include/machine/types.h /usr/include/machine/endian.h \ + /usr/include/sys/endian.h /usr/include/machine/endian_machdep.h \ + /usr/include/machine/bswap.h /usr/include/machine/byte_swap.h \ + /usr/include/sys/bswap.h /usr/include/sys/fd_set.h \ + /usr/include/pthread_types.h /usr/include/sys/time.h \ + /usr/include/sys/select.h /usr/include/sys/sigtypes.h \ + /usr/include/sys/satypes.h /usr/include/time.h /usr/include/sys/null.h \ + /usr/include/arpa/inet.h /usr/include/stdio.h /usr/include/stdlib.h \ + /usr/include/strings.h /usr/include/stdint.h \ + /usr/include/machine/int_mwgwtypes.h /usr/include/machine/int_limits.h \ + /usr/include/machine/int_const.h /usr/include/machine/wchar_limits.h \ + /usr/include/string.h /usr/include/unistd.h /usr/include/sys/unistd.h \ + ldp.h ldp_command.h socketops.h mpls_routes.h /usr/include/net/if.h \ + /usr/include/sys/mutex.h /usr/include/sys/inttypes.h \ + /usr/include/sys/stdint.h /usr/include/machine/int_fmtio.h \ + /usr/include/machine/mutex.h /usr/include/x86/mutex.h \ + /usr/include/sys/condvar.h /usr/include/sys/queue.h \ + /usr/include/net/dlt.h /usr/include/net/pfil.h \ + /usr/include/altq/if_altq.h /usr/include/net/if_arp.h \ + /usr/include/net/route.h /usr/include/stdbool.h \ + /usr/include/net/radix.h /usr/include/netmpls/mpls.h \ + /usr/include/sys/param.h /usr/include/sys/syslimits.h \ + /usr/include/sys/signal.h /usr/include/sys/siginfo.h \ + /usr/include/machine/signal.h /usr/include/machine/trap.h \ + /usr/include/x86/trap.h /usr/include/machine/fpu.h \ + /usr/include/machine/mcontext.h /usr/include/machine/frame_regs.h \ + /usr/include/sys/ucontext.h /usr/include/machine/param.h \ + /usr/include/machine/limits.h /usr/include/sys/proc.h \ + /usr/include/machine/proc.h /usr/include/sys/user.h \ + /usr/include/machine/pcb.h /usr/include/machine/segments.h \ + /usr/include/machine/tss.h /usr/include/machine/sysarch.h \ + /usr/include/x86/sysarch.h /usr/include/machine/frame.h \ + /usr/include/sys/aio.h /usr/include/sys/rwlock.h \ + /usr/include/machine/rwlock.h /usr/include/x86/rwlock.h \ + /usr/include/sys/mqueue.h /usr/include/sys/lwp.h \ + /usr/include/sys/callout.h /usr/include/sys/signalvar.h \ + /usr/include/sys/sched.h /usr/include/sys/specificdata.h \ + /usr/include/sys/syncobj.h /usr/include/sys/resource.h \ + /usr/include/sys/event.h /usr/include/sys/ioctl.h \ + /usr/include/sys/ttycom.h /usr/include/sys/ioccom.h \ + /usr/include/sys/dkio.h /usr/include/prop/plistref.h \ + /usr/include/sys/filio.h /usr/include/sys/sockio.h \ + /usr/include/net/if_dl.h ldp_peer.h tlv.h pdu.h fsm.h ldp_errors.h \ + mpls_interface.h diff --git a/usr.sbin/ldpd/ldp.h b/usr.sbin/ldpd/ldp.h new file mode 100644 index 000000000000..509093eb673e --- /dev/null +++ b/usr.sbin/ldpd/ldp.h @@ -0,0 +1,79 @@ +/* $NetBSD: ldp.h,v 1.1 2010/12/08 07:20:14 kefren Exp $ */ + +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Mihai Chelaru + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _LDP_H_ +#define _LDP_H_ + +#include +#include + +#define ALL_ROUTERS "224.0.0.2" +#define LDP_PORT 646 +#define LDP_COMMAND_PORT 2626 + +#define LDPD_VER "0.3.0" + +extern char my_ldp_id[20]; + +#define LDP_ID my_ldp_id + +/* LDP Messages */ +#define LDP_NOTIFICATION 0x0001 +#define LDP_HELLO 0x0100 +#define LDP_INITIALIZE 0x0200 +#define LDP_KEEPALIVE 0x0201 +#define LDP_ADDRESS 0x0300 +#define LDP_ADDRESS_WITHDRAW 0x0301 +#define LDP_LABEL_MAPPING 0x0400 +#define LDP_LABEL_REQUEST 0x0401 +#define LDP_LABEL_WITHDRAW 0x0402 +#define LDP_LABEL_RELEASE 0x0403 +#define LDP_LABEL_ABORT 0x0404 + +/* Protocol version */ +#define LDP_VERSION 1 + +/* Various timers */ +#define LDP_HELLO_TIME 5 +#define LDP_HELLO_KEEP 15 +#define LDP_KEEPALIVE_TIME 4 +#define LDP_HOLDTIME 15 + +#define MIN_LABEL 16 +#define MAX_LABEL 1048576 + +#define ROUTE_LOOKUP_LOOP 6 +#define REPLAY_MAX 100 +#define MAX_POLL_FDS 200 + +void print_usage(char*); + +#endif /* !_LDP_H_ */ diff --git a/usr.sbin/ldpd/ldp_command.c b/usr.sbin/ldpd/ldp_command.c new file mode 100644 index 000000000000..27c24b06b5cf --- /dev/null +++ b/usr.sbin/ldpd/ldp_command.c @@ -0,0 +1,564 @@ +/* $NetBSD: ldp_command.c,v 1.1 2010/12/08 07:20:14 kefren Exp $ */ + +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Mihai Chelaru + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "label.h" +#include "ldp.h" +#include "ldp_command.h" +#include "ldp_errors.h" +#include "ldp_peer.h" +#include "socketops.h" + +struct com_sock csockets[MAX_COMMAND_SOCKETS]; +extern int ldp_hello_time, debug_f, warn_f; + +#define writestr(soc, str) write(soc, str, strlen(str)) + +#define MAXSEND 1024 +char sendspace[MAXSEND]; + +static int verify_root_pwd(char *); +static void echo_on(int s); +static void echo_off(int s); + +struct com_func main_commands[] = { + { "show", show_func }, + { "set", set_func }, + { "quit", exit_func }, + { "exit", exit_func }, + { "", NULL } +}; + +struct com_func show_commands[] = { + { "neighbours", show_neighbours }, + { "bindings", show_bindings }, + { "debug", show_debug }, + { "hellos", show_hellos }, + { "parameters", show_parameters }, + { "version", show_version }, + { "warning", show_warning }, + { "", NULL } +}; + +struct com_func set_commands[] = { + { "debug", set_debug }, + { "hello-time", set_hello_time }, + { "warning", set_warning }, + { "", NULL } +}; + +int +verify_root_pwd(char *pw) +{ + struct passwd *p; + + if ((p = getpwuid(0)) == NULL) + return 0; + + if (strcmp(crypt(pw, p->pw_passwd), p->pw_passwd)) + return 0; + + return 1; +} + + +void +init_command_sockets() +{ + int i; + + for (i = 0; isocket, recvspace, MAX_COMMAND_SIZE, MSG_PEEK); + + if (r < 0) { + command_close(cs->socket); + return; + } + + recv(cs->socket, recvspace, r, MSG_WAITALL); + + if (r < 3) { /*at least \r\n */ + if (cs->auth) { + /*writestr(cs->socket, "Unknown command. Use ? for help\n");*/ + send_prompt(cs->socket); + } else { + writestr(cs->socket, "Bad password\n"); + command_close(cs->socket); + } + return; + } + + recvspace[r - 2] = '\0'; + + if (!cs->auth) { + if (verify_root_pwd(recvspace)) { + echo_on(cs->socket); + cs->auth = 1; + writestr(cs->socket, "\n"); + send_prompt(cs->socket); + } else { + echo_on(cs->socket); + writestr(cs->socket, "Bad password\n"); + command_close(cs->socket); + } + return; + } + + strsep(&nextc, " "); + + command_match(main_commands, cs->socket, recvspace, nextc); + +} + +void +command_close(int s) +{ + int i; + + for (i=0; i "); +} + +void +send_pwd_prompt(int s) { + echo_off(s); + writestr(s, "Password: "); +} + +static void echo_off(int s) +{ + char iac_will_echo[3] = { 0xff, 0xfb, 0x01 }, bf[32]; + write(s, iac_will_echo, sizeof(iac_will_echo)); + read(s, bf, sizeof(bf)); +} + +static void echo_on(int s) +{ + char iac_wont_echo[3] = { 0xff, 0xfc, 0x01 }, bf[32]; + write(s, iac_wont_echo, sizeof(iac_wont_echo)); + read(s, bf, sizeof(bf)); +} + +/* + * Matching function + * Returns 1 if matched anything + */ +int +command_match(struct com_func *cf, int s, char *orig, char *next) +{ + int i, matched = 0, last_match; + + if (orig == NULL || orig[0] == '\0') { + send_prompt(s); + return 0; + } + + if (!strcmp(orig, "?")) { + for (i=0; cf[i].func != NULL; i++) { + snprintf(sendspace, MAXSEND, "\t%s\n", cf[i].com); + writestr(s, sendspace); + } + send_prompt(s); + return 0; + } + + for (i=0; cf[i].func != NULL; i++) + if(strncasecmp(orig, cf[i].com, strlen(orig)) == 0) { + matched++; + last_match = i; + } + if (!matched) { + writestr(s, "Unknown command. Use ? for help\n"); + send_prompt(s); + return 0; + } + + if (matched > 1) { + writestr(s, "Ambiguous command. Use ? for help\n"); + send_prompt(s); + return 0; + } + + if(cf[last_match].func(s, next) != 0) + send_prompt(s); + return 1; + +} + +/* + * Main CLI functions + */ +int +set_func(int s, char *recvspace) +{ + char *nextc = recvspace; + + if (recvspace == NULL || recvspace[0] == '\0') { + writestr(s, "Unknown set command. Use set ? for help\n"); + return 1; + } + + strsep(&nextc, " "); + + command_match(set_commands, s, recvspace, nextc); + return 0; +} + +int +show_func(int s, char *recvspace) +{ + char *nextc = recvspace; + + if (recvspace == NULL || recvspace[0] == '\0') { + writestr(s, "Unknown show command. Use show ? for help\n"); + return 1; + } + + strsep(&nextc, " "); + + command_match(show_commands, s, recvspace, nextc); + return 0; +} + +int +exit_func(int s, char *recvspace) +{ + command_close(s); + return 0; +} + +/* + * Show functions + */ +int +show_neighbours(int s, char *recvspace) +{ + struct ldp_peer *p; + struct ldp_peer_address *wp; + struct sockaddr_in ssin; + socklen_t sin_len = sizeof(struct sockaddr_in); + + SLIST_FOREACH(p, &ldp_peer_head, peers) { + snprintf(sendspace, MAXSEND, "LDP peer: %s\n", + inet_ntoa(p->ldp_id)); + writestr(s, sendspace); + snprintf(sendspace, MAXSEND, "Transport address: %s\n", + inet_ntoa(p->transport_address)); + writestr(s, sendspace); + snprintf(sendspace, MAXSEND, "Next-hop address: %s\n", + inet_ntoa(p->address)); + writestr(s, sendspace); + snprintf(sendspace, MAXSEND, "State: %s\n", + ldp_state_to_name(p->state)); + writestr(s, sendspace); + if (p->state == LDP_PEER_ESTABLISHED) { + snprintf(sendspace, MAXSEND, "Since: %s", + ctime(&p->established_t)); + writestr(s, sendspace); + } + snprintf(sendspace, MAXSEND, "Holdtime: %d\nTimeout: %d\n", + p->holdtime, p->timeout); + writestr(s, sendspace); + + switch(p->state) { + case LDP_PEER_CONNECTING: + case LDP_PEER_CONNECTED: + case LDP_PEER_ESTABLISHED: + if (getsockname(p->socket,(struct sockaddr *) &ssin, + &sin_len)) + break; + snprintf(sendspace, MAXSEND,"Socket: %d\nLocal %s:%d\n", + p->socket, inet_ntoa(ssin.sin_addr), + ntohs(ssin.sin_port)); + writestr(s, sendspace); + + if (getpeername(p->socket,(struct sockaddr *) &ssin, + &sin_len)) + break; + snprintf(sendspace, MAXSEND, "Remote %s:%d\n", + inet_ntoa(ssin.sin_addr), ntohs(ssin.sin_port)); + writestr(s, sendspace); + } + + snprintf(sendspace, MAXSEND,"Addresses bounded to this peer: "); + writestr(s, sendspace); + SLIST_FOREACH(wp, &p->ldp_peer_address_head, addresses) { + snprintf(sendspace, MAXSEND, "%s ", + inet_ntoa(wp->address)); + writestr(s, sendspace); + } + sendspace[0] = sendspace[1] = '\n'; + write(s, sendspace, 2); + } + return 1; +} + +int +show_bindings(int s, char *recvspace) +{ + struct label *l; + + snprintf(sendspace, MAXSEND, "Local label\tNetwork\t\t\t\tNexthop\n"); + writestr(s, sendspace); + SLIST_FOREACH (l, &label_head, labels) { + snprintf(sendspace, MAXSEND, "%d\t\t%s/", l->binding, + union_ntoa(&l->so_dest)); + writestr(s, sendspace); + snprintf(sendspace, MAXSEND, "%s", union_ntoa(&l->so_pref)); + writestr(s, sendspace); + if (l->p) + snprintf(sendspace, MAXSEND, "\t%s:%d\n", + inet_ntoa(l->p->address), l->label); + else + snprintf(sendspace, MAXSEND, "\n"); + writestr(s, sendspace); + } + return 1; +} + +int +show_debug(int s, char *recvspace) +{ + if (recvspace) { + writestr(s, "Invalid command\n"); + return 1; + } + + snprintf(sendspace, MAXSEND, "Debug: %s\n", + debug_f ? "YES" : "NO"); + writestr(s, sendspace); + return 1; +} + +int +show_hellos(int s, char *recvspace) +{ + struct hello_info *hi; + + SLIST_FOREACH(hi, &hello_info_head, infos) { + snprintf(sendspace, MAXSEND, "%s: %ds\n", inet_ntoa(hi->ldp_id), + hi->keepalive); + writestr(s, sendspace); + } + return 1; +} + +int +show_parameters(int s, char *recvspace) +{ + snprintf(sendspace, MAXSEND, "LDP ID: %s\nProtocol version: %d\n" + "Hello time: %d\nKeepalive time: %d\nHoldtime: %d\n" + "Minimum label: %d\nMaximum label: %d\n", + my_ldp_id, + LDP_VERSION, + ldp_hello_time, + LDP_KEEPALIVE_TIME, + LDP_HOLDTIME, + MIN_LABEL, + MAX_LABEL); + writestr(s, sendspace); + return 1; +} + +int +show_version(int s, char *recvspace) +{ + if (recvspace) { /* Nothing more after this */ + writestr(s, "Invalid command\n"); + return 1; + } + + snprintf(sendspace, MAXSEND, "NetBSD LDP daemon version: %s\n", + LDPD_VER); + writestr(s, sendspace); + return 1; +} + +int +show_warning(int s, char *recvspace) +{ + if (recvspace) { + writestr(s, "Invalid command\n"); + return 1; + } + + snprintf(sendspace, MAXSEND, "Warnings: %s\n", + warn_f ? "YES" : "NO"); + writestr(s, sendspace); + return 1; +} + +/* Set commands */ +int +set_hello_time(int s, char *recvspace) +{ + if (!recvspace || atoi(recvspace) < 1) { + writestr(s, "Invalid timeout\n"); + return 1; + } + + ldp_hello_time = atoi(recvspace); + return 1; +} + +int +set_debug(int s, char *recvspace) +{ + if (!recvspace || atoi(recvspace) < 0) { + writestr(s, "Invalid command\n"); + return 1; + } + + debug_f = atoi(recvspace); + return 1; +} + +int +set_warning(int s, char *recvspace) +{ + if (!recvspace || atoi(recvspace) < 0) { + writestr(s, "Invalid command\n"); + return 1; + } + + warn_f = atoi(recvspace); + return 1; +} diff --git a/usr.sbin/ldpd/ldp_command.h b/usr.sbin/ldpd/ldp_command.h new file mode 100644 index 000000000000..2bcca6fa519c --- /dev/null +++ b/usr.sbin/ldpd/ldp_command.h @@ -0,0 +1,79 @@ +/* $NetBSD: ldp_command.h,v 1.1 2010/12/08 07:20:14 kefren Exp $ */ + +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Mihai Chelaru + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _LDP_COMMAND_H_ +#define _LDP_COMMAND_H_ + +#define MAX_COMMAND_SOCKETS 64 +#define MAX_COMMAND_SIZE 512 + +struct com_sock { + int socket; + int auth; /* 1 if socket is authenticated */ +}; + +struct com_func { + char com[64]; + int (* func)(int, char *); +}; + +void init_command_sockets(void); +int create_command_socket(int); +struct com_sock * is_command_socket(int); +void command_accept(int); +int add_command_socket(int); +void command_dispatch(struct com_sock *); +void command_close(int); + +void send_prompt(int); +void send_pwd_prompt(int); +int command_match(struct com_func*, int, char*, char*); + +/* Main functions */ +int show_func(int, char *); +int set_func(int, char *); +int exit_func(int, char *); + +/* Show functions */ +int show_neighbours(int, char *); +int show_bindings(int, char *); +int show_debug(int, char *); +int show_hellos(int, char *); +int show_parameters(int, char *); +int show_version(int, char *); +int show_warning(int, char *); + +/* Set functions */ +int set_hello_time(int, char *); +int set_debug(int, char *); +int set_warning(int, char *); + +#endif /* !_LDP_COMMAND_H_ */ diff --git a/usr.sbin/ldpd/ldp_errors.c b/usr.sbin/ldpd/ldp_errors.c new file mode 100644 index 000000000000..17305cf7b1df --- /dev/null +++ b/usr.sbin/ldpd/ldp_errors.c @@ -0,0 +1,110 @@ +/* $NetBSD: ldp_errors.c,v 1.1 2010/12/08 07:20:14 kefren Exp $ */ + +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Mihai Chelaru + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +#include "ldp.h" +#include "ldp_errors.h" + +int debug_f = 0, warn_f = 0, syslog_f = 0; + +static void do_syslog(int, const char*, va_list); + +void +debugp(const char *fmt, ...) +{ + va_list va; + + if (!debug_f) + return; + + va_start(va, fmt); + if (syslog_f) + do_syslog(LOG_DEBUG, fmt, va); + else + vprintf(fmt, va); + va_end(va); +} + +void +warnp(const char *fmt, ...) +{ + va_list va; + + if (!debug_f && !warn_f) + return; + + va_start(va, fmt); + if (syslog_f) + do_syslog(LOG_WARNING, fmt, va); + else + vprintf(fmt, va); + va_end(va); +} + +void +fatalp(const char *fmt, ...) +{ + va_list va; + + va_start(va, fmt); + if (syslog_f) + do_syslog(LOG_ERR, fmt, va); + else + vprintf(fmt, va); + va_end(va); +} + +static void +do_syslog(int prio, const char *fmt, va_list va) +{ + vsyslog(prio, fmt, va); +} + +void +printtime() +{ + time_t t; + char buf[26]; + int i; + + time(&t); + ctime_r(&t, buf); + for (i = 0; (i < 26 && buf[i] != 0); i++) + if (buf[i] == '\n') + buf[i] = 0; + printf("%s ", buf); +} diff --git a/usr.sbin/ldpd/ldp_errors.h b/usr.sbin/ldpd/ldp_errors.h new file mode 100644 index 000000000000..50e233d81be7 --- /dev/null +++ b/usr.sbin/ldpd/ldp_errors.h @@ -0,0 +1,58 @@ +/* $NetBSD: ldp_errors.h,v 1.1 2010/12/08 07:20:14 kefren Exp $ */ + +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Mihai Chelaru + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _LDP_ERRORS_H_ +#define _LDP_ERRORS_H_ + +#define LDP_E_OK 0 +#define LDP_E_BAD_VERSION 1 +#define LDP_E_BAD_LENGTH 2 +#define LDP_E_MEMORY 3 +#define LDP_E_NOENT 4 +#define LDP_E_ALREADY_DONE 5 +#define LDP_E_BAD_AF 6 +#define LDP_E_BAD_FEC 7 +#define LDP_E_BAD_LABEL 8 +#define LDP_E_NO_SUCH_ROUTE LDP_E_ROUTE_ERROR +/* 9, 10 Free */ +#define LDP_E_ROUTE_ERROR 11 +#define LDP_E_NO_BINDING 12 +#define LDP_E_TOO_MANY_LABELS 13 +#define LDP_E_INVAL 14 +#define LDP_E_GENERIC 255 + +void printtime(void); + +void debugp(const char *, ...); +void fatalp(const char *, ...); +void warnp(const char *, ...); + +#endif /* !_LDP_ERRORS_H_ */ diff --git a/usr.sbin/ldpd/ldp_peer.c b/usr.sbin/ldpd/ldp_peer.c new file mode 100644 index 000000000000..644c8d2bfb14 --- /dev/null +++ b/usr.sbin/ldpd/ldp_peer.c @@ -0,0 +1,498 @@ +/* $NetBSD: ldp_peer.c,v 1.1 2010/12/08 07:20:14 kefren Exp $ */ + +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Mihai Chelaru + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include "socketops.h" +#include "ldp_errors.h" +#include "ldp.h" +#include "tlv_stack.h" +#include "mpls_interface.h" +#include "notifications.h" +#include "ldp_peer.h" + +struct in_addr *myaddresses; + +void +ldp_peer_init(void) +{ + SLIST_INIT(&ldp_peer_head); + myaddresses = NULL; +} + +/* + * soc should be > 1 if there is already a TCP socket for this else we'll + * initiate a new one + */ +struct ldp_peer * +ldp_peer_new(struct in_addr * ldp_id, struct in_addr * a, + struct in_addr * tradd, uint16_t holdtime, int soc) +{ + struct ldp_peer *p; + int s = soc; + struct sockaddr_in sa; + + if (s < 1) { + s = socket(PF_INET, SOCK_STREAM, 0); + memset(&sa, 0, sizeof(sa)); + sa.sin_len = sizeof(sa); + sa.sin_family = AF_INET; + + if (tradd) + memcpy(&sa.sin_addr, tradd, + sizeof(struct in_addr)); + else + memcpy(&sa.sin_addr, a, + sizeof(struct in_addr)); + sa.sin_port = htons(LDP_PORT); + + set_ttl(s); + } + + /* Set the peer in CONNECTING/CONNECTED state */ + p = (struct ldp_peer *) malloc(sizeof(struct ldp_peer)); + + if (!p) { + fatalp("ldp_peer_new: malloc problem\n"); + return NULL; + } + + memset(p, 0, sizeof(struct ldp_peer)); + SLIST_INSERT_HEAD(&ldp_peer_head, p, peers); + memcpy(&p->address, a, sizeof(struct in_addr)); + memcpy(&p->ldp_id, ldp_id, sizeof(struct in_addr)); + if (tradd) + memcpy(&p->transport_address, tradd, + sizeof(struct in_addr)); + else + memcpy(&p->transport_address, a, + sizeof(struct in_addr)); + p->holdtime = holdtime > LDP_HOLDTIME ? holdtime : LDP_HOLDTIME; + p->socket = s; + if (soc < 1) { + p->state = LDP_PEER_CONNECTING; + p->master = 1; + } else { + p->state = LDP_PEER_CONNECTED; + p->master = 0; + set_ttl(p->socket); + } + SLIST_INIT(&p->ldp_peer_address_head); + SLIST_INIT(&p->label_mapping_head); + p->timeout = p->holdtime; + + /* And connect to peer */ + if (soc < 1) + if (connect(s, (struct sockaddr *) & sa, sizeof(sa)) == -1) { + if (errno == EINTR) { + return p; /* We take care of this in + * big_loop */ + } + warnp("connect to %s failed: %s\n", + inet_ntoa(sa.sin_addr), strerror(errno)); + ldp_peer_holddown(p); + return NULL; + } + p->state = LDP_PEER_CONNECTED; + return p; +} + +void +ldp_peer_holddown(struct ldp_peer * p) +{ + if (!p) + return; + if (p->state == LDP_PEER_ESTABLISHED) + mpls_delete_ldp_peer(p); + p->state = LDP_PEER_HOLDDOWN; + p->timeout = LDP_HOLDTIME; + shutdown(p->socket, SHUT_RDWR); + ldp_peer_delete_all_mappings(p); + del_all_ifaddr(p); + fatalp("LDP Neighbour %s is DOWN\n", inet_ntoa(p->ldp_id)); +} + +void +ldp_peer_holddown_all() +{ + struct ldp_peer *p; + + SLIST_FOREACH(p, &ldp_peer_head, peers) { + if ((p->state == LDP_PEER_ESTABLISHED) || + (p->state == LDP_PEER_CONNECTED)) + send_notification(p, get_message_id(), NOTIF_SHUTDOWN); + ldp_peer_holddown(p); + } +} + +void +ldp_peer_delete(struct ldp_peer * p) +{ + + if (!p) + return; + + SLIST_REMOVE(&ldp_peer_head, p, ldp_peer, peers); + close(p->socket); + warnp("LDP Neighbor %s holddown timer expired\n", inet_ntoa(p->ldp_id)); + free(p); +} + +struct ldp_peer * +get_ldp_peer(struct in_addr * a) +{ + struct ldp_peer *p; + + SLIST_FOREACH(p, &ldp_peer_head, peers) { + if (!memcmp((void *) a, (void *) &p->ldp_id, + sizeof(struct in_addr))) + return p; + if (!memcmp((void *) a, (void *) &p->address, + sizeof(struct in_addr))) + return p; + if (check_ifaddr(p, a)) + return p; + } + return NULL; +} + +struct ldp_peer * +get_ldp_peer_by_socket(int s) +{ + struct ldp_peer *p; + + SLIST_FOREACH(p, &ldp_peer_head, peers) + if (p->socket == s) + return p; + return NULL; +} + +/* + * Adds address list bounded to a specific peer + * Returns the number of addresses inserted successfuly + */ +int +add_ifaddresses(struct ldp_peer * p, struct al_tlv * a) +{ + int i, c, n; + struct in_addr *ia; + + /* + * Check if tlv is Address type, if it's correct size (at least one + * address) and if it's IPv4 + */ + + if ((ntohs(a->type) != TLV_ADDRESS_LIST) || + (ntohs(a->length) < sizeof(a->af) + sizeof(struct in_addr)) || + (ntohs(a->af) != LDP_AF_INET)) + return 0; + + /* Number of addresses to insert */ + n = (ntohs(a->length) - sizeof(a->af)) / sizeof(struct in_addr); + + debugp("Trying to add %d addresses to peer %s ... \n", n, + inet_ntoa(p->ldp_id)); + + for (ia = (struct in_addr *) & a->address, c = 0, i = 0; i < n; i++) { + if (add_ifaddr(p, &ia[i]) == LDP_E_OK) + c++; + } + + debugp("Added %d addresses\n", c); + + return c; +} + +int +del_ifaddresses(struct ldp_peer * p, struct al_tlv * a) +{ + int i, c, n; + struct in_addr *ia; + + /* + * Check if tlv is Address type, if it's correct size (at least one + * address) and if it's IPv4 + */ + + if (ntohs(a->type) != TLV_ADDRESS_LIST || + ntohs(a->length) > sizeof(a->af) + sizeof(struct in_addr) || + ntohs(a->af) != LDP_AF_INET) + return -1; + + n = (ntohs(a->length) - sizeof(a->af)) / sizeof(struct in_addr); + + debugp("Trying to delete %d addresses from peer %s ... \n", n, + inet_ntoa(p->ldp_id)); + + for (ia = (struct in_addr *) & a[1], c = 0, i = 0; i < n; i++) { + if (del_ifaddr(p, &ia[i]) == LDP_E_OK) + c++; + } + + debugp("Deleted %d addresses\n", c); + + return c; +} + + +/* Adds a _SINGLE_ address to a specific peer */ +int +add_ifaddr(struct ldp_peer * p, struct in_addr * a) +{ + struct ldp_peer_address *lpa; + + /* Is it already there ? */ + if (check_ifaddr(p, a)) + return LDP_E_ALREADY_DONE; + + lpa = (struct ldp_peer_address*)malloc(sizeof(struct ldp_peer_address)); + + if (!lpa) { + fatalp("add_ifaddr: malloc problem\n"); + return LDP_E_MEMORY; + } + + memset(lpa, 0, sizeof(struct ldp_peer_address)); + + memcpy(&lpa->address, a, sizeof(struct in_addr)); + + SLIST_INSERT_HEAD(&p->ldp_peer_address_head, lpa, addresses); + return LDP_E_OK; +} + +/* Deletes an address bounded to a specific peer */ +int +del_ifaddr(struct ldp_peer * p, struct in_addr * a) +{ + struct ldp_peer_address *wp; + + wp = check_ifaddr(p, a); + if (!wp) + return LDP_E_NOENT; + + SLIST_REMOVE(&p->ldp_peer_address_head, wp, ldp_peer_address, + addresses); + free(wp); + return LDP_E_OK; +} + +/* Checks if an address is already bounded */ +struct ldp_peer_address * +check_ifaddr(struct ldp_peer * p, struct in_addr * a) +{ + struct ldp_peer_address *wp; + + SLIST_FOREACH(wp, &p->ldp_peer_address_head, addresses) + if (memcmp(a, &wp->address, sizeof(struct in_addr)) == 0) + return wp; + return NULL; +} + +void +del_all_ifaddr(struct ldp_peer * p) +{ + struct ldp_peer_address *wp; + + while (!SLIST_EMPTY(&p->ldp_peer_address_head)) { + wp = SLIST_FIRST(&p->ldp_peer_address_head); + SLIST_REMOVE_HEAD(&p->ldp_peer_address_head, addresses); + free(wp); + } +} + +void +print_bounded_addresses(struct ldp_peer * p) +{ + struct ldp_peer_address *wp; + char abuf[512]; + + snprintf(abuf, sizeof(abuf), "Addresses bounded to peer %s: ", + inet_ntoa(p->address)); + SLIST_FOREACH(wp, &p->ldp_peer_address_head, addresses) { + strncat(abuf, inet_ntoa(wp->address), sizeof(abuf) -1); + strncat(abuf, " ", sizeof(abuf) -1); + } + warnp("%s\n", abuf); +} + +void +add_my_if_addrs(struct in_addr * a, int count) +{ + myaddresses = (struct in_addr *) malloc((count + 1) * + (sizeof(struct in_addr))); + + if (!myaddresses) { + fatalp("add_my_if_addrs: malloc problem\n"); + return; + } + memcpy(myaddresses, a, count * sizeof(struct in_addr)); + myaddresses[count].s_addr = 0; +} + +/* Adds a label and a prefix to a specific peer */ +int +ldp_peer_add_mapping(struct ldp_peer * p, struct in_addr * a, int prefix, + int label) +{ + struct label_mapping *lma; + + if (!p) + return -1; + if (ldp_peer_get_lm(p, a, prefix)) + return LDP_E_ALREADY_DONE; + + lma = (struct label_mapping *) malloc(sizeof(struct label_mapping)); + + if (!lma) { + fatalp("ldp_peer_add_mapping: malloc problem\n"); + return LDP_E_MEMORY; + } + + memcpy(&lma->address, a, sizeof(struct in_addr)); + lma->prefix = prefix; + lma->label = label; + + SLIST_INSERT_HEAD(&p->label_mapping_head, lma, mappings); + + return LDP_E_OK; +} + +int +ldp_peer_delete_mapping(struct ldp_peer * p, struct in_addr * a, int prefix) +{ + struct label_mapping *lma; + + if (!a) + return ldp_peer_delete_all_mappings(p); + + lma = ldp_peer_get_lm(p, a, prefix); + if (!lma) + return LDP_E_NOENT; + + SLIST_REMOVE(&p->label_mapping_head, lma, label_mapping, mappings); + free(lma); + + return LDP_E_OK; +} + +struct label_mapping * +ldp_peer_get_lm(struct ldp_peer * p, struct in_addr * a, int prefix) +{ + struct label_mapping *rv; + + if (!p) + return NULL; + + SLIST_FOREACH(rv, &p->label_mapping_head, mappings) + if ((rv->prefix == prefix) && (!memcmp(a, &rv->address, + sizeof(struct in_addr)))) + break; + + return rv; + +} + +int +ldp_peer_delete_all_mappings(struct ldp_peer * p) +{ + struct label_mapping *lma; + + while(!SLIST_EMPTY(&p->label_mapping_head)) { + lma = SLIST_FIRST(&p->label_mapping_head); + SLIST_REMOVE_HEAD(&p->label_mapping_head, mappings); + free(lma); + } + + return LDP_E_OK; +} + +/* returns a mapping and its peer */ +struct peer_map * +ldp_test_mapping(struct in_addr * a, int prefix, struct in_addr * gate) +{ + struct ldp_peer *lpeer; + struct peer_map *rv = NULL; + struct label_mapping *lm = NULL; + + /* Checks if it's LPDID, else checks if it's an interface */ + + lpeer = get_ldp_peer(gate); + if (!lpeer) { + debugp("Gateway %s is not an LDP peer\n", inet_ntoa(*gate)); + return NULL; + } + if (lpeer->state != LDP_PEER_ESTABLISHED) { + warnp("ldp_test_mapping: peer is down ?!\n"); + return NULL; + } + lm = ldp_peer_get_lm(lpeer, a, prefix); + + if (!lm) { + debugp("Cannot match that prefix to the specified peer\n"); + return NULL; + } + rv = (struct peer_map *) malloc(sizeof(struct peer_map)); + + if (!rv) { + fatalp("ldp_test_mapping: malloc problem\n"); + return NULL; + } + + rv->lm = lm; + rv->peer = lpeer; + + return rv; +} + +/* Name from state */ +const char * ldp_state_to_name(int state) +{ + switch(state) { + case LDP_PEER_CONNECTING: + return "CONNECTING"; + case LDP_PEER_CONNECTED: + return "CONNECTED"; + case LDP_PEER_ESTABLISHED: + return "ESTABLISHED"; + case LDP_PEER_HOLDDOWN: + return "HOLDDOWN"; + } + return "UNKNOWN"; +} diff --git a/usr.sbin/ldpd/ldp_peer.h b/usr.sbin/ldpd/ldp_peer.h new file mode 100644 index 000000000000..11fe04423881 --- /dev/null +++ b/usr.sbin/ldpd/ldp_peer.h @@ -0,0 +1,118 @@ +/* $NetBSD: ldp_peer.h,v 1.1 2010/12/08 07:20:14 kefren Exp $ */ + +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Mihai Chelaru + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _LDP_PEER_H_ +#define _LDP_PEER_H_ + +#include "sys/types.h" +#include "sys/queue.h" +#include "netinet/in.h" + +#include "tlv.h" + +struct ldp_peer_address { + struct in_addr address; + SLIST_ENTRY(ldp_peer_address) addresses; +}; + +struct label_mapping { + struct in_addr address; + int prefix; + int label; + SLIST_ENTRY(label_mapping) mappings; +}; + + +struct ldp_peer { + /* + * I add routes to address. + * I maintain LDP TCP connection on transport_address. + * I use ldp_id as peer identificator. + */ + struct in_addr address, transport_address, ldp_id; + /* TCP socket */ + int socket; + /* Usual peer parameters */ + uint16_t holdtime, timeout; + int master; /* 0 if we're passive */ + int state; /* see below for possible states */ + time_t established_t; /* time when it did connected */ + /* Here I maintain all the addresses announced by a peer */ + SLIST_HEAD(,ldp_peer_address) ldp_peer_address_head; + SLIST_HEAD(,label_mapping) label_mapping_head; + + SLIST_ENTRY(ldp_peer) peers; +}; +SLIST_HEAD(,ldp_peer) ldp_peer_head; + +struct peer_map { + struct ldp_peer *peer; + struct label_mapping *lm; +}; + +/* LDP Peers States */ +#define LDP_PEER_CONNECTING 0 +#define LDP_PEER_CONNECTED 1 +#define LDP_PEER_ESTABLISHED 2 +#define LDP_PEER_HOLDDOWN 3 + +void ldp_peer_init(void); +struct ldp_peer * ldp_peer_new(struct in_addr *, struct in_addr *, + struct in_addr *, uint16_t, int); +void ldp_peer_holddown(struct ldp_peer *); +void ldp_peer_delete(struct ldp_peer *); +struct ldp_peer * get_ldp_peer(struct in_addr *); +struct ldp_peer * get_ldp_peer_by_socket(int); +int add_ifaddresses(struct ldp_peer *, struct al_tlv *); +int add_ifaddr(struct ldp_peer *, struct in_addr *); +int del_ifaddr(struct ldp_peer *, struct in_addr *); +struct ldp_peer_address * check_ifaddr(struct ldp_peer *, + struct in_addr *); +void print_bounded_addresses(struct ldp_peer *); +void del_all_ifaddr(struct ldp_peer *); +int del_ifaddresses(struct ldp_peer *, struct al_tlv *); +void add_my_if_addrs(struct in_addr *, int); + +int ldp_peer_add_mapping(struct ldp_peer *, struct in_addr *, + int, int); +int ldp_peer_delete_mapping(struct ldp_peer *, struct in_addr *, + int); +struct label_mapping * ldp_peer_get_lm(struct ldp_peer *, struct in_addr *, + int); +int ldp_peer_delete_all_mappings(struct ldp_peer *); +void ldp_peer_holddown_all(void); + +struct peer_map * ldp_test_mapping(struct in_addr *, int, + struct in_addr *); + +const char * ldp_state_to_name(int); + +#endif /* !_LDP_PEER_H_ */ diff --git a/usr.sbin/ldpd/ldpd.8 b/usr.sbin/ldpd/ldpd.8 new file mode 100644 index 000000000000..bb316e15286c --- /dev/null +++ b/usr.sbin/ldpd/ldpd.8 @@ -0,0 +1,94 @@ +.\" $NetBSD: ldpd.8,v 1.1 2010/12/08 07:20:14 kefren Exp $ +.\" +.\" Copyright (c) 2010 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd December 7, 2010 +.Dt ldpd 8 +.Os +.Sh NAME +.Nm ldpd +.Nd Label Distribution Protocol Daemon +.Sh SYNOPSIS +.Nm +.Op Fl dDW +.Bk -words +.Op Fl p Ar port +.Ek +.Sh DESCRIPTION +.Nm +is a utility used to automatically distribute labels between two MPLS LSRs +almost conforming to RFC3036. Right now is in BETA stage and many features +are not implemented or may not work. As a security measure you SHOULD filter +the LDP well-known ( 646 ) TCP and UDP ports before starting +.Nm +using your favourite packet filter. Also this is the current measure used +to filter neighbours. You should see some logs reported via +.Xr syslog 3 +interface. +.Pp +You can increase the log verbosity using -W and -D parameters. +Also you can telnet on control port (default: 2626) and use this interface +in order to get informations about protocol, neighbours etc. but also to +set runtime parameters. The required password is the same as root password. +.Pp +.Nm +computes existing routes and try to match them on MPLS labels announced by +other LDP peers. This means that 'normal' routes will be changed into tagged +routes, and MPLS routing table will be populated. It will also announce +peers about its mappings. +.Nm +will also listen on a route socket and compute the necessary changes in +order to change untagged routes into tagged routes. This means that one may +use his favourite dynamic routing protocol daemon without modifications. +.Pp +The options are as follows: +.Bl -tag -width 15n +.It Fl d +Don't use route interception code. +.It Fl D +Enable debug mode. +.It Fl f +Run in foreground. Use STDOUT for warning and debug messages. +.It Fl h +Outputs supported flags. +.It Fl p Ar port +Changes the TCP control port (default: 2626). +.It Fl W +Enable warning messages output. +.El +.Sh SEE ALSO +.Rs +.%R RFC +.%N 3036 +.%D January 2001 +.%T LDP Specification +.Re +.Sh HISTORY +The +.Nm +command appeared in NetBSD 6.0 +.Sh BUGS +.Nm +supports only IPv4 diff --git a/usr.sbin/ldpd/main.c b/usr.sbin/ldpd/main.c new file mode 100644 index 000000000000..fe1381d2487e --- /dev/null +++ b/usr.sbin/ldpd/main.c @@ -0,0 +1,146 @@ +/* $NetBSD: main.c,v 1.1 2010/12/08 07:20:14 kefren Exp $ */ + +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Mihai Chelaru + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "ldp.h" +#include "ldp_command.h" +#include "socketops.h" +#include "tlv.h" +#include "pdu.h" +#include "fsm.h" +#include "ldp_errors.h" +#include "mpls_interface.h" + +extern int ls; /* TCP listening socket */ +extern int dont_catch; +extern int command_port; +extern int command_socket; + +extern int debug_f, warn_f, syslog_f; + +extern struct sockaddr mplssockaddr; + +void print_usage(char *myself) +{ + printf("\nUsage: %s [-hdDW] [-p PORT]\n\n", myself); +} + +int +main(int argc, char *argv[]) +{ + int ch, forkres, dontfork = 0; + + while((ch = getopt(argc, argv, "dDfhp:W")) != -1) + switch(ch) { + case 'd': + dont_catch = 1; + break; + case 'D': + debug_f = 1; + break; + case 'f': + dontfork = 1; + break; + case 'p': + if ((command_port = atoi(optarg)) < 1) { + print_usage(argv[0]); + return -1; + } + break; + case 'W': + warn_f = 1; + break; + case 'h': + default: + print_usage(argv[0]); + return -1; + break; + } + if (geteuid()) { + fatalp("You have to run this as ROOT\n"); + return -1; + } + if (set_my_ldp_id()) { + fatalp("Cannot set LDP ID\n"); + return -1; + } + if (mplssockaddr.sa_len == 0) { + fatalp("You need one mpls interface up and an IP " + "address set for it\n"); + return -1; + } + if (mpls_start_ldp() == -1) + return -1; + if (!strcmp(LDP_ID, "0.0.0.0")) { + fatalp("Cannot set my LDP ID.\nAre you sure you've " + "got a non-loopback INET interface UP ?\n"); + return -1; + } + init_command_sockets(); + if ((command_socket = create_command_socket(command_port)) < 1) { + fatalp("Cannot create command socket\n"); + return -1; + } + if (create_hello_socket() < 1) { + fatalp("Cannot create hello socket\n"); + return -1; + } + + ls = create_listening_socket(); + + if (ls < 0) { + fatalp("Cannot create listening socket\n"); + return -1; + } + + if (dontfork == 1) + the_big_loop(); + + forkres = fork(); + if (forkres == 0) { + syslog_f = 1; + the_big_loop(); + } + if (forkres < 0) + perror("fork"); + + return 0; +} diff --git a/usr.sbin/ldpd/mpls_interface.c b/usr.sbin/ldpd/mpls_interface.c new file mode 100644 index 000000000000..cfad3e452e5d --- /dev/null +++ b/usr.sbin/ldpd/mpls_interface.c @@ -0,0 +1,226 @@ +/* $NetBSD: mpls_interface.c,v 1.1 2010/12/08 07:20:14 kefren Exp $ */ + +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Mihai Chelaru + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "ldp.h" +#include "ldp_peer.h" +#include "ldp_errors.h" +#include "tlv_stack.h" +#include "label.h" +#include "mpls_interface.h" +#include "mpls_routes.h" + +#define ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) + +int +mpls_add_label(struct ldp_peer * p, struct rt_msg * inh_rg, + struct in_addr * addr, int len, int label, int rlookup) +{ + char padd[20]; + int kount = 0, rv; + union sockunion *so_dest, *so_pref = NULL, *so_gate, *so_nexthop, *so_tag, + *so_oldifa = NULL, *so_ifa; + struct rt_msg rg; + struct rt_msg *rgp = &rg; + struct label *lab; + + strlcpy(padd, inet_ntoa(p->address), 20); + debugp("Trying to add %s/%d as label %d to peer %s\n", inet_ntoa(*addr), + len, label, padd); + + /* Don't accept default route XXX: should be option-able */ + if (!len) + return LDP_E_BAD_AF; + + /* Is there a label mapping for this ? */ + if (ldp_peer_get_lm(p, addr, len) == NULL) + return LDP_E_NOENT; + + if (!inh_rg || (inh_rg->m_rtm.rtm_addrs & RTA_IFA) == 0) { + /* + * XXX: Check if we have a route for that. + * Why the hell isn't kernel inserting the route immediatly ? + * let's loop until we have it.. + */ + + so_dest = make_inet_union(inet_ntoa(*addr)); + if (len != 32) + so_pref = from_cidr_to_union(len); + + do { + if (kount == rlookup) { + debugp("No route for this prefix\n"); + return LDP_E_NO_SUCH_ROUTE; + } + kount++; + /* Last time give it a higher chance */ + if (kount == rlookup) + usleep(5000); + + rv = get_route(rgp, so_dest, so_pref, 1); + if (rv != LDP_E_OK && len == 32) + /* Host maybe ? */ + rv = get_route(rgp, so_dest, NULL, 1); + } while (rv != LDP_E_OK); + + free(so_dest); + if (so_pref) + free(so_pref); + + } else + rgp = inh_rg; + + /* Check if it's an IPv4 route */ + + so_gate = (union sockunion *) rgp->m_space; + so_gate = (union sockunion *)((char*)so_gate + + ROUNDUP(so_gate->sa.sa_len)); + if (rgp->m_rtm.rtm_addrs & RTA_IFA) { + int li = 1; + so_oldifa = so_gate; + if (rgp->m_rtm.rtm_addrs & RTA_NETMASK) + li++; + if (rgp->m_rtm.rtm_addrs & RTA_GENMASK) + li++; + if (rgp->m_rtm.rtm_addrs & RTA_IFP) + li++; + for (int i = 0; i < li; i++) + so_oldifa = (union sockunion *)((char*)so_oldifa + + ROUNDUP(so_oldifa->sa.sa_len)); + } + + if (so_gate->sa.sa_family != AF_INET) { + debugp("Failed at family check - only INET supoprted for now\n"); + return LDP_E_BAD_AF; + } + + /* Check if the address is bounded to the peer */ + if (check_ifaddr(p, &so_gate->sin.sin_addr) == NULL) { + debugp("Failed at next-hop check\n"); + return LDP_E_ROUTE_ERROR; + } + + /* CHECK IF WE HAVE A BINDING FOR THAT */ + lab = label_get_by_prefix(addr, len); + + /* We should have a label because we have a route */ + assert (lab); + + if (lab->binding == MPLS_LABEL_IMPLNULL) { + change_local_label(lab, get_free_local_label()); + if (!lab->binding) { + fatalp("No more free labels !!!\n"); + return LDP_E_TOO_MANY_LABELS; + } + } + + warnp("[mpls_add_label] Adding %s/%d as local binding %d, label %d" + " to peer %s\n", + inet_ntoa(*addr), len, lab->binding, label, padd); + + /* Modify existing label */ + lab->label = label; + lab->p = p; + + /* Add switching route */ + so_dest = make_mpls_union(lab->binding); + so_nexthop = (union sockunion *)malloc(sizeof(union sockunion)); + memcpy(so_nexthop, so_gate, so_gate->sa.sa_len); + so_tag = make_mpls_union(label); + if (add_route(so_dest, NULL, so_nexthop, NULL, so_tag, FREESO, RTM_ADD) != LDP_E_OK) + return LDP_E_ROUTE_ERROR; + + /* Now, let's add tag to IPv4 route and point it to mpls interface */ + so_dest = make_inet_union(inet_ntoa(*addr)); + + /* if prefixlen == 32 check if it's inserted as host + * and treat it as host. It may also be set as /32 prefix + * (thanks mlelstv for heads-up about this) + */ + if ((len == 32) && (rgp->m_rtm.rtm_flags & RTF_HOST)) + so_pref = NULL; + else + so_pref = from_cidr_to_union(len); + + /* Add tag to route */ + so_nexthop = (union sockunion *)malloc(sizeof(union sockunion)); + memcpy(so_nexthop, so_gate, so_gate->sa.sa_len); + so_tag = make_mpls_union(label); + if (so_oldifa != NULL) { + so_ifa = (union sockunion *)malloc(sizeof(union sockunion)); + memcpy(so_ifa, so_oldifa, so_oldifa->sa.sa_len); + } else + so_ifa = NULL; + if (add_route(so_dest, so_pref, so_nexthop, so_ifa, so_tag, FREESO, RTM_CHANGE) != LDP_E_OK) + return LDP_E_ROUTE_ERROR; + + debugp("Added %s/%d as label %d to peer %s\n", inet_ntoa(*addr), len, + label, padd); + + return LDP_E_OK; +} + +int +mpls_add_ldp_peer(struct ldp_peer * p) +{ + return LDP_E_OK; +} + +int +mpls_delete_ldp_peer(struct ldp_peer * p) +{ + + /* Reput all the routes also to IPv4 */ + label_reattach_all_peer_labels(p, LDP_READD_CHANGE); + + return LDP_E_OK; +} + +int +mpls_start_ldp() +{ + ldp_peer_init(); + label_init(); + + return LDP_E_OK; +} diff --git a/usr.sbin/ldpd/mpls_interface.h b/usr.sbin/ldpd/mpls_interface.h new file mode 100644 index 000000000000..336d84915f37 --- /dev/null +++ b/usr.sbin/ldpd/mpls_interface.h @@ -0,0 +1,43 @@ +/* $NetBSD: mpls_interface.h,v 1.1 2010/12/08 07:20:14 kefren Exp $ */ + +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Mihai Chelaru + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _MPLS_INTERFACE_H_ +#define _MPLS_INTERFACE_H_ + +#include "mpls_routes.h" + +int mpls_add_label(struct ldp_peer *, struct rt_msg *, + struct in_addr *, int, int, int); +int mpls_add_ldp_peer(struct ldp_peer *); +int mpls_delete_ldp_peer(struct ldp_peer *); +int mpls_start_ldp(void); + +#endif /* _MPLS_INTERFACE_H_ */ diff --git a/usr.sbin/ldpd/mpls_routes.c b/usr.sbin/ldpd/mpls_routes.c new file mode 100644 index 000000000000..5050dc98d007 --- /dev/null +++ b/usr.sbin/ldpd/mpls_routes.c @@ -0,0 +1,893 @@ +/* $NetBSD: mpls_routes.c,v 1.1 2010/12/08 07:20:15 kefren Exp $ */ + +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Mihai Chelaru + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include "ldp.h" +#include "ldp_errors.h" +#include "ldp_peer.h" +#include "mpls_interface.h" +#include "tlv_stack.h" +#include "label.h" +#include "mpls_routes.h" + +extern int route_socket; +int rt_seq = 0; +int dont_catch = 0; + +struct rt_msg replay_rt[REPLAY_MAX]; +int replay_index = 0; + +int read_route_socket(char *, int); +void mask_addr(union sockunion *); +int compare_sockunion(union sockunion *, union sockunion *); +char * mpls_ntoa(union mpls_shim); + +extern struct sockaddr mplssockaddr; + +/* Many lines inspired or shamelessly stolen from sbin/route/route.c */ + +#define ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) +#define NEXTADDR(u) \ + do { l = ROUNDUP(u->sa.sa_len); memcpy(cp, u, l); cp += l; } while(0); +#define NEXTADDR2(u) \ + do { l = ROUNDUP(u.sa_len); memcpy(cp, &u, l); cp += l; } while(0); +#define GETNEXT(sunion) \ + (union sockunion *) ((char *) (sunion) + ROUNDUP((sunion)->sa.sa_len)) + +int +read_route_socket(char *s, int max) +{ + int rv, to_read; + fd_set fs; + struct timeval tv; + struct rt_msghdr *rhdr; + + tv.tv_sec = 0; + tv.tv_usec = 5000; + + FD_ZERO(&fs); + FD_SET(route_socket, &fs); + + errno = 0; + + do { + rv = select(route_socket + 1, &fs, NULL, &fs, &tv); + } while ((rv == -1) && (errno == EINTR)); + + if (rv < 1) { + if (rv == 0) { + fatalp("read_route_socket: select timeout\n"); + } else + fatalp("read_route_socket: select: %s", + strerror(errno)); + return 0; + } + + do { + rv = recv(route_socket, s, max, MSG_PEEK); + } while((rv == -1) && (errno == EINTR)); + + if (rv < 1) { + debugp("read_route_socket: recv error\n"); + return 0; + } + if (rv > max) { + rv = max; + debugp("read_route_socket: rv > max\n"); + } + + rhdr = (struct rt_msghdr *)s; + to_read = rhdr->rtm_msglen > max ? max : rhdr->rtm_msglen; + rv = 0; + + do { + rv += recv(route_socket, s, to_read - rv, 0); + } while (rv != to_read); + + return rv; +} + +/* Recalculate length */ +void +mask_addr(union sockunion * su) +{ +/* + int olen = su->sa.sa_len; + char *cp1 = olen + (char *) su; + + for (su->sa.sa_len = 0; cp1 > (char *) su;) + if (*--cp1 != 0) { + su->sa.sa_len = 1 + cp1 - (char *) su; + break; + } +*/ +/* Let's use INET only version for the moment */ +su->sa.sa_len = 4 + from_union_to_cidr(su) / 8 + + ( from_union_to_cidr(su) % 8 ? 1 : 0 ); +} + +/* creates a sockunion from an IP address */ +union sockunion * +make_inet_union(char *s) +{ + union sockunion *so_inet; + + so_inet = (union sockunion *) malloc(sizeof(union sockunion)); + + if (!so_inet) { + fatalp("make_inet_union: malloc problem\n"); + return NULL; + } + + memset(so_inet, 0, sizeof(union sockunion)); + + so_inet->sin.sin_len = sizeof(struct sockaddr_in); + so_inet->sin.sin_family = AF_INET; + inet_aton(s, &so_inet->sin.sin_addr); + + return so_inet; +} + +/* creates a sockunion from a label */ +union sockunion * +make_mpls_union(uint32_t label) +{ + union sockunion *so_mpls; + + so_mpls = (union sockunion *) malloc(sizeof(union sockunion)); + + if (!so_mpls) { + fatalp("make_mpls_union: malloc problem\n"); + return NULL; + } + + memset(so_mpls, 0, sizeof(union sockunion)); + + so_mpls->smpls.smpls_len = sizeof(struct sockaddr_mpls); + so_mpls->smpls.smpls_family = AF_MPLS; + so_mpls->smpls.smpls_addr.shim.label = label; + + so_mpls->smpls.smpls_addr.s_addr = + htonl(so_mpls->smpls.smpls_addr.s_addr); + + return so_mpls; +} + +int +compare_sockunion(union sockunion * __restrict a, + union sockunion * __restrict b) +{ + if (a->sa.sa_len != b->sa.sa_len) + return 1; + return memcmp(a, b, a->sa.sa_len); +} + +union sockunion * +from_cidr_to_union(uint8_t prefixlen) +{ + union sockunion *u; + int32_t n = -1; + uint32_t *m = (uint32_t*)&n; + + *m = (*m >> (32 - prefixlen) ) << (32 - prefixlen); + *m = ntohl(*m); + + u = (union sockunion *) malloc(sizeof(union sockunion)); + + if (!u) { + fatalp("from_cidr_to_union: malloc problem\n"); + return NULL; + } + + memset (u, 0, sizeof(union sockunion)); + + u->sin.sin_len = sizeof(struct sockaddr_in); + u->sin.sin_family = AF_INET; + u->sin.sin_addr.s_addr = *m; + + return u; + +} + +uint8_t +from_mask_to_cidr(char *mask) +{ + /* LoL (although I don't think about something faster right now) */ + char mtest[20]; + uint8_t i; + + for (i = 1; i < 32; i++) { + from_cidr_to_mask(i, mtest); + if (!strcmp(mask, mtest)) + break; + } + return i; +} + +uint8_t +from_union_to_cidr(union sockunion *so_pref) +{ + struct sockaddr_in *sin = (struct sockaddr_in*)so_pref; + uint32_t a; + uint8_t r; + + a = ntohl(sin->sin_addr.s_addr); + for (r=0; a ; a = a << 1, r++); + + return r; +} + +/* returns in mask the netmask created from CIDR prefixlen */ +void +from_cidr_to_mask(uint8_t prefixlen, char *mask) +{ + uint32_t a = 0, p = prefixlen; + if (prefixlen > 32) { + strlcpy(mask, "255.255.255.255", 16); + return; + } + for (; p > 0; p--) { + a = a >> (p - 1); + a += 1; + a = a << (p - 1); + } + /* is this OK ? */ +#if _BYTE_ORDER == _LITTLE_ENDIAN + a = a << (32 - prefixlen); +#endif + + snprintf(mask, 16, "%d.%d.%d.%d", a >> 24, (a << 8) >> 24, + (a << 16) >> 24, (a << 24) >> 24); +} + +char * +mpls_ntoa(union mpls_shim ms) +{ + static char ret[255]; + union mpls_shim ms2; + + ms2.s_addr = ntohl(ms.s_addr); + snprintf(ret, sizeof(ret), "%d", ms2.shim.label); + return ret; +} + +char * +union_ntoa(union sockunion * so) +{ + static char defret[] = "Unknown family address"; + switch (so->sa.sa_family) { + case AF_INET: + return inet_ntoa(so->sin.sin_addr); + case AF_LINK: + return link_ntoa(&so->sdl); + case AF_MPLS: + return mpls_ntoa(so->smpls.smpls_addr); + } + fatalp("Unknown family address in union_ntoa: %d\n", + so->sa.sa_family); + return defret; +} + +/* From src/sbin/route/route.c */ +static const char * +route_strerror(int error) +{ + + switch (error) { + case ESRCH: + return "not in table"; + case EBUSY: + return "entry in use"; + case ENOBUFS: + return "routing table overflow"; + default: + return strerror(error); + } +} + + +/* Adds a route. Or changes it. */ +int +add_route(union sockunion *so_dest, union sockunion *so_prefix, + union sockunion *so_gate, union sockunion *so_ifa, union sockunion *so_tag, + int fr, int optype) +{ + int l, rlen, rv = LDP_E_OK; + struct rt_msg rm; + char *cp; + + if(dont_catch) + return LDP_E_OK; + + memset(&rm, 0, sizeof(rm)); + cp = rm.m_space; + + rm.m_rtm.rtm_type = (optype == RTM_READD) ? RTM_ADD : optype; + rm.m_rtm.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC; + + rm.m_rtm.rtm_version = RTM_VERSION; + rm.m_rtm.rtm_seq = ++rt_seq; + rm.m_rtm.rtm_addrs = RTA_DST; + if (so_gate) + rm.m_rtm.rtm_addrs |= RTA_GATEWAY; + + assert(so_dest); + + /* Order is: destination, gateway, netmask, genmask, ifp, ifa, tag */ + NEXTADDR(so_dest); + if (so_gate) + NEXTADDR(so_gate); + + if (so_prefix) { + mask_addr(so_prefix); + NEXTADDR(so_prefix); + /* XXX: looks like nobody cares about this */ + rm.m_rtm.rtm_flags |= RTF_MASK; + rm.m_rtm.rtm_addrs |= RTA_NETMASK; + } else + rm.m_rtm.rtm_flags |= RTF_HOST; + + /* route to mpls interface */ + if (optype != RTM_READD && so_dest->sa.sa_family != AF_MPLS) { + NEXTADDR2(mplssockaddr); + rm.m_rtm.rtm_addrs |= RTA_IFP; + } + + if (so_ifa != NULL) { + NEXTADDR(so_ifa); + rm.m_rtm.rtm_addrs |= RTA_IFA; + } + + if (so_tag) { + NEXTADDR(so_tag); + rm.m_rtm.rtm_addrs |= RTA_TAG; + } + + rm.m_rtm.rtm_msglen = l = cp - (char *) &rm; + + if ((rlen = write(route_socket, (char *) &rm, l)) < l) { + warnp("Error adding a route: %s\n", route_strerror(errno)); + warnp("Destination was: %s\n", union_ntoa(so_dest)); + if (so_prefix) + warnp("Prefix was: %s\n", union_ntoa(so_prefix)); + if (so_gate) + warnp("Gateway was: %s\n", union_ntoa(so_gate)); + rv = LDP_E_ROUTE_ERROR; + } + if (fr) { + free(so_dest); + if (so_prefix) + free(so_prefix); + if (so_gate) + free(so_gate); + if (so_ifa) + free(so_ifa); + if (so_tag) + free(so_tag); + } + + return rv; +} + +/* Deletes a route */ +int +delete_route(union sockunion * so_dest, union sockunion * so_pref, int freeso) +{ + int l, rlen; + struct rt_msg rm; + char *cp; + + if(dont_catch) + return LDP_E_OK; + + memset(&rm, 0, sizeof(struct rt_msg)); + cp = rm.m_space; + + rm.m_rtm.rtm_type = RTM_DELETE; + rm.m_rtm.rtm_version = RTM_VERSION; + rm.m_rtm.rtm_seq = ++rt_seq; + if (so_pref) + rm.m_rtm.rtm_addrs = RTA_DST | RTA_NETMASK; + else + rm.m_rtm.rtm_addrs = RTA_DST; + + /* destination, gateway, netmask, genmask, ifp, ifa */ + + NEXTADDR(so_dest); + + if (so_pref) { + mask_addr(so_pref); + NEXTADDR(so_pref); + } + rm.m_rtm.rtm_msglen = l = cp - (char *) &rm; + + if (freeso == FREESO) { + free(so_dest); + if (so_pref) + free(so_pref); + } + if ((rlen = write(route_socket, (char *) &rm, l)) < l) { + if(so_pref) { + char spreftmp[INET_ADDRSTRLEN]; + strlcpy(spreftmp, inet_ntoa(so_pref->sin.sin_addr), + INET_ADDRSTRLEN); + warnp("Error deleting route(%s): %s/%s", + route_strerror(errno), union_ntoa(so_dest), + spreftmp); + } else + warnp("Error deleting route(%s) : %s", + route_strerror(errno), union_ntoa(so_dest)); + return LDP_E_NO_SUCH_ROUTE; + } + return LDP_E_OK; +} + +/* + * Check for a route and returns it in rg + * If exact_match is set it compares also the so_dest and so_pref + * with the returned result + */ +int +get_route(struct rt_msg * rg, union sockunion * so_dest, + union sockunion * so_pref, int exact_match) +{ + int l, rlen, myseq; + struct rt_msg rm; + char *cp; + union sockunion *su; + + memset(&rm, 0, sizeof(struct rt_msg)); + cp = rm.m_space; + + myseq = ++rt_seq; + + rm.m_rtm.rtm_type = RTM_GET; + rm.m_rtm.rtm_version = RTM_VERSION; + rm.m_rtm.rtm_seq = myseq; + + /* + * rtm_addrs should contain what we provide into this message but + * RTA_DST | RTA_IFP trick is allowed in order to find out the + * interface. + */ + + rm.m_rtm.rtm_addrs = RTA_DST | RTA_IFP; + + /* + * ORDER of fields is: destination, gateway, netmask, genmask, ifp, + * ifa + */ + + NEXTADDR(so_dest); + if (so_pref) { + rm.m_rtm.rtm_addrs |= RTA_NETMASK; + mask_addr(so_pref); + NEXTADDR(so_pref); + } + rm.m_rtm.rtm_msglen = l = cp - (char *) &rm; + + if ((rlen = write(route_socket, (char *) &rm, l)) < l) { + debugp("Cannot get a route !(rlen=%d instead of %d) - %s\n", + rlen, l, strerror(errno)); + return LDP_E_NO_SUCH_ROUTE; + } else + do { + rlen = read_route_socket((char *) rg, + sizeof(struct rt_msg)); + if (rlen < 1) + break; + /* + * We might lose important messages here. WORKAROUND: + * For now I just try to save this messages and replay + * them later + */ + if ((rg->m_rtm.rtm_pid != getpid()) || + (rg->m_rtm.rtm_seq != myseq)) { + /* + * Shortcut: my pid but not + * the expected sequence + */ + if (rg->m_rtm.rtm_pid == getpid()) + continue; + + debugp("Added to replay PID: %d, SEQ: %d\n", + rg->m_rtm.rtm_pid, rg->m_rtm.rtm_seq); + memcpy(&replay_rt[replay_index], rg, + sizeof(struct rt_msg)); + if (replay_index < REPLAY_MAX - 1) + replay_index++; + continue; + } + } while ((rg->m_rtm.rtm_seq != myseq) || + (rg->m_rtm.rtm_pid != getpid())); + + if ((uint)rlen <= sizeof(struct rt_msghdr)) { + debugp("Got only %d bytes, expecting at least %u\n", rlen, + sizeof(struct rt_msghdr)); + return LDP_E_ROUTE_ERROR; + } + + /* Check if we don't have a less specific route */ + if (exact_match) { + su = (union sockunion*)(rg->m_space); + if (compare_sockunion(so_dest, su)) { + debugp("Dest %s ", union_ntoa(so_dest)); + debugp("not like %s\n", union_ntoa(su)); + return LDP_E_NO_SUCH_ROUTE; + } + } + + return LDP_E_OK; +} + + +/* triggered when a route event occurs */ +int +check_route(struct rt_msg * rg, uint rlen) +{ + union sockunion *so_dest = NULL, *so_gate = NULL, *so_pref = NULL; + int so_pref_allocated = 0; + int prefixlen; + struct peer_map *pm; + struct label *lab; + char dest[50], gate[50], pref[50], oper[50]; + dest[0] = 0; + gate[0] = 0; + pref[0] = 0; + + if (rlen <= sizeof(struct rt_msghdr)) + return LDP_E_ROUTE_ERROR; + + if (rg->m_rtm.rtm_version != RTM_VERSION) + return LDP_E_ROUTE_ERROR; + + if ((rg->m_rtm.rtm_flags & RTF_DONE) == 0) + return LDP_E_OK; + + if (rg->m_rtm.rtm_pid == getpid()) /* We did it.. */ + return LDP_E_OK; + else + debugp("Check route triggered by PID: %d\n", rg->m_rtm.rtm_pid); + + so_dest = (union sockunion *) rg->m_space; + + if (so_dest->sa.sa_family != AF_INET) + return LDP_E_OK;/* We don't care about non-IP changes */ + + if (rg->m_rtm.rtm_addrs & RTA_GATEWAY) { + so_gate = GETNEXT(so_dest); + if ((so_gate->sa.sa_family != AF_INET) && + (so_gate->sa.sa_family != AF_MPLS)) + return LDP_E_OK; + } + if (rg->m_rtm.rtm_addrs & RTA_NETMASK) { + if (so_gate) + so_pref = so_gate; + else + so_pref = so_dest; + so_pref = GETNEXT(so_pref); + } + if (!(rg->m_rtm.rtm_flags & RTF_GATEWAY)) { + if (rg->m_rtm.rtm_addrs & RTA_GENMASK) { + debugp("Used GENMASK\n"); + } else + debugp("No GENMASK to use\n"); + } + /* Calculate prefixlen */ + if (so_pref) + prefixlen = from_mask_to_cidr(inet_ntoa(so_pref->sin.sin_addr)); + else { + prefixlen = 32; + so_pref = from_cidr_to_union(32); + so_pref_allocated = 1; + } + + so_pref->sa.sa_family = AF_INET; + so_pref->sa.sa_len = sizeof(struct sockaddr_in); + + switch (rg->m_rtm.rtm_type) { + case RTM_CHANGE: + warnp("XXX: RTM_CHANGE\n"); + /* Fallthrough */ + case RTM_ADD: + /* + * Check if the route is connected. If so, bind it to + * POP_LABEL and send announce. If not, check if the prefix + * was announced by a LDP neighbour and route it there + */ + + /* First of all check if we already know this one */ + lab = label_get(so_dest, so_pref); + if (!lab) { + if (!(rg->m_rtm.rtm_flags & RTF_GATEWAY)) + lab = label_add(so_dest, so_pref, NULL, + MPLS_LABEL_IMPLNULL, NULL, 0); + else { + pm = ldp_test_mapping(&so_dest->sin.sin_addr, + prefixlen, &so_gate->sin.sin_addr); + if (pm) { + lab = label_add(so_dest, so_pref, + so_gate, 0, NULL, 0); + mpls_add_label(pm->peer, rg, + &so_dest->sin.sin_addr, prefixlen, + pm->lm->label, ROUTE_LOOKUP_LOOP); + free(pm); + } else + lab = label_add(so_dest, so_pref, + so_gate, MPLS_LABEL_IMPLNULL, + NULL, 0); + } + } else /* We already know about this prefix */ + debugp("Binding already there for prefix %s/%d !\n", + union_ntoa(so_dest), prefixlen); + break; + case RTM_DELETE: + if (!so_gate) + break; /* Non-existent route XXX ?! */ + /* + * Send withdraw check the binding, delete the route, delete + * the binding + */ + lab = label_get(so_dest, so_pref); + if (!lab) + break; + send_withdraw_tlv_to_all(&so_dest->sin.sin_addr, prefixlen); + /* No readd as IPv4. Also don't even try to delete it */ + label_reattach_route(lab, LDP_READD_NODEL); + label_del(lab); + break; + } + + /* Rest is just for debug */ + + if (so_dest) + strlcpy(dest, union_ntoa(so_dest), 16); + if (so_pref) + snprintf(pref, 3, "%d", prefixlen); + if (so_gate) + strlcpy(gate, union_ntoa(so_gate), 16); + + switch (rg->m_rtm.rtm_type) { + case RTM_ADD: + strlcpy(oper, "added", 20); + break; + case RTM_DELETE: + strlcpy(oper, "delete", 20); + break; + case RTM_GET: + strlcpy(oper, "get", 20); + break; + case RTM_CHANGE: + strlcpy(oper, "change", 20); + break; + case RTM_LOSING: + strlcpy(oper, "losing", 20); + break; + case RTM_NEWADDR: + strlcpy(oper, "new address", 20); + break; + case RTM_DELADDR: + strlcpy(oper, "del address", 20); + break; + default: + snprintf(oper, 50, "unknown 0x%X operation", + rg->m_rtm.rtm_type); + } + + warnp("[check_route] Route %s: %s / %s -> %s by PID:%d\n", oper, dest, + pref, gate, rg->m_rtm.rtm_pid); + + if(so_pref_allocated) + free(so_pref); + return LDP_E_OK; +} + +int +bind_current_routes() +{ + size_t needed; + int mib[6]; + char *buf, *next, *lim; + struct rt_msghdr *rtmes; + union sockunion *so_dst, *so_pref, *so_gate; + struct label *lab; + + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; + mib[3] = 0; + mib[4] = NET_RT_DUMP; + mib[5] = 0; + if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) { + fatalp("route-sysctl-estimate: %s", + strerror(errno)); + return LDP_E_ROUTE_ERROR; + } + if ((buf = malloc(needed)) == 0) + return LDP_E_ROUTE_ERROR; + if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) { + free(buf); + return LDP_E_ROUTE_ERROR; + } + lim = buf + needed; + + for (next = buf; next < lim; next += rtmes->rtm_msglen) { + rtmes = (struct rt_msghdr *) next; + so_pref = NULL; + so_gate = NULL; + if (rtmes->rtm_flags & RTF_LLINFO) /* No need for arps */ + continue; + if (!(rtmes->rtm_addrs & RTA_DST)) { + debugp("No dst\n"); + continue; + } + + so_dst = (union sockunion *) & rtmes[1]; + + /* + * As this function is call only at startup use this ocassion + * to delete all MPLS routes + */ + if (so_dst->sa.sa_family == AF_MPLS) { + delete_route(so_dst, NULL, NO_FREESO); + debugp("MPLS route deleted.\n"); + continue; + } + + if (so_dst->sa.sa_family != AF_INET) { + debugp("sa_dst is not AF_INET\n"); + continue; + } + + /* Check if it's the default gateway */ + if (so_dst->sin.sin_addr.s_addr == 0) + continue; + + /* XXX: Check if it's loopback */ + if ((ntohl(so_dst->sin.sin_addr.s_addr) >> 24)==IN_LOOPBACKNET) + continue; + + /* Get Gateway */ + if (rtmes->rtm_addrs & RTA_GATEWAY) + so_gate = GETNEXT(so_dst); + + /* Get prefix */ + if (rtmes->rtm_flags & RTF_HOST) + so_pref = from_cidr_to_union(32); + else if (rtmes->rtm_addrs & RTA_GATEWAY) + so_pref = GETNEXT(so_gate); + else + so_pref = GETNEXT(so_dst); + + so_pref->sa.sa_family = AF_INET; + so_pref->sa.sa_len = sizeof(struct sockaddr_in); + + /* Also deletes when dest is IPv4 and gateway MPLS */ + if ((rtmes->rtm_addrs & RTA_GATEWAY) && + (so_gate->sa.sa_family == AF_MPLS)) { + debugp("MPLS route to %s deleted.\n", + inet_ntoa(so_dst->sin.sin_addr)); + delete_route(so_dst, so_pref, NO_FREESO); + if (rtmes->rtm_flags & RTF_HOST) + free(so_pref); + continue; + } + if (so_gate->sa.sa_family == AF_INET) + lab = label_add(so_dst, so_pref, so_gate, + MPLS_LABEL_IMPLNULL, NULL, 0); + + if (rtmes->rtm_flags & RTF_HOST) + free(so_pref); + } + free(buf); + return LDP_E_OK; +} + +int +flush_mpls_routes() +{ + size_t needed; + int mib[6]; + char *buf, *next, *lim; + struct rt_msghdr *rtm; + union sockunion *so_dst, *so_pref, *so_gate; + + mib[0] = CTL_NET; + mib[1] = PF_ROUTE; + mib[2] = 0; + mib[3] = 0; + mib[4] = NET_RT_DUMP; + mib[5] = 0; + if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) { + fatalp("route-sysctl-estimate: %s", strerror(errno)); + return LDP_E_ROUTE_ERROR; + } + if ((buf = malloc(needed)) == 0) + return LDP_E_ROUTE_ERROR; + if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) { + free(buf); + return LDP_E_ROUTE_ERROR; + } + lim = buf + needed; + + for (next = buf; next < lim; next += rtm->rtm_msglen) { + rtm = (struct rt_msghdr *) next; + so_pref = NULL; + so_gate = NULL; + if (rtm->rtm_flags & RTF_LLINFO) /* No need for arps */ + continue; + if (!(rtm->rtm_addrs & RTA_DST)) { + debugp("No dst\n"); + continue; + } + so_dst = (union sockunion *) & rtm[1]; + + if (so_dst->sa.sa_family == AF_MPLS) { + delete_route(so_dst, NULL, NO_FREESO); + debugp("MPLS route deleted.\n"); + continue; + } + + if (rtm->rtm_addrs & RTA_GATEWAY) { + so_gate = GETNEXT(so_dst); + so_pref = GETNEXT(so_gate); + } else + so_pref = GETNEXT(so_dst); + + if (so_gate->sa.sa_family == AF_MPLS) { + debugp("MPLS route to %s deleted.\n", + inet_ntoa(so_dst->sin.sin_addr)); + delete_route(so_dst, so_pref, NO_FREESO); + continue; + } + + } + free(buf); + return LDP_E_OK; +} diff --git a/usr.sbin/ldpd/mpls_routes.h b/usr.sbin/ldpd/mpls_routes.h new file mode 100644 index 000000000000..c9655df1fb8b --- /dev/null +++ b/usr.sbin/ldpd/mpls_routes.h @@ -0,0 +1,78 @@ +/* $NetBSD: mpls_routes.h,v 1.1 2010/12/08 07:20:15 kefren Exp $ */ + +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Mihai Chelaru + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _MPLS_ROUTES_H_ +#define _MPLS_ROUTES_H_ + +#include +#include +#include +#include +#include +#include + +#include + +#define NO_FREESO 0 +#define FREESO 1 + +#define RTM_READD -1 + +union sockunion { + struct sockaddr sa; + struct sockaddr_in sin; + struct sockaddr_mpls smpls; + struct sockaddr_dl sdl; +}; + +struct rt_msg { + struct rt_msghdr m_rtm; + char m_space[512]; +} __packed; + +union sockunion * make_inet_union(char *); +union sockunion * make_mpls_union(uint32_t); +union sockunion * make_mplsinet_union(uint16_t peer, uint32_t label, + struct in_addr *addr); +uint8_t from_mask_to_cidr(char *); +void from_cidr_to_mask(uint8_t, char *); +int add_route(union sockunion *, union sockunion *, union sockunion *, + union sockunion *, union sockunion *, int, int); +int delete_route(union sockunion *, union sockunion *, int); +int get_route(struct rt_msg *, union sockunion *, union sockunion *, int); +int bind_current_routes(void); +int flush_mpls_routes(void); +int check_route(struct rt_msg *, uint); +char* union_ntoa(union sockunion *); +uint8_t from_union_to_cidr(union sockunion *); +union sockunion * from_cidr_to_union(uint8_t); + +#endif /* !_MPLS_ROUTES_H_ */ diff --git a/usr.sbin/ldpd/notifications.c b/usr.sbin/ldpd/notifications.c new file mode 100644 index 000000000000..dd1dadc5f1e8 --- /dev/null +++ b/usr.sbin/ldpd/notifications.c @@ -0,0 +1,79 @@ +/* $NetBSD: notifications.c,v 1.1 2010/12/08 07:20:15 kefren Exp $ */ + +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Mihai Chelaru + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include "ldp.h" +#include "socketops.h" +#include "tlv.h" +#include "ldp_errors.h" +#include "notifications.h" + +/* builds a notification TLV. Do not forget to free the result */ +struct notification_tlv * +build_notification(uint32_t msg, uint32_t n) +{ + struct notification_tlv *t; + t = (struct notification_tlv *) malloc(sizeof(struct notification_tlv)); + + if (!t) { + fatalp("build_notification: malloc problem\n"); + return NULL; + } + + t->type = htons(LDP_NOTIFICATION); + t->length = htons(sizeof(struct notification_tlv) - TLV_TYPE_LENGTH); + t->status = htons(TLV_STATUS); + t->st_len = sizeof(struct notification_tlv) - 2 * TLV_TYPE_LENGTH; + t->st_code = htonl(n); + t->msg_id = htonl(msg); + t->msg_type = 0; + return t; + +} + +int +send_notification(struct ldp_peer * p, uint32_t msg, uint32_t code) +{ + struct notification_tlv *nt; + int rv; + + if (p->state != LDP_PEER_CONNECTED && p->state != LDP_PEER_ESTABLISHED) + return -1; + + nt = build_notification(msg, code); + + rv = send_tlv(p, (struct tlv *) nt); + free(nt); + return rv; +} diff --git a/usr.sbin/ldpd/notifications.h b/usr.sbin/ldpd/notifications.h new file mode 100644 index 000000000000..be7af6d09a26 --- /dev/null +++ b/usr.sbin/ldpd/notifications.h @@ -0,0 +1,98 @@ +/* $NetBSD: notifications.h,v 1.1 2010/12/08 07:20:15 kefren Exp $ */ + +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Mihai Chelaru + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _NOTIFICATIONS_H_ +#define _NOTIFICATIONS_H_ + +/* Notifications codes RFC3036 2.9 */ +#define NOTIF_SUCCESS 0x00000000 +#define NOTIF_BAD_LDP_ID 0x00000001 +#define NOTIF_BAD_LDP_VER 0x00000002 +#define NOTIF_BAD_PDU_LEN 0x00000003 +#define NOTIF_UNKNOWN_MESSAGE 0x00000004 +#define NOTIF_BAD_MSG_LEN 0x00000005 +#define NOTIF_UNKNOWN_TLV 0x00000006 +#define NOTIF_BAD_TLV_LEN 0x00000007 +#define NOTIF_MALFORMED_TLV_VALUE 0x00000008 +#define NOTIF_HOLD_TIME_EXPIRED 0x00000009 +#define NOTIF_SHUTDOWN 0x0000000A +#define NOTIF_LOOP_DETECTED 0x0000000B +#define NOTIF_UNKNOWN_FEC 0x0000000C +#define NOTIF_NO_ROUTE 0x0000000D +#define NOTIF_NO_LABEL_RESOURCES 0x0000000E +#define NOTIF_LABEL_RESOURCES_AVAIL 0x0000000F +#define NOTIF_SESSION_REJECTED_NO_HELLO 0x00000010 +#define NOTIF_SESSION_REJECTED_ADV_MODE 0x00000011 +#define NOTIF_SESSION_REJECTED_MAX_PDU 0x00000012 +#define NOTIF_SESSION_REJECTED_LRANGE 0x00000013 +#define NOTIF_KEEP_ALIVE_TIMER_EXPIRED 0x00000014 +#define NOTIF_LABEL_REQUEST_ABORTED 0x00000015 +#define NOTIF_MISSING_MESSAGE 0x00000016 +#define NOTIF_UNSUPPORTED_AF 0x00000017 +#define NOTIF_SESSION_REJECTED_BAD_KEEP 0x00000018 +#define NOTIF_INTERNAL_ERROR 0x00000019 + +#define NOTIF_FATAL 0x80000000 + +static const char *NOTIF_STR[] __unused = +{ + "Success", + "Bad LDP ID", + "Bad LDP Version", + "Bad PDU Length", + "Unknown message", + "Bad message length", + "Unknown TLV", + "Bad TLV Length", + "Malformed TLV Value", + "Hold time expired", + "Shutdown", + "Loop detected", + "Unknown FEC", + "No route", + "No label resources", + "Label resources available", + "Session rejected: No hello", + "Session rejected: Parameters Advertising Mode", + "Session rejected: Max PDU Length", + "Session rejected: Label range", + "Keepalive timer expired", + "Label request aborted", + "Missing message", + "Unsupported Address Family", + "Session rejected: Bad keepalive time", + "Internal error" +}; + +struct notification_tlv * build_notification(uint32_t, uint32_t); +int send_notification(struct ldp_peer *, uint32_t, uint32_t); + +#endif /* !_NOTIFICATIONS_H_ */ diff --git a/usr.sbin/ldpd/pdu.c b/usr.sbin/ldpd/pdu.c new file mode 100644 index 000000000000..faa042af45a1 --- /dev/null +++ b/usr.sbin/ldpd/pdu.c @@ -0,0 +1,90 @@ +/* $NetBSD: pdu.c,v 1.1 2010/12/08 07:20:15 kefren Exp $ */ + +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Mihai Chelaru + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include + +#include "socketops.h" +#include "ldp_errors.h" +#include "ldp.h" +#include "ldp_peer.h" +#include "notifications.h" +#include "pdu.h" + +uint +get_pdu(unsigned char *s, struct ldp_pdu * p) +{ + struct ldp_pdu *p1; + p1 = (struct ldp_pdu *) s; + + p->version = ntohs(p1->version); + p->length = ntohs(p1->length); + memcpy(&p->ldp_id, &p1->ldp_id, sizeof(struct in_addr)); + p->label_space = ntohs(p1->label_space); + + return MIN_PDU_SIZE; +} + +/* Checks an incoming PDU for size and version */ +int +check_recv_pdu(struct ldp_peer * p, struct ldp_pdu * rpdu, int c) +{ + struct notification_tlv *notiftlv; + + /* Avoid underflow */ + if (c < MIN_PDU_SIZE) + return LDP_E_BAD_LENGTH; + + + /* Check PDU for right LDP version */ + if (ntohs(rpdu->version) != LDP_VERSION) { + fatalp("Invalid PDU version received from %s (%d)\n", + inet_ntoa(p->address), ntohs(rpdu->version)); + notiftlv = build_notification(0, NOTIF_BAD_LDP_VER); + send_tlv(p, (struct tlv *) notiftlv); + free(notiftlv); + return LDP_E_BAD_VERSION; + } + /* Check PDU for length validity */ + if (ntohs(rpdu->length) > c - PDU_VER_LENGTH) { + fatalp("Invalid PDU length received from %s (announced %d, " + "received %d)\n", inet_ntoa(p->address), + ntohs(rpdu->length), (int) (c - PDU_VER_LENGTH)); + notiftlv = build_notification(0, NOTIF_BAD_PDU_LEN); + send_tlv(p, (struct tlv *) notiftlv); + free(notiftlv); + return LDP_E_BAD_LENGTH; + } + return LDP_E_OK; +} diff --git a/usr.sbin/ldpd/pdu.h b/usr.sbin/ldpd/pdu.h new file mode 100644 index 000000000000..7e46a913ec72 --- /dev/null +++ b/usr.sbin/ldpd/pdu.h @@ -0,0 +1,58 @@ +/* $NetBSD: pdu.h,v 1.1 2010/12/08 07:20:15 kefren Exp $ */ + +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Mihai Chelaru + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _PDU_H_ +#define _PDU_H_ + +#include +#include + +#include "ldp_peer.h" + +#define MIN_PDU_SIZE 10 +#define MAX_PDU_SIZE 1300 + +#define PDU_VER_LENGTH (sizeof(uint16_t) + sizeof(uint16_t)) +#define PDU_PAYLOAD_LENGTH (sizeof(struct in_addr) + sizeof(uint16_t)) + +struct ldp_pdu { + uint16_t version; + uint16_t length; + struct in_addr ldp_id; + uint16_t label_space; +} __packed; + + +uint get_pdu(unsigned char *, struct ldp_pdu *); +int check_recv_pdu(struct ldp_peer *, struct ldp_pdu *, int); + + +#endif /* !_PDU_H_ */ diff --git a/usr.sbin/ldpd/socketops.c b/usr.sbin/ldpd/socketops.c new file mode 100644 index 000000000000..58c4966b27c5 --- /dev/null +++ b/usr.sbin/ldpd/socketops.c @@ -0,0 +1,1055 @@ +/* $NetBSD: socketops.c,v 1.1 2010/12/08 07:20:15 kefren Exp $ */ + +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Mihai Chelaru + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fsm.h" +#include "ldp.h" +#include "ldp_command.h" +#include "tlv.h" +#include "ldp_peer.h" +#include "notifications.h" +#include "tlv_stack.h" +#include "mpls_interface.h" +#include "label.h" +#include "mpls_routes.h" +#include "ldp_errors.h" +#include "socketops.h" + +int ls; /* TCP listening socket on port 646 */ +int route_socket; /* used to see when a route is added/deleted */ +int hello_socket; /* hello multicast listener - transmitter */ +int command_socket; /* Listening socket for interface command */ +int current_msg_id = 0x233; +int command_port = LDP_COMMAND_PORT; +extern int replay_index; +extern struct rt_msg replay_rt[REPLAY_MAX]; +extern struct com_sock csockets[MAX_COMMAND_SOCKETS]; + +int ldp_hello_time = LDP_HELLO_TIME; + +void recv_pdu(int); +void send_hello_alarm(int); +void bail_out(int); +static int get_local_addr(struct sockaddr_dl *, struct in_addr *); + +int +create_hello_socket() +{ + struct ip_mreq mcast_addr; + int s = socket(PF_INET, SOCK_DGRAM, 17); + + if (s < 0) + return s; + + /* + * RFC3036 specifies we should listen to all subnet routers multicast + * group + */ + mcast_addr.imr_multiaddr.s_addr = inet_addr(ALL_ROUTERS); + mcast_addr.imr_interface.s_addr = htonl(INADDR_ANY); + + socket_reuse_port(s); + /* Bind it to port 646 on specific address */ + if (bind_socket(s, htonl(INADDR_ANY)) == -1) { + warnp("Cannot bind hello socket\n"); + close(s); + return -1; + } + /* We don't need to receive back our messages */ + if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, &(uint8_t){0}, + sizeof(uint8_t)) == -1) { + fatalp("setsockopt: %s", strerror(errno)); + close(s); + return -1; + } + /* Finally join the group */ + if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mcast_addr, + sizeof(mcast_addr)) == -1) { + fatalp("setsockopt: %s", strerror(errno)); + close(s); + return -1; + } + /* TTL:1, TOS: 0xc0 */ + if (set_mcast_ttl(s) == -1) { + close(s); + return -1; + } + if (set_tos(s) == -1) { + fatalp("set_tos: %s", strerror(errno)); + close(s); + return -1; + } + if (setsockopt(s, IPPROTO_IP, IP_RECVIF, &(uint32_t){1}, sizeof(uint32_t)) == -1) { + fatalp("Cannot set IP_RECVIF\n"); + close(s); + return -1; + } + hello_socket = s; + return hello_socket; +} + +/* Sets the TTL to 1 as we don't want to transmit outside this subnet */ +int +set_ttl(int s) +{ + int ret; + if ((ret = setsockopt(s, IPPROTO_IP, IP_TTL, &(int){1}, sizeof(int))) + == -1) + fatalp("set_ttl: %s", strerror(errno)); + return ret; +} + +/* Sets multicast TTL to 1 */ +int +set_mcast_ttl(int s) +{ + int ret; + if ((ret = setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, &(int){1}, + sizeof(int))) == -1) + fatalp("set_mcast_ttl: %s", strerror(errno)); + return ret; +} + +/* Sets TOS to 0xc0 aka IP Precedence 6 */ +int +set_tos(int s) +{ + int ret; + if ((ret = setsockopt(s, IPPROTO_IP, IP_TOS, &(int){0xc0}, + sizeof(int))) == -1) + fatalp("set_tos: %s", strerror(errno)); + return ret; +} + +int +socket_reuse_port(int s) +{ + int ret; + if ((ret = setsockopt(s, SOL_SOCKET, SO_REUSEPORT, &(int){1}, + sizeof(int))) == -1) + fatalp("socket_reuse_port: %s", strerror(errno)); + return ret; +} + +/* binds an UDP socket */ +int +bind_socket(int s, uint32_t addr) +{ + struct sockaddr_in sa; + + sa.sin_len = sizeof(sa); + sa.sin_family = AF_INET; + sa.sin_port = htons(LDP_PORT); + sa.sin_addr.s_addr = addr; + if (bind(s, (struct sockaddr *) (&sa), sizeof(sa))) { + fatalp("bind_socket: %s", strerror(errno)); + return -1; + } + return 0; +} + +/* Create / bind the TCP socket */ +int +create_listening_socket(void) +{ + struct sockaddr_in sa; + int s; + + sa.sin_len = sizeof(sa); + sa.sin_family = AF_INET; + sa.sin_port = htons(LDP_PORT); + sa.sin_addr.s_addr = htonl(INADDR_ANY); + + s = socket(PF_INET, SOCK_STREAM, 6); + if (s < 0) + return s; + if (bind(s, (struct sockaddr *) & sa, sizeof(sa))) { + fatalp("bind: %s", strerror(errno)); + close(s); + return -1; + } + if (listen(s, 10) == -1) { + fatalp("listen: %s", strerror(errno)); + close(s); + return -1; + } +/* if (set_tos(s) == -1) { + fatalp("set_tos: %s", strerror(errno)); + close(s); + return -1; + } +*/ return s; +} + +/* + * It's ugly. We need a function to pass all tlvs and create pdu but since I + * use UDP socket only to send hellos, I didn't bother + */ +void +send_hello(void) +{ + struct hello_tlv *t; + struct common_hello_tlv *cht; + struct ldp_pdu *spdu; + struct transport_address_tlv *trtlv; + void *v; + struct sockaddr_in sadest; /* Destination ALL_ROUTERS */ + int sb = 0; /* sent bytes */ + struct ifaddrs *ifa, *ifb; + struct sockaddr_in *if_sa; + char lastifname[20]; + +#define HELLO_MSG_SIZE (sizeof(struct ldp_pdu) + /* PDU */ \ + TLV_TYPE_LENGTH + MSGID_SIZE + /* Hello TLV */ \ + /* Common Hello TLV */ \ + sizeof(struct common_hello_tlv) + \ + /* IPv4 Transport Address */ \ + sizeof(struct transport_address_tlv)) + + if ((v = malloc(HELLO_MSG_SIZE)) == NULL) { + fatalp("malloc problem in send_hello()\n"); + return; + } + memset(v, 0, HELLO_MSG_SIZE); + + spdu = (struct ldp_pdu *)((char *)v); + t = (struct hello_tlv *)(spdu + 1); + cht = &t->ch; /* Hello tlv struct includes CHT */ + trtlv = (struct transport_address_tlv *)(t + 1); + + /* Prepare PDU envelope */ + spdu->version = htons(LDP_VERSION); + spdu->length = htons(HELLO_MSG_SIZE - PDU_VER_LENGTH); + inet_aton(LDP_ID, &spdu->ldp_id); + + /* Prepare Hello TLV */ + t->type = htons(LDP_HELLO); + t->length = htons(MSGID_SIZE + + sizeof(struct common_hello_tlv) + + sizeof(struct transport_address_tlv)); + /* + * I used ID 0 instead of htonl(get_message_id()) because I've + * seen hellos from a cisco router doing the same thing + */ + t->messageid = 0; + + /* Prepare Common Hello attributes */ + cht->type = htons(TLV_COMMON_HELLO); + cht->length = htons(sizeof(cht->holdtime) + sizeof(cht->res)); + cht->holdtime = htons(LDP_HOLDTIME); + cht->res = 0; + + /* + * Prepare Transport Address TLV RFC3036 says: "If this optional TLV + * is not present the IPv4 source address for the UDP packet carrying + * the Hello should be used." But we send it because everybody seems + * to do so + */ + trtlv->type = htons(TLV_IPV4_TRANSPORT); + trtlv->length = htons(sizeof(struct in_addr)); + /* trtlv->address will be set for each socket */ + + /* Destination sockaddr */ + memset(&sadest, 0, sizeof(sadest)); + sadest.sin_len = sizeof(sadest); + sadest.sin_family = AF_INET; + sadest.sin_port = htons(LDP_PORT); + inet_aton(ALL_ROUTERS, &sadest.sin_addr); + + if (getifaddrs(&ifa) == -1) { + free(v); + return; + } + + lastifname[0] = '\0'; + for (ifb = ifa; ifb; ifb = ifb->ifa_next) { + if_sa = (struct sockaddr_in *) ifb->ifa_addr; + if (if_sa->sin_family != AF_INET) + continue; + if (ntohl(if_sa->sin_addr.s_addr) >> 24 == IN_LOOPBACKNET) + continue; + /* Send only once per interface, using master address */ + if (strcmp(ifb->ifa_name, lastifname) == 0) + continue; + debugp("Sending hello on %s\n", ifb->ifa_name); + if (setsockopt(hello_socket, IPPROTO_IP, IP_MULTICAST_IF, + &if_sa->sin_addr, sizeof(struct in_addr)) == -1) { + warnp("setsockopt failed: %s\n", strerror(errno)); + continue; + } + trtlv->address.s_addr = if_sa->sin_addr.s_addr; + + strlcpy(lastifname, ifb->ifa_name, sizeof(lastifname)); + + /* Send to the wire */ + sb = sendto(hello_socket, v, HELLO_MSG_SIZE, + 0, (struct sockaddr *) & sadest, sizeof(sadest)); + if (sb < (int)HELLO_MSG_SIZE) + fatalp("send: %s", strerror(errno)); + else + debugp("Send %d bytes (PDU: %d, Hello TLV: %d, CH: %d)\n", + sb, (int) (sizeof(struct ldp_pdu) - PDU_VER_LENGTH), + (int) (TLV_TYPE_LENGTH + MSGID_SIZE), + (int) (sizeof(struct common_hello_tlv))); + + } + freeifaddrs(ifa); + free(v); +} + +int +get_message_id(void) +{ + current_msg_id++; + return current_msg_id; +} + +static int +get_local_addr(struct sockaddr_dl *sdl, struct in_addr *sin) +{ + struct ifaddrs *ifa, *ifb; + struct sockaddr_in *sinet; + + if (sdl == NULL) + return -1; + + if (getifaddrs(&ifa) == -1) + return -1; + for (ifb = ifa; ifb; ifb = ifb->ifa_next) + if (ifb->ifa_addr->sa_family == AF_INET) { + if (if_nametoindex(ifb->ifa_name) != sdl->sdl_index) + continue; + sinet = (struct sockaddr_in*) ifb->ifa_addr; + sin->s_addr = sinet->sin_addr.s_addr; + freeifaddrs(ifa); + return 0; + } + freeifaddrs(ifa); + return -1; +} + +/* Receive PDUs on Multicast UDP socket */ +void +recv_pdu(int sock) +{ + struct ldp_pdu rpdu; + int c, i; + struct msghdr msg; + struct iovec iov[1]; + unsigned char recvspace[MAX_PDU_SIZE]; + struct hello_tlv *t; + struct sockaddr_in fromsa; + struct sockaddr_dl *sdl = NULL; + struct in_addr my_ldp_addr, local_addr; + struct cmsghdr *cmptr; + union { + struct cmsghdr cm; + char control[1024]; + } control_un; + + debugp("Entering RECV_PDU\n"); + + memset(&msg, 0, sizeof(msg)); + msg.msg_control = control_un.control; + msg.msg_controllen = sizeof(control_un.control); + msg.msg_flags = 0; + msg.msg_name = &fromsa; + msg.msg_namelen = sizeof(fromsa); + iov[0].iov_base = recvspace; + iov[0].iov_len = sizeof(recvspace); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + c = recvmsg(sock, &msg, MSG_WAITALL); + debugp("Incoming PDU size: %d\n", c); + + debugp("PDU from: %s\n", inet_ntoa(fromsa.sin_addr)); + + /* Check to see if this is larger than MIN_PDU_SIZE */ + if (c < MIN_PDU_SIZE) + return; + + /* Read the PDU */ + i = get_pdu(recvspace, &rpdu); + + /* We currently understand Version 1 */ + if (rpdu.version != LDP_VERSION) { + fatalp("recv_pdu: Version mismatch\n"); + return; + } + + /* Maybe it's our hello */ + inet_aton(LDP_ID, &my_ldp_addr); + if (rpdu.ldp_id.s_addr == my_ldp_addr.s_addr) { + fatalp("Received our PDU..\n"); /* it should be not looped */ + return; + } + + if (msg.msg_controllen < (socklen_t)sizeof(struct cmsghdr) || + (msg.msg_flags & MSG_CTRUNC)) + local_addr.s_addr = my_ldp_addr.s_addr; + else { + for (cmptr = CMSG_FIRSTHDR(&msg); cmptr != NULL; + cmptr = CMSG_NXTHDR(&msg, cmptr)) + if (cmptr->cmsg_level == IPPROTO_IP && + cmptr->cmsg_type == IP_RECVIF) { + sdl = (struct sockaddr_dl *) CMSG_DATA(cmptr); + break; + } + if (get_local_addr(sdl, &local_addr) != 0) + local_addr.s_addr = my_ldp_addr.s_addr; + } + + + debugp("Read %d bytes from address %s Length: %.4d Version: %d\n", + c, inet_ntoa(rpdu.ldp_id), rpdu.length, rpdu.version); + + /* Fill the TLV messages */ + t = get_hello_tlv(recvspace + i, c - i); + run_ldp_hello(&rpdu, t, &fromsa.sin_addr, &local_addr, sock); +} + +void +send_hello_alarm(int unused) +{ + struct ldp_peer *p; + struct hello_info *hi; + time_t t = time(NULL); + int olderrno = errno; + + /* Send hellos */ + if (!(t % ldp_hello_time)) + send_hello(); + + /* Timeout -- */ + SLIST_FOREACH(p, &ldp_peer_head, peers) + p->timeout--; + + /* Check for timeout */ +check_peer: + SLIST_FOREACH(p, &ldp_peer_head, peers) + if (p->timeout < 1) + switch (p->state) { + case LDP_PEER_HOLDDOWN: + debugp("LDP holddown expired for peer %s\n", + inet_ntoa(p->ldp_id)); + ldp_peer_delete(p); + goto check_peer; + case LDP_PEER_ESTABLISHED: + case LDP_PEER_CONNECTED: + send_notification(p, 0, + NOTIF_KEEP_ALIVE_TIMER_EXPIRED); + warnp("Keepalive expired for %s\n", + inet_ntoa(p->ldp_id)); + ldp_peer_holddown(p); + break; + } /* switch */ + + /* send keepalives */ + if (!(t % LDP_KEEPALIVE_TIME)) { + SLIST_FOREACH(p, &ldp_peer_head, peers) + if (p->state == LDP_PEER_ESTABLISHED) { + debugp("Sending KeepAlive to %s\n", + inet_ntoa(p->ldp_id)); + keep_alive(p); + } + } + + /* Decrement hello info keepalives */ + SLIST_FOREACH(hi, &hello_info_head, infos) + hi->keepalive--; + + /* Check hello keepalives */ +check_hello: + SLIST_FOREACH(hi, &hello_info_head, infos) + if (hi->keepalive < 1) { + SLIST_REMOVE(&hello_info_head, hi, hello_info, infos); + goto check_hello; + } + + /* Set the alarm again and bail out */ + alarm(1); + errno = olderrno; +} + +void +bail_out(int x) +{ + ldp_peer_holddown_all(); + flush_mpls_routes(); + exit(0); +} + +/* + * The big poll that catches every single event + * on every socket. + */ +void +the_big_loop(void) +{ + int sock_error; + uint32_t i; + socklen_t sock_error_size = sizeof(int); + struct ldp_peer *p; + struct com_sock *cs; + struct pollfd pfd[MAX_POLL_FDS]; + + SLIST_INIT(&hello_info_head); + + signal(SIGALRM, send_hello_alarm); + signal(SIGPIPE, SIG_IGN); + signal(SIGTERM, bail_out); + send_hello_alarm(1); + + route_socket = socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC); + + if (bind_current_routes() != LDP_E_OK) + fatalp("Cannot get current routes\n"); + + for (;;) { + nfds_t pollsum = 4; + + pfd[0].fd = ls; + pfd[0].events = POLLRDNORM; + pfd[0].revents = 0; + + pfd[1].fd = route_socket; + pfd[1].events = POLLRDNORM; + pfd[1].revents = 0; + + pfd[2].fd = command_socket; + pfd[2].events = POLLRDNORM; + pfd[2].revents = 0; + + /* Hello socket */ + pfd[3].fd = hello_socket; + pfd[3].events = POLLIN; + pfd[3].revents = 0; + + /* Command sockets */ + for (i=0; i < MAX_COMMAND_SOCKETS; i++) + if (csockets[i].socket != -1) { + pfd[pollsum].fd = csockets[i].socket; + pfd[pollsum].events = POLLIN; + pfd[pollsum].revents = 0; + pollsum++; + } + + /* LDP Peer sockets */ + SLIST_FOREACH(p, &ldp_peer_head, peers) { + if (p->socket < 1) + continue; + switch (p->state) { + case LDP_PEER_CONNECTED: + case LDP_PEER_ESTABLISHED: + pfd[pollsum].fd = p->socket; + pfd[pollsum].events = POLLRDNORM; + pfd[pollsum].revents = 0; + pollsum++; + break; + case LDP_PEER_CONNECTING: + pfd[pollsum].fd = p->socket; + pfd[pollsum].events = POLLWRNORM; + pfd[pollsum].revents = 0; + pollsum++; + break; + } + } + + if (pollsum >= MAX_POLL_FDS) { + fatalp("Too many sockets. Increase MAX_POLL_FDS\n"); + return; + } + if (poll(pfd, pollsum, INFTIM) < 0) { + if (errno != EINTR) + fatalp("poll: %s", strerror(errno)); + continue; + } + + for (i = 0; i < pollsum; i++) { + if ((pfd[i].revents & POLLRDNORM) || + (pfd[i].revents & POLLIN)) { + if(pfd[i].fd == ls) { + new_peer_connection(); + } else if (pfd[i].fd == route_socket) { + struct rt_msg xbuf; + int l, to_read; + do { + l = recv(route_socket, &xbuf, + sizeof(struct rt_msg), MSG_PEEK); + } while ((l == -1) && (errno == EINTR)); + + if (l == -1) + break; + + to_read = l; + l = 0; + do { + l += recv(route_socket, &xbuf, + to_read - l, MSG_WAITALL); + } while (l != to_read); + + check_route(&xbuf, to_read); + + } else if (pfd[i].fd == hello_socket) { + /* Receiving hello socket */ + recv_pdu(pfd[i].fd); + } else if (pfd[i].fd == command_socket) { + command_accept(command_socket); + } else if ((cs = is_command_socket(pfd[i].fd)) + != NULL) { + command_dispatch(cs); + } else { + /* ldp peer socket */ + p = get_ldp_peer_by_socket(pfd[i].fd); + if (p) + recv_session_pdu(p); + } + } else if(pfd[i].revents & POLLWRNORM) { + p = get_ldp_peer_by_socket(pfd[i].fd); + if (!p) + continue; + if ((getsockopt(pfd[i].fd, SOL_SOCKET, SO_ERROR, + &sock_error, &sock_error_size) != 0) || + (sock_error)) { + ldp_peer_holddown(p); + } else { + p->state = LDP_PEER_CONNECTED; + send_initialize(p); + } + } + } + + for (int ri = 0; ri < replay_index; ri++) { + debugp("Replaying: PID %d, SEQ %d\n", + replay_rt[ri].m_rtm.rtm_pid, + replay_rt[ri].m_rtm.rtm_seq); + check_route(&replay_rt[ri], sizeof(struct rt_msg)); + } + replay_index = 0; + } /* for (;;) */ +} + +void +new_peer_connection() +{ + struct sockaddr_in sa, sin_me; + int s; + + s = accept(ls, (struct sockaddr *) & sa, + & (socklen_t) { sizeof(struct sockaddr_in) } ); + if (s < 0) { + fatalp("accept: %s", strerror(errno)); + return; + } + + if (get_ldp_peer(&sa.sin_addr) != NULL) { + close(s); + return; + } + + warnp("Accepted a connection from %s\n", inet_ntoa(sa.sin_addr)); + + if (getsockname(s, (struct sockaddr *)&sin_me, + & (socklen_t) { sizeof(struct sockaddr_in) } )) { + fatalp("new_peer_connection(): cannot getsockname\n"); + close(s); + return; + } + + if (ntohl(sa.sin_addr.s_addr) < ntohl(sin_me.sin_addr.s_addr)) { + fatalp("Peer %s: connect from lower ID\n", + inet_ntoa(sa.sin_addr)); + close(s); + return; + } + /* XXX: sa.sin_addr ain't peer LDP ID ... */ + ldp_peer_new(&sa.sin_addr, &sa.sin_addr, NULL, LDP_HOLDTIME, s); + +} + +void +send_initialize(struct ldp_peer * p) +{ + struct init_tlv ti; + + ti.type = htons(LDP_INITIALIZE); + ti.length = htons(sizeof(struct init_tlv) - TLV_TYPE_LENGTH); + ti.messageid = htonl(get_message_id()); + ti.cs_type = htons(TLV_COMMON_SESSION); + ti.cs_len = htons(CS_LEN); + ti.cs_version = htons(LDP_VERSION); + ti.cs_keepalive = htons(2 * LDP_KEEPALIVE_TIME); + ti.cs_adpvlim = 0; + ti.cs_maxpdulen = htons(MAX_PDU_SIZE); + ti.cs_peeraddress.s_addr = p->ldp_id.s_addr; + ti.cs_peeraddrspace = 0; + + send_tlv(p, (struct tlv *) (void *) &ti); +} + +void +keep_alive(struct ldp_peer * p) +{ + struct ka_tlv kt; + + kt.type = htons(LDP_KEEPALIVE); + kt.length = htons(sizeof(kt.messageid)); + kt.messageid = htonl(get_message_id()); + + send_tlv(p, (struct tlv *) (void *) &kt); + +} + +void +recv_session_pdu(struct ldp_peer * p) +{ + struct ldp_pdu *rpdu; + struct address_tlv *atlv; + struct al_tlv *altlv; + struct init_tlv *itlv; + struct label_map_tlv *lmtlv; + struct fec_tlv *fectlv; + struct label_tlv *__packed labeltlv; + struct notification_tlv *nottlv; + struct hello_info *hi; + + int c; + int32_t wo = 0; + struct tlv *ttmp; + unsigned char recvspace[MAX_PDU_SIZE]; + + memset(recvspace, 0, MAX_PDU_SIZE); + + c = recv(p->socket, (void *) recvspace, MAX_PDU_SIZE, MSG_PEEK); + + debugp("Ready to read %d bytes\n", c); + + if (c < 1) { /* Session closed */ + warnp("Error in connection with %s\n", inet_ntoa(p->ldp_id)); + ldp_peer_holddown(p); + return; + } + if (c > MAX_PDU_SIZE) { + debugp("Incoming PDU size exceeds MAX_PDU_SIZE !\n"); + return; + } + if (c < MIN_PDU_SIZE) { + debugp("PDU too small received from peer %s\n", inet_ntoa(p->ldp_id)); + return; + } + rpdu = (struct ldp_pdu *) recvspace; + /* XXX: buggy messages may crash the whole thing */ + c = recv(p->socket, (void *) recvspace, + ntohs(rpdu->length) + PDU_VER_LENGTH, MSG_WAITALL); + rpdu = (struct ldp_pdu *) recvspace; + + /* Check if it's somehow OK... */ + if (check_recv_pdu(p, rpdu, c) != 0) + return; + + debugp("Read %d bytes, PDU size: %d bytes\n", c, ntohs(rpdu->length)); + wo = sizeof(struct ldp_pdu); + + while (wo + TLV_TYPE_LENGTH < (uint)c) { + + ttmp = (struct tlv *) (&recvspace[wo]); + + if ((ntohs(ttmp->type) != LDP_KEEPALIVE) && + (ntohs(ttmp->type) != LDP_LABEL_MAPPING)) { + debugp("Got Type: 0x%.4X (Length: %d) from %s\n", + ntohs(ttmp->type), ntohs(ttmp->length), + inet_ntoa(p->ldp_id)); + } else + debugp("Got Type: 0x%.4X (Length: %d) from %s\n", + ntohs(ttmp->type), ntohs(ttmp->length), + inet_ntoa(p->ldp_id)); + + /* Should we get the message ? */ + if (p->state != LDP_PEER_ESTABLISHED && + ntohs(ttmp->type) != LDP_INITIALIZE && + ntohs(ttmp->type) != LDP_KEEPALIVE) + break; + /* The big switch */ + switch (ntohs(ttmp->type)) { + case LDP_INITIALIZE: + itlv = (struct init_tlv *)ttmp; + /* Check size */ + if (ntohs(itlv->length) < + sizeof(struct init_tlv) - TLV_TYPE_LENGTH) { + send_notification(p, 0, + NOTIF_BAD_PDU_LEN | NOTIF_FATAL); + ldp_peer_holddown(p); + break; + } + /* Check version */ + if (ntohs(itlv->cs_version) != LDP_VERSION) { + send_notification(p, ntohl(itlv->messageid), + NOTIF_BAD_LDP_VER | NOTIF_FATAL); + ldp_peer_holddown(p); + break; + } + /* Check if we got any hello from this one */ + SLIST_FOREACH(hi, &hello_info_head, infos) + if (hi->ldp_id.s_addr == rpdu->ldp_id.s_addr) + break; + if (hi == NULL) { + send_notification(p, ntohl(itlv->messageid), + NOTIF_SESSION_REJECTED_NO_HELLO | NOTIF_FATAL); + ldp_peer_holddown(p); + break; + } + + if (!p->master) { + keep_alive(p); + send_initialize(p); + } else { + p->state = LDP_PEER_ESTABLISHED; + p->established_t = time(NULL); + keep_alive(p); + + /* + * Recheck here ldp id because we accepted + * connection without knowing who is it for sure + */ + p->ldp_id.s_addr = rpdu->ldp_id.s_addr; + + fatalp("LDP neighbour %s is UP\n", + inet_ntoa(p->ldp_id)); + mpls_add_ldp_peer(p); + send_addresses(p); + send_all_bindings(p); + } + break; + case LDP_KEEPALIVE: + if ((p->state == LDP_PEER_CONNECTED) && (!p->master)) { + p->state = LDP_PEER_ESTABLISHED; + p->established_t = time(NULL); + fatalp("LDP neighbour %s is UP\n", + inet_ntoa(p->ldp_id)); + mpls_add_ldp_peer(p); + send_addresses(p); + send_all_bindings(p); + } + p->timeout = p->holdtime; + break; + case LDP_ADDRESS: + /* Add peer addresses */ + atlv = (struct address_tlv *) ttmp; + altlv = (struct al_tlv *) (&atlv[1]); + add_ifaddresses(p, altlv); + print_bounded_addresses(p); + break; + case LDP_ADDRESS_WITHDRAW: + atlv = (struct address_tlv *) ttmp; + altlv = (struct al_tlv *) (&atlv[1]); + del_ifaddresses(p, altlv); + break; + case LDP_LABEL_MAPPING: + lmtlv = (struct label_map_tlv *) ttmp; + fectlv = (struct fec_tlv *) (&lmtlv[1]); + labeltlv = (struct label_tlv *)((unsigned char *)fectlv + + ntohs(fectlv->length) + TLV_TYPE_LENGTH); + map_label(p, fectlv, labeltlv); + break; + case LDP_LABEL_REQUEST: + lmtlv = (struct label_map_tlv *) ttmp; + fectlv = (struct fec_tlv *) (&lmtlv[1]); + switch (request_respond(p, lmtlv, fectlv)) { + case LDP_E_BAD_FEC: + send_notification(p, ntohl(lmtlv->messageid), + NOTIF_UNKNOWN_TLV); + break; + case LDP_E_BAD_AF: + send_notification(p, ntohl(lmtlv->messageid), + NOTIF_UNSUPPORTED_AF); + break; + case LDP_E_NO_SUCH_ROUTE: + send_notification(p, ntohl(lmtlv->messageid), + NOTIF_NO_ROUTE); + break; + } + break; + case LDP_LABEL_WITHDRAW: + lmtlv = (struct label_map_tlv *) ttmp; + fectlv = (struct fec_tlv *) (&lmtlv[1]); + if (withdraw_label(p, fectlv) == LDP_E_OK) { + /* Send RELEASE */ + prepare_release(ttmp); + send_tlv(p, ttmp); + } + break; + case LDP_LABEL_RELEASE: + /* + * XXX: we need to make a timed queue... + * For now I just assume peers are processing messages + * correctly so I just ignore confirmations + */ + wo = -1; /* Ignore rest of message */ + break; + case LDP_LABEL_ABORT: + /* XXX: For now I pretend I can process everything + * RFC 3036, Section 3.5.9.1 + * If an LSR receives a Label Abort Request Message after it + * has responded to the Label Request in question with a Label + * Mapping message or a Notification message, it ignores the + * abort request. + */ + wo = -1; + break; + case LDP_NOTIFICATION: + nottlv = (struct notification_tlv *) ttmp; + nottlv->st_code = ntohl(nottlv->st_code); + fatalp("Got notification 0x%X from peer %s\n", + nottlv->st_code, inet_ntoa(p->ldp_id)); + if (nottlv->st_code >> 31) { + fatalp("LDP peer %s signalized %s\n", + inet_ntoa(p->ldp_id), + NOTIF_STR[(nottlv->st_code << 1) >> 1]); + ldp_peer_holddown(p); + wo = -1; + } + break; + case LDP_HELLO: + /* No hellos should came on tcp session */ + wo = -1; + break; + default: + warnp("Unknown TLV received from %s\n", + inet_ntoa(p->ldp_id)); + debug_tlv(ttmp); + wo = -1;/* discard the rest of the message */ + break; + } + if (wo < 0) { + debugp("Discarding the rest of the message\n"); + break; + } else { + wo += ntohs(ttmp->length) + TLV_TYPE_LENGTH; + debugp("WORKED ON %u bytes (Left %d)\n", wo, c - wo); + } + } /* while */ + +} + +/* Sends a pdu, tlv pair to a connected peer */ +int +send_message(struct ldp_peer * p, struct ldp_pdu * pdu, struct tlv * t) +{ + unsigned char sendspace[MAX_PDU_SIZE]; + + /* Check if peer is connected */ + switch (p->state) { + case LDP_PEER_CONNECTED: + case LDP_PEER_ESTABLISHED: + break; + default: + return -1; + } + + /* Check length validity first */ + if (ntohs(pdu->length) != + ntohs(t->length) + TLV_TYPE_LENGTH + PDU_PAYLOAD_LENGTH) { + fatalp("LDP: TLV - PDU incompability. Message discarded\n"); + fatalp("LDP: TLV len %d - PDU len %d\n", ntohs(t->length), + ntohs(pdu->length)); + return -1; + } + if (ntohs(t->length) + PDU_VER_LENGTH > MAX_PDU_SIZE) { + fatalp("Message to large discarded\n"); + return -1; + } + /* Arrange them in a buffer and send */ + memcpy(sendspace, pdu, sizeof(struct ldp_pdu)); + memcpy(sendspace + sizeof(struct ldp_pdu), t, + ntohs(t->length) + TLV_TYPE_LENGTH); + + /* Report keepalives only for DEBUG */ + if ((ntohs(t->type) != 0x201) && (ntohs(t->type) != 0x400)) { + debugp("Sending message type 0x%.4X to %s (size: %d)\n", + ntohs(t->type), inet_ntoa(p->ldp_id), ntohs(t->length)); + } else + /* downgraded from warnp to debugp for now */ + debugp("Sending message type 0x%.4X to %s (size: %d)\n", + ntohs(t->type), inet_ntoa(p->ldp_id), ntohs(t->length)); + + /* Send it finally */ + return send(p->socket, sendspace, + ntohs(pdu->length) + PDU_VER_LENGTH, 0); +} + +/* + * Encapsulates TLV into a PDU and sends it to a peer + */ +int +send_tlv(struct ldp_peer * p, struct tlv * t) +{ + struct ldp_pdu pdu; + + pdu.version = htons(LDP_VERSION); + inet_aton(LDP_ID, &pdu.ldp_id); + pdu.label_space = 0; + pdu.length = htons(ntohs(t->length) + TLV_TYPE_LENGTH + + PDU_PAYLOAD_LENGTH); + + return send_message(p, &pdu, t); +} + + +int +send_addresses(struct ldp_peer * p) +{ + struct address_list_tlv *t; + int ret; + + t = build_address_list_tlv(); + + ret = send_tlv(p, (struct tlv *) t); + free(t); + return ret; + +} diff --git a/usr.sbin/ldpd/socketops.h b/usr.sbin/ldpd/socketops.h new file mode 100644 index 000000000000..3b1b198a6756 --- /dev/null +++ b/usr.sbin/ldpd/socketops.h @@ -0,0 +1,69 @@ +/* $NetBSD: socketops.h,v 1.1 2010/12/08 07:20:15 kefren Exp $ */ + +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Mihai Chelaru + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SOCKETOPS_H_ +#define _SOCKETOPS_H_ + +#include "mpls_routes.h" +#include "ldp_peer.h" +#include "pdu.h" +#include "tlv.h" + +/* Address families from RFC1700 */ +#define LDP_AF_INET 1 +#define LDP_AF_INET6 2 + +int set_ttl(int); +int set_mcast_ttl(int); +int set_tos(int); +int socket_reuse_port(int); +int bind_socket(int, uint32_t); +int create_hello_socket(void); +int create_listening_socket(void); +void send_hello(void); +int get_message_id(void); +void the_big_loop(void); +void new_peer_connection(void); +void send_initialize(struct ldp_peer *); +void keep_alive(struct ldp_peer *); +void recv_session_pdu(struct ldp_peer *); +int send_message(struct ldp_peer *, struct ldp_pdu *, struct tlv *); +int send_tlv(struct ldp_peer *, struct tlv *); +int send_addresses(struct ldp_peer *); + +struct hello_info { + struct in_addr address, transport_address, ldp_id; + int keepalive; + SLIST_ENTRY(hello_info) infos; +}; +SLIST_HEAD(,hello_info) hello_info_head; + +#endif /* !_SOCKETOPS_H_ */ diff --git a/usr.sbin/ldpd/tlv.c b/usr.sbin/ldpd/tlv.c new file mode 100644 index 000000000000..36654f013c60 --- /dev/null +++ b/usr.sbin/ldpd/tlv.c @@ -0,0 +1,75 @@ +/* $NetBSD: tlv.c,v 1.1 2010/12/08 07:20:15 kefren Exp $ */ + +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Mihai Chelaru + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include + +#include "ldp.h" +#include "fsm.h" +#include "ldp_errors.h" +#include "tlv.h" + +/* Reads and checks a tlv struct from a buffer */ +struct hello_tlv * +get_hello_tlv(unsigned char *s, uint max) +{ + struct hello_tlv *t; + + /* Do we have at least Type + Length + MSG_ID ? */ + if (max <= TLV_TYPE_LENGTH + MSGID_SIZE) + return NULL; + + t = (struct hello_tlv *) s; + + if (ntohs(t->type) != LDP_HELLO) + return NULL; + + /* Does its size fit into max ? */ + if (ntohs(t->length) + TLV_TYPE_LENGTH > max) + return NULL; + + t->type = ntohs(t->type); + t->length = ntohs(t->length); + t->messageid = ntohl(t->messageid); + return t; + /* We don't check for Common Hello Params here */ +} + +/* Prints out some information about TLV */ +void +debug_tlv(struct tlv * t) +{ + warnp("TLV type %.4X, Length %d, Message ID %.8X\n", ntohs(t->type), + ntohs(t->length), ntohs(t->messageid)); +} diff --git a/usr.sbin/ldpd/tlv.h b/usr.sbin/ldpd/tlv.h new file mode 100644 index 000000000000..36418437c3f0 --- /dev/null +++ b/usr.sbin/ldpd/tlv.h @@ -0,0 +1,206 @@ +/* $NetBSD: tlv.h,v 1.1 2010/12/08 07:20:15 kefren Exp $ */ + +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Mihai Chelaru + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _TLV_H_ +#define _TLV_H_ + +/* TLV messages */ +#define TLV_FEC 0x0100 +#define TLV_ADDRESS_LIST 0x0101 +#define TLV_HOP_COUNT 0x0103 +#define TLV_PATH_VECTOR 0x0104 +#define TLV_GENERIC_LABEL 0x0200 +#define TLV_ATM_LABEL 0x0201 +#define TLV_FR_LABEL 0x0202 +#define TLV_STATUS 0x0300 +#define TLV_EXTENDED_STATUS 0x0301 +#define TLV_RETURNED_PDU 0x0302 +#define TLV_RETURNED_MESSAGE 0x0303 +#define TLV_COMMON_HELLO 0x0400 +#define TLV_IPV4_TRANSPORT 0x0401 +#define TLV_CONFIGURATION_SEQ 0x0402 +#define TLV_IPV6_TRANSPORT 0x0403 +#define TLV_COMMON_SESSION 0x0500 +#define TLV_ATM_SESSION 0x0501 +#define TLV_FR_SESSION 0x0502 +#define TLV_LABEL_REQUEST 0x0600 + +/* Some common lengths in order to avoid writing them every time */ +#define TLV_TYPE_LENGTH (sizeof(uint16_t) + sizeof(uint16_t)) +#define MSGID_SIZE (sizeof(uint32_t)) + +/* General TLV structure */ +struct tlv { + uint16_t type; + uint16_t length; + uint32_t messageid; + void *value; + struct ldp_pdu *pdu; +} __packed; + +/* Common Hello TLV structure */ +struct common_hello_tlv { + uint16_t type; + uint16_t length; + uint16_t holdtime; + union { + /* XXX: Endianness ?! */ + uint8_t tr:2; + uint16_t res; + }; +} __packed; + +/* Hello TLV structure */ +struct hello_tlv { + uint16_t type; + uint16_t length; + uint32_t messageid; + struct common_hello_tlv ch; + /* XXX: optional parameters */ +} __packed; + +/* IPv4 Transport address TLV */ +struct transport_address_tlv { + uint16_t type; + uint16_t length; + struct in_addr address; +} __packed; + + +#define CS_LEN (sizeof(struct init_tlv) - TLV_TYPE_LENGTH - MSGID_SIZE - \ + sizeof(uint32_t)) + +/* Initialization TLV structure */ +struct init_tlv { + uint16_t type; + uint16_t length; + uint32_t messageid; + /* + * Common Session Parameters + */ + uint16_t cs_type; + uint16_t cs_len; + uint16_t cs_version; + uint16_t cs_keepalive; + uint16_t cs_adpvlim; /* XXX */ + uint16_t cs_maxpdulen; + struct in_addr cs_peeraddress; + uint16_t cs_peeraddrspace; +} __packed; + +/* Keepalive TLV */ +struct ka_tlv { /* Keepalive message */ + uint16_t type; + uint16_t length; + uint32_t messageid; +} __packed; + +/* Notification TLV */ +struct notification_tlv { + uint16_t type; + uint16_t length; + uint32_t messageid; + uint16_t status; + uint16_t st_len; + uint32_t st_code; + uint32_t msg_id; + uint32_t msg_type; +} __packed; + +/* Address LIST TLV for SEND */ +struct address_list_tlv { + uint16_t type; + uint16_t length; + uint32_t messageid; + uint16_t a_type; + uint16_t a_length; + uint16_t a_af; + struct in_addr a_address; +} __packed; + +/* Real AL TLV used for RCV for now */ +struct al_tlv { + uint16_t type; + uint16_t length; + uint16_t af; + struct in_addr address; +} __packed; + +struct address_tlv { + uint16_t type; + uint16_t length; + uint32_t messageid; +} __packed; + +/* Label map TLV */ +struct label_map_tlv { + uint16_t type; + uint16_t length; + uint32_t messageid; +} __packed; + +/* FEC TLV */ +struct fec_tlv { + uint16_t type; + uint16_t length; +} __packed; + +struct prefix_tlv { + uint8_t type; + uint16_t af; + uint8_t prelen; + struct in_addr prefix; +} __packed; + +struct host_tlv { + uint8_t type; + uint16_t af; + uint8_t length; + struct in_addr address; +} __packed; + +struct label_tlv { + uint16_t type; + uint16_t length; + uint32_t label; +} __packed; + +/* Label Request Message ID TLV */ +struct label_request_tlv { + uint16_t type; + uint16_t length; /* 4 */ + uint32_t messageid; +} __packed; + +struct hello_tlv * get_hello_tlv(unsigned char *, uint); +void debug_tlv(struct tlv *); + +#endif /* !_TLV_H_ */ diff --git a/usr.sbin/ldpd/tlv_stack.c b/usr.sbin/ldpd/tlv_stack.c new file mode 100644 index 000000000000..f2483e974d1d --- /dev/null +++ b/usr.sbin/ldpd/tlv_stack.c @@ -0,0 +1,415 @@ +/* $NetBSD: tlv_stack.c,v 1.1 2010/12/08 07:20:15 kefren Exp $ */ + +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Mihai Chelaru + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include +#include +#include + +#include "ldp.h" +#include "ldp_errors.h" +#include "ldp_peer.h" +#include "tlv.h" +#include "socketops.h" +#include "pdu.h" +#include "label.h" +#include "mpls_interface.h" +#include "tlv_stack.h" + +uint8_t ldp_ceil8(int); + +uint8_t +ldp_ceil8(int x) +{ + if (x % 8 == 0) + return x / 8; + return x / 8 + 1; +} + +int +map_label(struct ldp_peer * p, struct fec_tlv * f, struct label_tlv * l) +{ + int n; + struct prefix_tlv *pref; + struct in_addr inatmp; + + if (ntohs(f->type) != TLV_FEC) { + debugp("Invalid FEC TLV !\n"); + return LDP_E_BAD_FEC; + } + if (ntohs(l->type) != TLV_GENERIC_LABEL) { + debugp("Invalid LABEL TLV! (0x%.4X)\n", ntohs(l->type)); + return LDP_E_BAD_LABEL; + } + /* + * Actually address field length is given only in length field in + * bits ! + */ + + n = ntohs(f->length); + if (!n) + return LDP_E_BAD_FEC; + + debugp("Label %u for:\n", ntohl(l->label)); + + pref = (struct prefix_tlv *) & f[1]; + + /* + * Section 3.4.1 + * Note that this version of LDP supports the use of multiple FEC + * Elements per FEC for the Label Mapping message only. The use of + * multiple FEC Elements in other messages is not permitted in this + * version, and is a subject for future study. + */ + + for (; n > 0; pref = (struct prefix_tlv *) ((unsigned char *) pref + + ldp_ceil8(pref->prelen) + TLV_TYPE_LENGTH)) { + n -= ldp_ceil8(pref->prelen) + TLV_TYPE_LENGTH; + if (ntohs(pref->af) != LDP_AF_INET) { + debugp("BAD ADDRESS FAMILY (%d) ! (prefix type %d, " + "length %d)\n", ntohs(pref->af), pref->type, + pref->prelen); + return LDP_E_BAD_AF; + } + switch(pref->type) { + case FEC_PREFIX: + case FEC_HOST: + memset(&inatmp, 0, sizeof(struct in_addr)); + memcpy(&inatmp, &pref->prefix, ldp_ceil8(pref->prelen)); + debugp("Prefix/Host add: %s/%d\n", inet_ntoa(inatmp), + pref->prelen); + ldp_peer_add_mapping(p, &inatmp, pref->prelen, + ntohl(l->label)); + mpls_add_label(p, NULL, &inatmp, pref->prelen, + ntohl(l->label), 1); + break; + case FEC_WILDCARD: + fatalp("LDP: Wildcard add from peer %s\n", + inet_ntoa(p->address)); + return LDP_E_BAD_FEC; + default: + fatalp("Unknown FEC type %d\n", pref->type); + return LDP_E_BAD_FEC; + } + } + + return LDP_E_OK; +} + +int +withdraw_label(struct ldp_peer * p, struct fec_tlv * f) +{ + int n; + struct prefix_tlv *pref; + struct in_addr inatmp; + struct label *lab; + + if (ntohs(f->type) != TLV_FEC) { + debugp("Invalid FEC TLV !\n"); + return LDP_E_BAD_FEC; + } + n = ntohs(f->length); + if (!n) + return LDP_E_BAD_FEC; + + pref = (struct prefix_tlv *) & f[1]; + if (ntohs(pref->af) != LDP_AF_INET) { + debugp("BAD ADDRESS FAMILY (%d)! (prefix type %d, length %d)\n", + ntohs(pref->af), pref->type, pref->prelen); + return LDP_E_BAD_AF; + } + switch(pref->type) { + case FEC_PREFIX: + case FEC_HOST: + memset(&inatmp, 0, sizeof(struct in_addr)); + memcpy(&inatmp, &pref->prefix, ldp_ceil8(pref->prelen)); + debugp("Prefix/Host withdraw: %s/%d\n", inet_ntoa(inatmp), + pref->prelen); + + /* Delete mapping */ + ldp_peer_delete_mapping(p, &inatmp, pref->prelen); + + /* Get label, see if we're pointing to this peer + * if so, send withdraw, reattach IP route and announce + * POP Label + */ + lab = label_get_by_prefix(&inatmp, pref->prelen); + if ((lab) && (lab->p == p)) { + change_local_label(lab, MPLS_LABEL_IMPLNULL); + label_reattach_route(lab, LDP_READD_CHANGE); + } + break; + case FEC_WILDCARD: + fatalp("LDP neighbour %s: Wildcard withdraw !!!\n", + inet_ntoa(p->address)); + ldp_peer_delete_mapping(p, NULL, 0); + label_reattach_all_peer_labels(p, LDP_READD_CHANGE); + break; + default: + fatalp("Unknown FEC type %d\n", pref->type); + return LDP_E_BAD_FEC; + } + + return LDP_E_OK; +} + + +/* + * In case of label redraw, reuse the same buffer to send label release + * Simply replace type and message id + */ +void +prepare_release(struct tlv * v) +{ + struct label_map_tlv *t; + + t = (struct label_map_tlv *) v; + + t->type = htons(LDP_LABEL_RELEASE); + t->messageid = htonl(get_message_id()); +} + +/* Sends a label mapping */ +void +send_label_tlv(struct ldp_peer * peer, struct in_addr * addr, + uint8_t prefixlen, uint32_t label, struct label_request_tlv *lrt) +{ + struct label_map_tlv *lmt; + struct fec_tlv *fec; + struct prefix_tlv *p; + struct label_tlv *l; + + /* + * Ok, so we have label map tlv that contains fec tlvs and label tlv + * but fec tlv contains prefix or host tlvs and prefix or host tlvs + * contains the network. After that we may have optional parameters + * Got it ? + */ + + lmt = (struct label_map_tlv *) malloc( + sizeof(struct label_map_tlv) + + sizeof(struct fec_tlv) + + sizeof(struct prefix_tlv) - sizeof(struct in_addr) + + ldp_ceil8(prefixlen) + + sizeof(struct label_tlv) + + /* Label request optional parameter */ + (lrt != NULL ? sizeof(struct label_request_tlv) : 0) ); + + if (!lmt) { + fatalp("send_label_tlv: malloc problem\n"); + return; + } + + lmt->type = htons(LDP_LABEL_MAPPING); + lmt->length = htons(sizeof(struct label_map_tlv) - TLV_TYPE_LENGTH + + sizeof(struct fec_tlv) + + sizeof(struct prefix_tlv) - sizeof(struct in_addr) + + ldp_ceil8(prefixlen) + + sizeof(struct label_tlv) + + + (lrt != NULL ? sizeof(struct label_request_tlv) : 0)); + lmt->messageid = htonl(get_message_id()); + + /* FEC TLV */ + fec = (struct fec_tlv *) & lmt[1]; + fec->type = htons(TLV_FEC); + fec->length = htons(sizeof(struct fec_tlv) - TLV_TYPE_LENGTH + + sizeof(struct prefix_tlv) - sizeof(struct in_addr) + + ldp_ceil8(prefixlen)); + + /* Now let's do the even a dirtier job: PREFIX TLV */ + p = (struct prefix_tlv *) & fec[1]; + /* Cisco and Juniper don't support FEC type HOST + * so everything is FEC_PREFIX.. + * + * if (prefixlen == 32) p->type = FEC_HOST; else + */ + p->type = FEC_PREFIX; + p->af = htons(LDP_AF_INET); + p->prelen = prefixlen; + memcpy(&p->prefix, addr, ldp_ceil8(prefixlen)); + + /* LABEL TLV */ + l = (struct label_tlv *) ((unsigned char *) p + + sizeof(struct prefix_tlv) - sizeof(struct in_addr) + + ldp_ceil8(prefixlen)); + l->type = htons(TLV_GENERIC_LABEL); + l->length = htons(sizeof(l->label)); + l->label = htonl(label); + + /* Label request optional parameter */ + if (lrt) + memcpy(((char*)l) + TLV_TYPE_LENGTH + ntohs(l->length), + lrt, htons(lrt->length) + TLV_TYPE_LENGTH); + + /* Wow, seems we're ready */ + send_tlv(peer, (struct tlv *) lmt); + free(lmt); +} + +void +send_label_tlv_to_all(struct in_addr * addr, uint8_t prefixlen, uint32_t label) +{ + struct ldp_peer *p; + SLIST_FOREACH(p, &ldp_peer_head, peers) + send_label_tlv(p, addr, prefixlen, label, NULL); +} + +/* + * Send all local labels to a peer + */ +void +send_all_bindings(struct ldp_peer * peer) +{ + struct label *l; + + SLIST_FOREACH(l, &label_head, labels) + send_label_tlv(peer, &((struct sockaddr_in*)(&l->so_dest))->sin_addr, + from_union_to_cidr(&l->so_pref), l->binding, NULL); + +} + +/* Sends a label WITHDRAW */ +void +send_withdraw_tlv(struct ldp_peer * peer, struct in_addr * addr, + uint8_t prefixlen) +{ + struct label_map_tlv *lmt; + struct fec_tlv *fec; + struct prefix_tlv *p; + + /* + * Ok, so we have label map tlv that contains fec tlvs but fec tlv + * contains prefix or host tlvs and prefix or host tlvs contains the + * network. Yes, we don't have to announce label here + */ + + lmt = (struct label_map_tlv *) malloc(sizeof(struct label_map_tlv) + + sizeof(struct fec_tlv) + + sizeof(struct prefix_tlv) - sizeof(struct in_addr) + + ldp_ceil8(prefixlen)); + + if (!lmt) { + fatalp("send_withdraw_tlv: malloc problem\n"); + return; + } + + lmt->type = htons(LDP_LABEL_WITHDRAW); + lmt->length = htons(sizeof(struct label_map_tlv) - TLV_TYPE_LENGTH + + sizeof(struct fec_tlv) + + sizeof(struct prefix_tlv) - sizeof(struct in_addr) + + ldp_ceil8(prefixlen)); + lmt->messageid = htonl(get_message_id()); + + /* FEC TLV */ + fec = (struct fec_tlv *) & lmt[1]; + fec->type = htons(TLV_FEC); + fec->length = htons(sizeof(struct fec_tlv) - TLV_TYPE_LENGTH + + sizeof(struct prefix_tlv) - sizeof(struct in_addr) + + ldp_ceil8(prefixlen)); + + /* Now the even dirtier job: PREFIX TLV */ + p = (struct prefix_tlv *) & fec[1]; + /* See above comment + * + * if (prefixlen == 32) p->type = FEC_HOST; else + */ + p->type = FEC_PREFIX; + p->af = htons(LDP_AF_INET); + p->prelen = prefixlen; + memcpy(&p->prefix, addr, ldp_ceil8(prefixlen)); + + /* Wow, seems we're ready */ + send_tlv(peer, (struct tlv *) lmt); + free(lmt); +} + +void +send_withdraw_tlv_to_all(struct in_addr * addr, uint8_t prefixlen) +{ + struct ldp_peer *p; + SLIST_FOREACH(p, &ldp_peer_head, peers) + send_withdraw_tlv(p, addr, prefixlen); +} + +int +request_respond(struct ldp_peer *p, struct label_map_tlv *lmt, + struct fec_tlv *fec) +{ + struct prefix_tlv *pref; + struct in_addr inatmp; + struct label *lab; + struct label_request_tlv lrm; + + if (ntohs(fec->type) != TLV_FEC || fec->length == 0) { + debugp("Invalid FEC TLV !\n"); + return LDP_E_BAD_FEC; + } + pref = (struct prefix_tlv *) (fec + 1); + + if (ntohs(pref->af) != LDP_AF_INET) { + debugp("request_respond: Bad address family\n"); + return LDP_E_BAD_AF; + } + + switch (pref->type) { + case FEC_PREFIX: + case FEC_HOST: + + memset(&inatmp, 0, sizeof(struct in_addr)); + memcpy(&inatmp, &pref->prefix, ldp_ceil8(pref->prelen)); + debugp("Prefix/Host request: %s/%d\n", inet_ntoa(inatmp), + pref->prelen); + + lab = label_get_by_prefix(&inatmp, pref->prelen); + if (!lab) + return LDP_E_NO_SUCH_ROUTE; + lrm.type = htons(TLV_LABEL_REQUEST); + lrm.length = htons(sizeof(uint32_t)); + lrm.messageid = lmt->messageid; + send_label_tlv(p, &inatmp, pref->prelen, lab->binding, &lrm); + break; + + case FEC_WILDCARD: + /* + * Section 3.4.1 + * To be used only in the Label Withdraw and Label Release + */ + /* Fallthrough */ + default: + + fatalp("Invalid FEC type\n"); + return LDP_E_BAD_FEC; + } + return LDP_E_OK; +} diff --git a/usr.sbin/ldpd/tlv_stack.h b/usr.sbin/ldpd/tlv_stack.h new file mode 100644 index 000000000000..324a9d3d0547 --- /dev/null +++ b/usr.sbin/ldpd/tlv_stack.h @@ -0,0 +1,54 @@ +/* $NetBSD: tlv_stack.h,v 1.1 2010/12/08 07:20:15 kefren Exp $ */ + +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Mihai Chelaru + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _TLV_STACK_H_ +#define _TLV_STACK_H_ + +#include "ldp_peer.h" +#include "tlv.h" + +#define FEC_WILDCARD 0x01 +#define FEC_PREFIX 0x02 +#define FEC_HOST 0x03 + +int map_label(struct ldp_peer *, struct fec_tlv *, struct label_tlv *); +int withdraw_label(struct ldp_peer *, struct fec_tlv *); +void prepare_release(struct tlv *); +void send_label_tlv(struct ldp_peer *, struct in_addr *, uint8_t, + uint32_t, struct label_request_tlv *); +void send_label_tlv_to_all(struct in_addr *, uint8_t, uint32_t); +void send_all_bindings(struct ldp_peer *); +void send_withdraw_tlv(struct ldp_peer *, struct in_addr *, uint8_t); +void send_withdraw_tlv_to_all(struct in_addr *, uint8_t); +int request_respond(struct ldp_peer *, struct label_map_tlv *, + struct fec_tlv *); + +#endif /* !_TLV_STACK_H_ */